@coreplane/switchboard 1.261.1 → 1.261.3
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 +44 -0
- 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/contract.ts +39 -0
- package/dist/assets/src/core/coordinator/driver.ts +10 -8
- package/dist/assets/src/core/runEvents.ts +15 -1
- package/dist/assets/src/core/runLedger/types.ts +4 -0
- package/dist/assets/src/core/ship/coordinator.ts +3 -0
- package/dist/assets/src/core/types.ts +6 -2
- package/dist/cli.js +761 -217
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4186,6 +4186,7 @@ var init_runEvents = __esm({
|
|
|
4186
4186
|
"ledger_untracked",
|
|
4187
4187
|
"rebind_refused",
|
|
4188
4188
|
"work_left_behind",
|
|
4189
|
+
"publication_blocked",
|
|
4189
4190
|
"workspace_torn_down",
|
|
4190
4191
|
"pr_not_opened",
|
|
4191
4192
|
"pr_head_unverified",
|
|
@@ -5792,6 +5793,7 @@ function isCoordinatorUnit(v) {
|
|
|
5792
5793
|
if (r.issue !== void 0 && !isFinite(r.issue)) return false;
|
|
5793
5794
|
if (r.pr !== void 0 && !isPr(r.pr)) return false;
|
|
5794
5795
|
if (r.resume !== void 0 && !isResume(r.resume)) return false;
|
|
5796
|
+
if (r.publication !== void 0 && !isPublication(r.publication)) return false;
|
|
5795
5797
|
if (r.record !== void 0 && (typeof r.record !== "string" || !/^\d{4}$/.test(r.record))) return false;
|
|
5796
5798
|
if (r.lastPush !== void 0 && !isText(r.lastPush)) return false;
|
|
5797
5799
|
if (r.segments !== void 0 && (!Array.isArray(r.segments) || !r.segments.every(isSegment))) return false;
|
|
@@ -5855,7 +5857,7 @@ async function sendChildSignal(workflow, signal2) {
|
|
|
5855
5857
|
return { kind: "failed", instance, type, reason: err2 instanceof Error ? err2.message : String(err2) };
|
|
5856
5858
|
}
|
|
5857
5859
|
}
|
|
5858
|
-
var COORDINATOR_IDENTITY, COORDINATOR_STEP_ACTION, PLAN_MERGE_ACTION, COORDINATOR_STEP_PATH_PREFIX, INSTANCE_ID_PATTERN, STEP_NAME_PATTERN, IDEMPOTENCY_KEY_PATTERN, UNIT_PATTERN, UNIT_KEY_PATTERN, CHILD_INTERRUPTED_EVENT_PREFIX, CHILD_RESUMED_EVENT_PREFIX, THREAD_EVENT_MAX_BYTES, serializedBytes, isThreadEventStagedFile, isThreadEventAttachment, isThreadEventMode, UNIT_NUDGE_EVENT_PREFIX, CHECKS_SETTLED_EVENT_PREFIX, PULL_MERGED_EVENT_PREFIX, IDLE_WHY_MAX, REPO_SLUG, MAX_TEXT, MAX_REPORT, MAX_ROUNDS, isText, isOptionalText, isFinite, isCount, isObject, isPr, isResume, isThread, isRoundGate, isHumanGatePending, isUnitIdle, isSegment, isUnitWakeAnswer;
|
|
5860
|
+
var COORDINATOR_IDENTITY, COORDINATOR_STEP_ACTION, PLAN_MERGE_ACTION, COORDINATOR_STEP_PATH_PREFIX, INSTANCE_ID_PATTERN, STEP_NAME_PATTERN, IDEMPOTENCY_KEY_PATTERN, UNIT_PATTERN, UNIT_KEY_PATTERN, CHILD_INTERRUPTED_EVENT_PREFIX, CHILD_RESUMED_EVENT_PREFIX, THREAD_EVENT_MAX_BYTES, serializedBytes, isThreadEventStagedFile, isThreadEventAttachment, isThreadEventMode, UNIT_NUDGE_EVENT_PREFIX, CHECKS_SETTLED_EVENT_PREFIX, PULL_MERGED_EVENT_PREFIX, IDLE_WHY_MAX, REPO_SLUG, MAX_TEXT, MAX_REPORT, MAX_ROUNDS, isText, isOptionalText, isFinite, isCount, isObject, isPr, isResume, isFullSha, isPublication, isThread, isRoundGate, isHumanGatePending, isUnitIdle, isSegment, isUnitWakeAnswer;
|
|
5859
5861
|
var init_contract2 = __esm({
|
|
5860
5862
|
"../../src/core/coordinator/contract.ts"() {
|
|
5861
5863
|
"use strict";
|
|
@@ -5895,6 +5897,8 @@ var init_contract2 = __esm({
|
|
|
5895
5897
|
isObject = (v) => typeof v === "object" && v !== null;
|
|
5896
5898
|
isPr = (v) => isObject(v) && isFinite(v.number) && isText(v.url, 2048);
|
|
5897
5899
|
isResume = (v) => isObject(v) && isFinite(v.pr) && (v.headSha === void 0 || isText(v.headSha)) && (v.url === void 0 || isText(v.url, 2048));
|
|
5900
|
+
isFullSha = (v) => typeof v === "string" && /^[0-9a-f]{40}$/i.test(v);
|
|
5901
|
+
isPublication = (v) => isObject(v) && typeof v.repo === "string" && REPO_SLUG.test(v.repo) && typeof v.pr === "number" && Number.isInteger(v.pr) && v.pr > 0 && isText(v.headRef) && isText(v.baseRef) && isFullSha(v.expectedHeadSha) && isText(v.publicationRef) && isObject(v.owner) && typeof v.owner.instanceId === "string" && INSTANCE_ID_PATTERN.test(v.owner.instanceId) && typeof v.owner.unit === "string" && UNIT_PATTERN.test(v.owner.unit);
|
|
5898
5902
|
isThread = (v) => isObject(v) && isText(v.threadKey) && isOptionalText(v.sourceUrl);
|
|
5899
5903
|
isRoundGate = (v) => isObject(v) && isAddressSeverity(v.level) && Array.isArray(v.findings) && v.findings.every((f) => typeof f === "string");
|
|
5900
5904
|
isHumanGatePending = (v) => isObject(v) && isObject(v.pr) && typeof v.pr.number === "number" && Number.isInteger(v.pr.number) && v.pr.number > 0 && isText(v.pr.url) && typeof v.round === "number" && Number.isInteger(v.round) && v.round >= 1 && Array.isArray(v.findings) && v.findings.every((finding) => isFindingShape(finding)) && (v.verdict === "approve" || v.verdict === "request_changes") && (v.reviewRunId === void 0 || isText(v.reviewRunId)) && (v.headSha === void 0 || isText(v.headSha)) && (v.askedAt === void 0 || isFinite(v.askedAt));
|
|
@@ -7128,9 +7132,9 @@ function makeSsrfLookup(resolve13) {
|
|
|
7128
7132
|
const family = (ip) => ip.includes(":") ? 6 : 4;
|
|
7129
7133
|
resolve13(hostname3).then(
|
|
7130
7134
|
(ips) => {
|
|
7131
|
-
const
|
|
7132
|
-
if (
|
|
7133
|
-
cb(new BlockedUrlError(`host ${hostname3} resolves to blocked address ${
|
|
7135
|
+
const blocked2 = ips.find((ip) => ipInBlockedRange(ip));
|
|
7136
|
+
if (blocked2) {
|
|
7137
|
+
cb(new BlockedUrlError(`host ${hostname3} resolves to blocked address ${blocked2}`));
|
|
7134
7138
|
return;
|
|
7135
7139
|
}
|
|
7136
7140
|
if (ips.length === 0) {
|
|
@@ -34683,6 +34687,9 @@ var init_instanceStore = __esm({
|
|
|
34683
34687
|
async putUnits(_units) {
|
|
34684
34688
|
return { ok: false, reason: "unavailable" };
|
|
34685
34689
|
}
|
|
34690
|
+
async claimLegacyContinuation(_expected, _recovered) {
|
|
34691
|
+
return { ok: false, reason: "unavailable" };
|
|
34692
|
+
}
|
|
34686
34693
|
async listUnits(_instanceId) {
|
|
34687
34694
|
return [];
|
|
34688
34695
|
}
|
|
@@ -34757,6 +34764,15 @@ var init_instanceStore = __esm({
|
|
|
34757
34764
|
if (d.ok === true) return { ok: true };
|
|
34758
34765
|
throw new Error(`coordinator store /runs/coordinator/units/put: unexpected answer (HTTP ${r.status})`);
|
|
34759
34766
|
}
|
|
34767
|
+
async claimLegacyContinuation(expected, recovered) {
|
|
34768
|
+
const r = await this.post("/runs/coordinator/units/claim-legacy-continuation", { expected, recovered });
|
|
34769
|
+
const d = r.data;
|
|
34770
|
+
if (r.status === 409 && d.reason === "stale") return { ok: false, reason: "stale" };
|
|
34771
|
+
if (d.ok === true) return { ok: true };
|
|
34772
|
+
throw new Error(
|
|
34773
|
+
`coordinator store /runs/coordinator/units/claim-legacy-continuation: unexpected answer (HTTP ${r.status})`
|
|
34774
|
+
);
|
|
34775
|
+
}
|
|
34760
34776
|
async listUnits(instanceId) {
|
|
34761
34777
|
const r = await this.post("/runs/coordinator/units/list", { instanceId });
|
|
34762
34778
|
const d = r.data;
|
|
@@ -36133,7 +36149,7 @@ function cardActivity(e) {
|
|
|
36133
36149
|
if (e.type === "tool_call" && e.command !== void 0) return { kind: "command", tool: e.tool, command: e.command };
|
|
36134
36150
|
return { kind: "line", text: activityLine(e) };
|
|
36135
36151
|
}
|
|
36136
|
-
function
|
|
36152
|
+
function compactActivity(activity) {
|
|
36137
36153
|
if (activity?.kind === "command") return { kind: "line", text: `\u2192 ${activity.tool}` };
|
|
36138
36154
|
return activity;
|
|
36139
36155
|
}
|
|
@@ -36238,7 +36254,9 @@ function cardLines(trace, opts) {
|
|
|
36238
36254
|
});
|
|
36239
36255
|
return { ...shape ? { shape } : {}, ...opts.queued ? { queued: opts.queued } : {} };
|
|
36240
36256
|
}
|
|
36241
|
-
async function replyCommandOutput(io, parsed, text) {
|
|
36257
|
+
async function replyCommandOutput(io, parsed, text, display = {}) {
|
|
36258
|
+
if (parsed.kind === "invoke" && parsed.id === "steer.run" && display.ok === true && !shows(display.verbosity ?? "quiet", "verbose"))
|
|
36259
|
+
return;
|
|
36242
36260
|
if (!io.attach || text.length <= LONG_COMMAND_REPLY_CHARS) return io.reply(text);
|
|
36243
36261
|
const nl = text.indexOf("\n");
|
|
36244
36262
|
const lead = nl === -1 ? text : text.slice(0, nl);
|
|
@@ -38783,6 +38801,79 @@ var init_commandChat = __esm({
|
|
|
38783
38801
|
}
|
|
38784
38802
|
});
|
|
38785
38803
|
|
|
38804
|
+
// ../../src/directives.ts
|
|
38805
|
+
function parseBudgetMinutes(value) {
|
|
38806
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
38807
|
+
const minutes = Number(value);
|
|
38808
|
+
return minutes >= MIN_BOUNDARY_MINUTES ? minutes : void 0;
|
|
38809
|
+
}
|
|
38810
|
+
function lastThreadDirectives(history) {
|
|
38811
|
+
const out = {};
|
|
38812
|
+
for (const h of history) {
|
|
38813
|
+
if (h.role !== "user") continue;
|
|
38814
|
+
for (const m of h.text.matchAll(DIRECTIVE_RE)) {
|
|
38815
|
+
if (m[1] === "model") out.model = m[2];
|
|
38816
|
+
else if (m[1] === "effort" && isEffort(m[2])) out.effort = m[2];
|
|
38817
|
+
else if (m[1] === "verbosity" && isVerbosity(m[2])) out.verbosity = m[2];
|
|
38818
|
+
}
|
|
38819
|
+
}
|
|
38820
|
+
return out;
|
|
38821
|
+
}
|
|
38822
|
+
function parseRenewals(value) {
|
|
38823
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
38824
|
+
const count = Number(value);
|
|
38825
|
+
return count <= GRANT_RENEWALS_MAX ? count : void 0;
|
|
38826
|
+
}
|
|
38827
|
+
function parseDirectives(input) {
|
|
38828
|
+
const out = { text: input };
|
|
38829
|
+
let text = input;
|
|
38830
|
+
const head = HEAD_AGENT_RE.exec(input);
|
|
38831
|
+
if (head !== null && AGENTS[head[1]] !== void 0) {
|
|
38832
|
+
out.agent = head[1];
|
|
38833
|
+
text = text.replace(head[0], " ");
|
|
38834
|
+
}
|
|
38835
|
+
for (const m of input.matchAll(DIRECTIVE_RE)) {
|
|
38836
|
+
const [token2, key, value] = m;
|
|
38837
|
+
if (key === "model") {
|
|
38838
|
+
out.model = value;
|
|
38839
|
+
} else if (key === "effort") {
|
|
38840
|
+
if (!isEffort(value)) continue;
|
|
38841
|
+
out.effort = value;
|
|
38842
|
+
} else if (key === "budget") {
|
|
38843
|
+
const minutes = parseBudgetMinutes(value);
|
|
38844
|
+
if (minutes === void 0) continue;
|
|
38845
|
+
out.budget = minutes;
|
|
38846
|
+
} else if (key === "severity") {
|
|
38847
|
+
if (!isAddressSeverity(value)) continue;
|
|
38848
|
+
out.severity = value;
|
|
38849
|
+
} else if (key === "renewals") {
|
|
38850
|
+
const count = parseRenewals(value);
|
|
38851
|
+
if (count === void 0) continue;
|
|
38852
|
+
out.renewals = count;
|
|
38853
|
+
} else {
|
|
38854
|
+
if (!isVerbosity(value)) continue;
|
|
38855
|
+
out.verbosity = value;
|
|
38856
|
+
}
|
|
38857
|
+
text = text.replace(token2, " ");
|
|
38858
|
+
}
|
|
38859
|
+
out.text = text.replace(/\s+/g, " ").trim();
|
|
38860
|
+
return out;
|
|
38861
|
+
}
|
|
38862
|
+
var DIRECTIVE_RE, HEAD_AGENT_RE;
|
|
38863
|
+
var init_directives = __esm({
|
|
38864
|
+
"../../src/directives.ts"() {
|
|
38865
|
+
"use strict";
|
|
38866
|
+
init_registry();
|
|
38867
|
+
init_validate();
|
|
38868
|
+
init_budgets();
|
|
38869
|
+
init_effort();
|
|
38870
|
+
init_shipPipeline();
|
|
38871
|
+
init_verbosity();
|
|
38872
|
+
DIRECTIVE_RE = /(?:^|\s)(model|effort|budget|severity|renewals|verbosity)[:=](\S+)/g;
|
|
38873
|
+
HEAD_AGENT_RE = /^\s*agent[:=](\S+)/;
|
|
38874
|
+
}
|
|
38875
|
+
});
|
|
38876
|
+
|
|
38786
38877
|
// ../../src/core/runRegistry/runControl.ts
|
|
38787
38878
|
var RunControl;
|
|
38788
38879
|
var init_runControl = __esm({
|
|
@@ -39782,6 +39873,7 @@ var init_commandRun = __esm({
|
|
|
39782
39873
|
async function answerChatCommand(deps, ctx) {
|
|
39783
39874
|
const { msg, io, ending, trace } = ctx;
|
|
39784
39875
|
const root = trace.root;
|
|
39876
|
+
const verbosity2 = deps.config.verbosityFor(msg.channelId, msg.userId, parseDirectives(msg.text).verbosity);
|
|
39785
39877
|
if (deps.commands) {
|
|
39786
39878
|
const chatCmd = parseChatCommand(msg.text, deps.commands);
|
|
39787
39879
|
if (chatCmd) {
|
|
@@ -39801,7 +39893,7 @@ async function answerChatCommand(deps, ctx) {
|
|
|
39801
39893
|
confirmations: deps.confirmations
|
|
39802
39894
|
})
|
|
39803
39895
|
);
|
|
39804
|
-
} : () => root.span("post.reply", () => replyCommandOutput(io, chatCmd, res.text));
|
|
39896
|
+
} : () => root.span("post.reply", () => replyCommandOutput(io, chatCmd, res.text, { verbosity: verbosity2, ok: res.ok }));
|
|
39805
39897
|
await ending.sealAfterReply(async () => {
|
|
39806
39898
|
}, replyFn);
|
|
39807
39899
|
if (res.followUp) postSettledOutcome(res.followUp, io, root);
|
|
@@ -39817,6 +39909,7 @@ var init_fastPath = __esm({
|
|
|
39817
39909
|
init_commandChat();
|
|
39818
39910
|
init_reply();
|
|
39819
39911
|
init_refusal();
|
|
39912
|
+
init_directives();
|
|
39820
39913
|
init_commandRun();
|
|
39821
39914
|
init_commandRun();
|
|
39822
39915
|
}
|
|
@@ -40110,79 +40203,6 @@ var init_references = __esm({
|
|
|
40110
40203
|
}
|
|
40111
40204
|
});
|
|
40112
40205
|
|
|
40113
|
-
// ../../src/directives.ts
|
|
40114
|
-
function parseBudgetMinutes(value) {
|
|
40115
|
-
if (!/^\d+$/.test(value)) return void 0;
|
|
40116
|
-
const minutes = Number(value);
|
|
40117
|
-
return minutes >= MIN_BOUNDARY_MINUTES ? minutes : void 0;
|
|
40118
|
-
}
|
|
40119
|
-
function lastThreadDirectives(history) {
|
|
40120
|
-
const out = {};
|
|
40121
|
-
for (const h of history) {
|
|
40122
|
-
if (h.role !== "user") continue;
|
|
40123
|
-
for (const m of h.text.matchAll(DIRECTIVE_RE)) {
|
|
40124
|
-
if (m[1] === "model") out.model = m[2];
|
|
40125
|
-
else if (m[1] === "effort" && isEffort(m[2])) out.effort = m[2];
|
|
40126
|
-
else if (m[1] === "verbosity" && isVerbosity(m[2])) out.verbosity = m[2];
|
|
40127
|
-
}
|
|
40128
|
-
}
|
|
40129
|
-
return out;
|
|
40130
|
-
}
|
|
40131
|
-
function parseRenewals(value) {
|
|
40132
|
-
if (!/^\d+$/.test(value)) return void 0;
|
|
40133
|
-
const count = Number(value);
|
|
40134
|
-
return count <= GRANT_RENEWALS_MAX ? count : void 0;
|
|
40135
|
-
}
|
|
40136
|
-
function parseDirectives(input) {
|
|
40137
|
-
const out = { text: input };
|
|
40138
|
-
let text = input;
|
|
40139
|
-
const head = HEAD_AGENT_RE.exec(input);
|
|
40140
|
-
if (head !== null && AGENTS[head[1]] !== void 0) {
|
|
40141
|
-
out.agent = head[1];
|
|
40142
|
-
text = text.replace(head[0], " ");
|
|
40143
|
-
}
|
|
40144
|
-
for (const m of input.matchAll(DIRECTIVE_RE)) {
|
|
40145
|
-
const [token2, key, value] = m;
|
|
40146
|
-
if (key === "model") {
|
|
40147
|
-
out.model = value;
|
|
40148
|
-
} else if (key === "effort") {
|
|
40149
|
-
if (!isEffort(value)) continue;
|
|
40150
|
-
out.effort = value;
|
|
40151
|
-
} else if (key === "budget") {
|
|
40152
|
-
const minutes = parseBudgetMinutes(value);
|
|
40153
|
-
if (minutes === void 0) continue;
|
|
40154
|
-
out.budget = minutes;
|
|
40155
|
-
} else if (key === "severity") {
|
|
40156
|
-
if (!isAddressSeverity(value)) continue;
|
|
40157
|
-
out.severity = value;
|
|
40158
|
-
} else if (key === "renewals") {
|
|
40159
|
-
const count = parseRenewals(value);
|
|
40160
|
-
if (count === void 0) continue;
|
|
40161
|
-
out.renewals = count;
|
|
40162
|
-
} else {
|
|
40163
|
-
if (!isVerbosity(value)) continue;
|
|
40164
|
-
out.verbosity = value;
|
|
40165
|
-
}
|
|
40166
|
-
text = text.replace(token2, " ");
|
|
40167
|
-
}
|
|
40168
|
-
out.text = text.replace(/\s+/g, " ").trim();
|
|
40169
|
-
return out;
|
|
40170
|
-
}
|
|
40171
|
-
var DIRECTIVE_RE, HEAD_AGENT_RE;
|
|
40172
|
-
var init_directives = __esm({
|
|
40173
|
-
"../../src/directives.ts"() {
|
|
40174
|
-
"use strict";
|
|
40175
|
-
init_registry();
|
|
40176
|
-
init_validate();
|
|
40177
|
-
init_budgets();
|
|
40178
|
-
init_effort();
|
|
40179
|
-
init_shipPipeline();
|
|
40180
|
-
init_verbosity();
|
|
40181
|
-
DIRECTIVE_RE = /(?:^|\s)(model|effort|budget|severity|renewals|verbosity)[:=](\S+)/g;
|
|
40182
|
-
HEAD_AGENT_RE = /^\s*agent[:=](\S+)/;
|
|
40183
|
-
}
|
|
40184
|
-
});
|
|
40185
|
-
|
|
40186
40206
|
// ../../src/execution/githubRepoProbe.ts
|
|
40187
40207
|
function githubRepoProbe(opts) {
|
|
40188
40208
|
const fetchImpl = opts.fetch ?? fetch;
|
|
@@ -41563,10 +41583,8 @@ function presetRequestOf(line4) {
|
|
|
41563
41583
|
async function executeOperatorDecision(deps, ctx) {
|
|
41564
41584
|
const { event, io, msg } = ctx;
|
|
41565
41585
|
const surface = io.offer ? "chat" : "typed";
|
|
41566
|
-
const
|
|
41567
|
-
|
|
41568
|
-
"verbose"
|
|
41569
|
-
);
|
|
41586
|
+
const verbosity2 = deps.config.verbosityFor(msg.channelId, msg.userId, parseDirectives(msg.text).verbosity);
|
|
41587
|
+
const verbose = shows(verbosity2, "verbose");
|
|
41570
41588
|
const answered = { kind: "answered" };
|
|
41571
41589
|
if (ctx.owner?.kind === "pipeline") return { kind: "fold" };
|
|
41572
41590
|
if (event.outcome === "question") {
|
|
@@ -41673,7 +41691,7 @@ async function executeOperatorDecision(deps, ctx) {
|
|
|
41673
41691
|
if (verbose) await io.reply(receipt);
|
|
41674
41692
|
const res = await runChatCommand(deps, msg, io, invocation, ctx.ending, ctx.trace, { operator: executedEvent });
|
|
41675
41693
|
carried = true;
|
|
41676
|
-
if (res.text.length > 0) await io.
|
|
41694
|
+
if (res.text.length > 0) await replyCommandOutput(io, invocation, res.text, { verbosity: verbosity2, ok: res.ok });
|
|
41677
41695
|
}
|
|
41678
41696
|
if (!carried) await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
|
|
41679
41697
|
return answered;
|
|
@@ -42352,7 +42370,7 @@ async function openAckCard(deps, ctx) {
|
|
|
42352
42370
|
});
|
|
42353
42371
|
if (route2) shell.note("debug", routeReasonLabel(route2.reason, route2.collapsed));
|
|
42354
42372
|
const card = coalesceStatus(
|
|
42355
|
-
await root.span("dispatch.ack_card", () => io.status(shell.ack())),
|
|
42373
|
+
await root.span("dispatch.ack_card", () => io.status(shell.ack(), { verbosity: resolved.verbosity })),
|
|
42356
42374
|
deps.statusUpdateMinMs ?? STATUS_UPDATE_MIN_MS
|
|
42357
42375
|
);
|
|
42358
42376
|
trace.bindCard({ setupLabel: (label) => shell.setSetupLabel(label) });
|
|
@@ -44079,7 +44097,7 @@ function watchedChild(io, on) {
|
|
|
44079
44097
|
on.replied(text);
|
|
44080
44098
|
await io.reply(text);
|
|
44081
44099
|
},
|
|
44082
|
-
status: (initial) => io.status(initial),
|
|
44100
|
+
status: (initial, display) => io.status(initial, display),
|
|
44083
44101
|
history: () => io.history(),
|
|
44084
44102
|
runStarted: (started) => {
|
|
44085
44103
|
io.runStarted?.(started);
|
|
@@ -46164,6 +46182,7 @@ async function claimRun(deps, ctx) {
|
|
|
46164
46182
|
parentInstanceId: coordinator.parentInstanceId,
|
|
46165
46183
|
...unit !== void 0 ? { unit } : {},
|
|
46166
46184
|
...coordinator.base !== void 0 ? { base: coordinator.base } : {},
|
|
46185
|
+
...coordinator.publication !== void 0 ? { publication: coordinator.publication } : {},
|
|
46167
46186
|
at: clock4()
|
|
46168
46187
|
});
|
|
46169
46188
|
}
|
|
@@ -47446,7 +47465,7 @@ function established(facts2) {
|
|
|
47446
47465
|
else if (w?.kind === "salvaged")
|
|
47447
47466
|
tree = `What the tree held was pushed to \`${w.branch}\`${w.head ? ` at \`${shortSha2(w.head)}\`` : ""} by the budget salvage, unreviewed \u2014 a follow-up starts from it.`;
|
|
47448
47467
|
else if (w?.kind === "left")
|
|
47449
|
-
tree = w.fate === "kept" ? `${counted(w)} sit in the workspace, kept for this thread until it idles out \u2014 a follow-up here reuses them.` : w.fate === "discarded" ? `${counted(w)} were left in the tree and discarded at the run's end.` : `${counted(w)} were left in the tree, which is torn down since a command may still be running in it.`;
|
|
47468
|
+
tree = w.fate === "kept" ? `${counted(w)} sit in the workspace, kept for this thread until it idles out \u2014 a follow-up here reuses them.` : w.fate === "retained_unverified" ? `${counted(w)} remain in the attached workspace after the existing-PR push was blocked; retention beyond this run is unverified, so no continuation is promised.` : w.fate === "discarded" ? `${counted(w)} were left in the tree and discarded at the run's end.` : `${counted(w)} were left in the tree, which is torn down since a command may still be running in it.`;
|
|
47450
47469
|
const description = facts2?.description === "submitted" ? "The PR description was submitted." : facts2?.description === "not_submitted" ? "No PR description was submitted." : "";
|
|
47451
47470
|
return [tree, description].filter((s) => s !== "").join(" ");
|
|
47452
47471
|
}
|
|
@@ -49979,10 +49998,28 @@ function judgeBashTimeout(timeout, ctx) {
|
|
|
49979
49998
|
return refused4(commandPastLoopEndRefusal(Math.round(timeout), Math.max(0, Math.floor(leftMs / 1e3))));
|
|
49980
49999
|
}
|
|
49981
50000
|
function judgePush(tail, ctx) {
|
|
49982
|
-
const [remote, refspec] = pushArguments(tail);
|
|
50001
|
+
const [remote, refspec, ...additionalRefspecs] = pushArguments(tail);
|
|
49983
50002
|
if (remote !== void 0 && remote !== "origin") {
|
|
49984
50003
|
return refused4(`repo:use \u2014 push to remote \`${remote}\`, not the run's repository (origin)`);
|
|
49985
50004
|
}
|
|
50005
|
+
if (ctx.publication !== void 0) {
|
|
50006
|
+
const publication = ctx.publication.authority;
|
|
50007
|
+
if ("blocked" in publication) return refused4(`repo:use \u2014 existing-PR publication blocked: ${publication.blocked}`);
|
|
50008
|
+
if (refspec === void 0)
|
|
50009
|
+
return refused4("repo:use \u2014 existing-PR publication requires the explicit owned destination");
|
|
50010
|
+
if (additionalRefspecs.length > 0)
|
|
50011
|
+
return refused4("repo:use \u2014 existing-PR publication allows exactly one owned destination");
|
|
50012
|
+
const destination2 = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
|
|
50013
|
+
const branch3 = destination2 === "HEAD" ? ctx.branch : destination2.replace(/^refs\/heads\//, "");
|
|
50014
|
+
if (branch3 !== publication.ref)
|
|
50015
|
+
return refused4(`repo:use \u2014 push to \`${branch3}\`, not the owned publication ref ${publication.ref}`);
|
|
50016
|
+
const lease = `--force-with-lease=refs/heads/${publication.ref}:${publication.expectedHeadSha}`;
|
|
50017
|
+
if (!tail.split(/\s+/).includes(lease))
|
|
50018
|
+
return refused4(
|
|
50019
|
+
`repo:use \u2014 existing-PR publication requires \`${lease}\` so concurrent movement fails atomically`
|
|
50020
|
+
);
|
|
50021
|
+
return allowed;
|
|
50022
|
+
}
|
|
49986
50023
|
if (refspec === void 0) return allowed;
|
|
49987
50024
|
const destination = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
|
|
49988
50025
|
if (ctx.branch !== void 0) {
|
|
@@ -50102,7 +50139,7 @@ function relayedToolDefinitions(harness) {
|
|
|
50102
50139
|
}
|
|
50103
50140
|
function authorizeToolCall(harness, ask) {
|
|
50104
50141
|
harness.gateSaw(ask.toolCallId);
|
|
50105
|
-
const
|
|
50142
|
+
const blocked2 = harness.toolsBlocked();
|
|
50106
50143
|
const refuse3 = (reason2) => {
|
|
50107
50144
|
harness.emit({
|
|
50108
50145
|
type: "run_note",
|
|
@@ -50111,7 +50148,7 @@ function authorizeToolCall(harness, ask) {
|
|
|
50111
50148
|
});
|
|
50112
50149
|
return { allow: false, reason: reason2 };
|
|
50113
50150
|
};
|
|
50114
|
-
if (
|
|
50151
|
+
if (blocked2 !== void 0) return refuse3(blocked2);
|
|
50115
50152
|
if (harness.tools.some((t) => t.name === ask.tool)) return { allow: true };
|
|
50116
50153
|
const verdict = judgeToolCall(ask.tool, ask.input, harness.rules);
|
|
50117
50154
|
if (verdict.verdict === "allowed") return { allow: true };
|
|
@@ -51991,6 +52028,49 @@ var init_resume = __esm({
|
|
|
51991
52028
|
}
|
|
51992
52029
|
});
|
|
51993
52030
|
|
|
52031
|
+
// ../../src/core/existingPrPublication.ts
|
|
52032
|
+
function verifyExistingPrPublication(durable, local, remote) {
|
|
52033
|
+
if (durable === void 0) return blocked("the durable existing-PR binding is missing");
|
|
52034
|
+
if (!fullSha(durable.expectedHeadSha)) return blocked("the durable expected head is not a full commit SHA");
|
|
52035
|
+
if (durable.headRef !== durable.publicationRef)
|
|
52036
|
+
return blocked("the durable head ref and owned publication ref do not agree");
|
|
52037
|
+
if (local.repo !== durable.repo) return blocked("the resolved repository does not match the durable binding");
|
|
52038
|
+
if (local.pr !== durable.pr) return blocked("the resolved pull request does not match the durable binding");
|
|
52039
|
+
if (local.ref !== durable.headRef) return blocked("the resolved head ref does not match the durable binding");
|
|
52040
|
+
if (local.baseRef !== durable.baseRef) return blocked("the resolved base ref does not match the durable binding");
|
|
52041
|
+
if (!fullSha(local.requestHeadSha)) return blocked("the request head is missing or not a full commit SHA");
|
|
52042
|
+
if (local.requestHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52043
|
+
return blocked("the request head does not match the durable expected commit");
|
|
52044
|
+
if (local.workspaceRef !== durable.publicationRef)
|
|
52045
|
+
return blocked("the checked-out publication ref does not match the durable binding");
|
|
52046
|
+
if (!fullSha(local.workspaceHeadSha)) return blocked("the checked-out head is missing or not a full commit SHA");
|
|
52047
|
+
if (local.workspaceHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52048
|
+
return blocked("the checked-out head does not match the durable expected commit");
|
|
52049
|
+
if (!sameOwner(local.owner, durable.owner)) return blocked("the publication owner changed");
|
|
52050
|
+
if (remote === void 0) return blocked("the fresh pull-request read is unavailable");
|
|
52051
|
+
if (remote.state !== "open") return blocked("the bound pull request is no longer open");
|
|
52052
|
+
if (remote.sameRepoHead !== true) return blocked("the pull request head is not in the bound repository");
|
|
52053
|
+
if (remote.headBranchExists !== true) return blocked("the pull request head branch is missing or unknown");
|
|
52054
|
+
if (remote.headRef !== durable.headRef) return blocked("the pull request head ref moved");
|
|
52055
|
+
if (remote.baseRef !== durable.baseRef) return blocked("the pull request base ref moved");
|
|
52056
|
+
if (!fullSha(remote.headSha)) return blocked("the fresh pull-request head is missing or not a full commit SHA");
|
|
52057
|
+
if (remote.headSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52058
|
+
return blocked("the pull request head moved from the durable expected commit");
|
|
52059
|
+
return {
|
|
52060
|
+
ok: true,
|
|
52061
|
+
publication: { ref: durable.publicationRef, expectedHeadSha: durable.expectedHeadSha.toLowerCase() }
|
|
52062
|
+
};
|
|
52063
|
+
}
|
|
52064
|
+
var fullSha, sameOwner, blocked;
|
|
52065
|
+
var init_existingPrPublication = __esm({
|
|
52066
|
+
"../../src/core/existingPrPublication.ts"() {
|
|
52067
|
+
"use strict";
|
|
52068
|
+
fullSha = (value) => value !== void 0 && /^[0-9a-f]{40}$/i.test(value);
|
|
52069
|
+
sameOwner = (left, right) => left?.instanceId === right.instanceId && left.unit === right.unit;
|
|
52070
|
+
blocked = (reason2) => ({ ok: false, reason: reason2 });
|
|
52071
|
+
}
|
|
52072
|
+
});
|
|
52073
|
+
|
|
51994
52074
|
// ../../src/execution/githubComments.ts
|
|
51995
52075
|
async function postReviewComment(target, body) {
|
|
51996
52076
|
const codePoints = [...body].length;
|
|
@@ -52234,7 +52314,26 @@ async function salvageBudgetPush(executor, opts, span) {
|
|
|
52234
52314
|
}
|
|
52235
52315
|
const unpushed = parseCountOutput(await run2("git rev-list --count HEAD --not --remotes")) ?? 0;
|
|
52236
52316
|
if (!dirty && !endingCheckpoint && unpushed === 0) return { pushed: false, summary: words.nothing };
|
|
52237
|
-
|
|
52317
|
+
if (opts.publication !== void 0 && "blocked" in opts.publication)
|
|
52318
|
+
return {
|
|
52319
|
+
pushed: false,
|
|
52320
|
+
summary: `${words.failedLead}: existing-PR publication is blocked (${opts.publication.blocked}) \u2014 the commit remains unpublished in the bound workspace; no alternate ref was created`
|
|
52321
|
+
};
|
|
52322
|
+
const lease = opts.publication !== void 0 ? ` --force-with-lease=${shellQuote(`refs/heads/${opts.publication.ref}:${opts.publication.expectedHeadSha}`)}` : "";
|
|
52323
|
+
try {
|
|
52324
|
+
await run2(`git push${lease} origin ${shellQuote(`HEAD:refs/heads/${opts.branch}`)}`);
|
|
52325
|
+
} catch (err2) {
|
|
52326
|
+
if (opts.publication !== void 0) {
|
|
52327
|
+
const detail = err2 instanceof Error ? err2.message : String(err2);
|
|
52328
|
+
const publicationBlocked = `the atomic leased push was rejected: ${detail}`;
|
|
52329
|
+
return {
|
|
52330
|
+
pushed: false,
|
|
52331
|
+
publicationBlocked,
|
|
52332
|
+
summary: `${words.failedLead}: existing-PR publication is blocked (${publicationBlocked}) \u2014 the commit remains unpublished in the bound workspace; no alternate ref was created`
|
|
52333
|
+
};
|
|
52334
|
+
}
|
|
52335
|
+
throw err2;
|
|
52336
|
+
}
|
|
52238
52337
|
const head = parseRevParseOutput(await probe("git rev-parse HEAD"));
|
|
52239
52338
|
return {
|
|
52240
52339
|
pushed: true,
|
|
@@ -52961,6 +53060,12 @@ async function runLoop(deps, ctx) {
|
|
|
52961
53060
|
return void 0;
|
|
52962
53061
|
}
|
|
52963
53062
|
})();
|
|
53063
|
+
let existingPrPublication;
|
|
53064
|
+
let existingPrPublicationFence;
|
|
53065
|
+
const blockExistingPrPublication = (reason2) => {
|
|
53066
|
+
existingPrPublication = { blocked: reason2 };
|
|
53067
|
+
if (existingPrPublicationFence !== void 0) existingPrPublicationFence.authority = existingPrPublication;
|
|
53068
|
+
};
|
|
52964
53069
|
const identitySeam = deps.identityRewrite;
|
|
52965
53070
|
const startStateRead = identitySeam !== void 0 && isCodingPrRun && repoCtx.repo !== void 0 ? (async () => {
|
|
52966
53071
|
const repo = repoCtx.repo;
|
|
@@ -52997,14 +53102,14 @@ async function runLoop(deps, ctx) {
|
|
|
52997
53102
|
);
|
|
52998
53103
|
return pace ? ` \xB7 ${pace}` : "";
|
|
52999
53104
|
};
|
|
53105
|
+
const showsActivity = shows(resolved.verbosity, "verbose");
|
|
53000
53106
|
const chatty = shows(resolved.verbosity, "debug");
|
|
53001
53107
|
const showBookkeeping = shows(resolved.verbosity, "verbose");
|
|
53002
53108
|
const currentFrame = () => shell.live({
|
|
53003
|
-
|
|
53004
|
-
suffix: chatty ? `${quietSuffix(clock4() - lastActivityAt, inFlightTool)}${cardPace()}` : cardPace(),
|
|
53109
|
+
suffix: showsActivity ? chatty ? `${quietSuffix(clock4() - lastActivityAt, inFlightTool)}${cardPace()}` : cardPace() : "",
|
|
53005
53110
|
notice: shutdownNotice(),
|
|
53006
53111
|
detail: [checklist],
|
|
53007
|
-
activity: chatty ? lastActivity :
|
|
53112
|
+
activity: showsActivity ? chatty ? lastActivity : compactActivity(lastActivity) : void 0
|
|
53008
53113
|
});
|
|
53009
53114
|
const checklistAsLeft = () => checklist;
|
|
53010
53115
|
const checklistCheckedOff = () => checklist?.replace(/^(\s*)[○✱](?=\s)/gm, "$1\u2713");
|
|
@@ -53239,8 +53344,18 @@ async function runLoop(deps, ctx) {
|
|
|
53239
53344
|
});
|
|
53240
53345
|
const salvaged = "skipped" in target ? { pushed: false, summary: target.skipped } : await root.span(
|
|
53241
53346
|
cue === "budget" ? "run.budget_salvage" : "run.work_salvage",
|
|
53242
|
-
(span) => salvageBudgetPush(
|
|
53347
|
+
(span) => salvageBudgetPush(
|
|
53348
|
+
executor,
|
|
53349
|
+
{
|
|
53350
|
+
branch: target.branch,
|
|
53351
|
+
cue,
|
|
53352
|
+
...existingPrPublication !== void 0 ? { publication: existingPrPublication } : {}
|
|
53353
|
+
},
|
|
53354
|
+
span
|
|
53355
|
+
)
|
|
53243
53356
|
);
|
|
53357
|
+
if ("publicationBlocked" in salvaged && salvaged.publicationBlocked !== void 0)
|
|
53358
|
+
blockExistingPrPublication(salvaged.publicationBlocked);
|
|
53244
53359
|
const cleanCompletion = cue === "completion" && !salvaged.pushed && (/ended with nothing to preserve/i.test(salvaged.summary) || "skipped" in target && (observedUncommitted ?? 0) === 0 && (observedUnpushed ?? 0) === 0);
|
|
53245
53360
|
if (cleanCompletion) return;
|
|
53246
53361
|
workSalvageAttempted = true;
|
|
@@ -53255,7 +53370,10 @@ async function runLoop(deps, ctx) {
|
|
|
53255
53370
|
salvagedTo = { branch: target.branch, head: salvaged.head };
|
|
53256
53371
|
} else if (salvaged.pushed && !("skipped" in target)) salvagedTo = { branch: target.branch };
|
|
53257
53372
|
if (salvaged.pushed) await observeWorkspaceNow();
|
|
53258
|
-
else if ("
|
|
53373
|
+
else if (existingPrPublication !== void 0 && "blocked" in existingPrPublication) {
|
|
53374
|
+
await observeWorkspaceNow();
|
|
53375
|
+
shell.note("quiet", salvaged.summary);
|
|
53376
|
+
} else if ("skipped" in target || /failed|not attempted/i.test(salvaged.summary))
|
|
53259
53377
|
shell.note("quiet", salvaged.summary);
|
|
53260
53378
|
};
|
|
53261
53379
|
let relaunchEndedRun = false;
|
|
@@ -53281,7 +53399,9 @@ async function runLoop(deps, ctx) {
|
|
|
53281
53399
|
return true;
|
|
53282
53400
|
};
|
|
53283
53401
|
const tailSkipped = () => run2.control.requested === "hard" || relaunchEndedRun || latchReviewStoppedBeforeStart();
|
|
53402
|
+
const keepBlockedPublicationWorkspace = () => existingPrPublication !== void 0 && "blocked" in existingPrPublication && run2.control.requested !== "hard" && !commandInFlight && !gateBypassed && ((observedUncommitted ?? 0) > 0 || (observedUnpushed ?? 0) > 0);
|
|
53284
53403
|
const releaseWorkspace = (span) => {
|
|
53404
|
+
if (keepBlockedPublicationWorkspace()) return Promise.resolve();
|
|
53285
53405
|
const events2 = recordEvents();
|
|
53286
53406
|
const pushed = pushedBranchesOf(events2);
|
|
53287
53407
|
return round2.release({
|
|
@@ -53434,7 +53554,38 @@ async function runLoop(deps, ctx) {
|
|
|
53434
53554
|
if (!providerCfg)
|
|
53435
53555
|
throw new Error(`the ${harness.name} harness found no provider named ${providerName} in the config`);
|
|
53436
53556
|
const prBase = repoCtx.baseRef ?? await coordinatorBase;
|
|
53437
|
-
|
|
53557
|
+
if (coordinator?.publication !== void 0) {
|
|
53558
|
+
const publication = coordinator.publication;
|
|
53559
|
+
const fresh = await (deps.fetchPrFacts ?? fetchPullRequestFacts)({
|
|
53560
|
+
repo: publication.repo,
|
|
53561
|
+
number: publication.pr
|
|
53562
|
+
}).catch(() => void 0);
|
|
53563
|
+
const verified = verifyExistingPrPublication(
|
|
53564
|
+
publication,
|
|
53565
|
+
{
|
|
53566
|
+
repo: repoCtx.repo,
|
|
53567
|
+
pr: repoCtx.pr,
|
|
53568
|
+
ref: repoCtx.ref,
|
|
53569
|
+
baseRef: prBase,
|
|
53570
|
+
requestHeadSha: repoCtx.headSha,
|
|
53571
|
+
workspaceRef: binding?.ref,
|
|
53572
|
+
workspaceHeadSha: binding?.sha,
|
|
53573
|
+
owner: {
|
|
53574
|
+
instanceId: coordinator.parentInstanceId,
|
|
53575
|
+
unit: unitOfIdempotencyKey(coordinator.idempotencyKey) ?? ""
|
|
53576
|
+
}
|
|
53577
|
+
},
|
|
53578
|
+
fresh
|
|
53579
|
+
);
|
|
53580
|
+
existingPrPublication = verified.ok ? verified.publication : { blocked: verified.reason };
|
|
53581
|
+
existingPrPublicationFence = { authority: existingPrPublication };
|
|
53582
|
+
if (!verified.ok)
|
|
53583
|
+
shell.note(
|
|
53584
|
+
"quiet",
|
|
53585
|
+
`Existing-PR publication is blocked: ${verified.reason}. Work stays on the bound checkout; no alternate branch or pull request will be published.`
|
|
53586
|
+
);
|
|
53587
|
+
}
|
|
53588
|
+
const ownBranch = coordinator?.publication !== void 0 ? coordinator.publication.publicationRef : prBase !== void 0 ? binding?.ref ?? repoCtx.ref : void 0;
|
|
53438
53589
|
const protectedBranches = [
|
|
53439
53590
|
...new Set(
|
|
53440
53591
|
(prBase !== void 0 ? [prBase] : [binding?.ref, repoCtx.ref]).filter(
|
|
@@ -53494,8 +53645,18 @@ async function runLoop(deps, ctx) {
|
|
|
53494
53645
|
const branch2 = ownBranch;
|
|
53495
53646
|
const salvaged = await root.span(
|
|
53496
53647
|
"run.compaction_salvage",
|
|
53497
|
-
(span) => salvageBudgetPush(
|
|
53648
|
+
(span) => salvageBudgetPush(
|
|
53649
|
+
executor,
|
|
53650
|
+
{
|
|
53651
|
+
branch: branch2,
|
|
53652
|
+
cue: "compaction",
|
|
53653
|
+
...existingPrPublication !== void 0 ? { publication: existingPrPublication } : {}
|
|
53654
|
+
},
|
|
53655
|
+
span
|
|
53656
|
+
)
|
|
53498
53657
|
);
|
|
53658
|
+
if (salvaged.publicationBlocked !== void 0)
|
|
53659
|
+
blockExistingPrPublication(salvaged.publicationBlocked);
|
|
53499
53660
|
onEvent({
|
|
53500
53661
|
type: "run_note",
|
|
53501
53662
|
kind: "compaction_salvage",
|
|
@@ -53508,7 +53669,8 @@ async function runLoop(deps, ctx) {
|
|
|
53508
53669
|
rules: {
|
|
53509
53670
|
checkout: binding?.workspace ?? "/workspace",
|
|
53510
53671
|
...ownBranch !== void 0 ? { branch: ownBranch } : {},
|
|
53511
|
-
protectedBranches
|
|
53672
|
+
protectedBranches,
|
|
53673
|
+
...existingPrPublicationFence !== void 0 ? { publication: existingPrPublicationFence } : {}
|
|
53512
53674
|
},
|
|
53513
53675
|
...round2.selection.backend ? { backend: round2.selection.backend } : {},
|
|
53514
53676
|
span: root,
|
|
@@ -53724,6 +53886,12 @@ async function runLoop(deps, ctx) {
|
|
|
53724
53886
|
if (descriptionTurnRan) await preserveCodingChildWork("completion");
|
|
53725
53887
|
await endHarness();
|
|
53726
53888
|
const leftBehind = isCodingPrRun && !tailSkipped() && !workSalvageAttempted ? workLeftBehindOf({ uncommittedChanges: observedUncommitted, unpushedCommits: observedUnpushed }) : void 0;
|
|
53889
|
+
const blockedCheckpoint = keepBlockedPublicationWorkspace() && existingPrPublication !== void 0 && "blocked" in existingPrPublication && observedUncommitted !== void 0 && observedUnpushed !== void 0 && (observedUncommitted > 0 || observedUnpushed > 0) ? {
|
|
53890
|
+
reason: existingPrPublication.blocked,
|
|
53891
|
+
uncommitted: observedUncommitted,
|
|
53892
|
+
unpushed: observedUnpushed,
|
|
53893
|
+
...observedHead !== void 0 ? { head: observedHead } : {}
|
|
53894
|
+
} : void 0;
|
|
53727
53895
|
const endingFacts = () => {
|
|
53728
53896
|
if (profile.machine === "none") return { workspace: { kind: "none" } };
|
|
53729
53897
|
if (!isCodingPrRun || tailSkipped()) return { workspace: { kind: "unread" } };
|
|
@@ -53742,11 +53910,16 @@ async function runLoop(deps, ctx) {
|
|
|
53742
53910
|
kind: "left",
|
|
53743
53911
|
uncommitted: observedUncommitted,
|
|
53744
53912
|
unpushed: observedUnpushed,
|
|
53745
|
-
fate: commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
53913
|
+
fate: blockedCheckpoint !== void 0 ? "retained_unverified" : commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
53746
53914
|
};
|
|
53747
53915
|
return { workspace, description: prDescription !== void 0 ? "submitted" : "not_submitted" };
|
|
53748
53916
|
};
|
|
53749
|
-
if (
|
|
53917
|
+
if (blockedCheckpoint !== void 0) {
|
|
53918
|
+
const checkpoint = blockedCheckpoint.head ? ` at ${blockedCheckpoint.head.slice(0, 7)}` : "";
|
|
53919
|
+
const summary = `the existing-PR push is blocked (${blockedCheckpoint.reason}); kept the local checkpoint${checkpoint} with ${blockedCheckpoint.uncommitted} uncommitted change(s) and ${blockedCheckpoint.unpushed} unpushed commit(s) in the run workspace; no alternate ref or pull request was created, and retention beyond this run is unverified`;
|
|
53920
|
+
events.publish({ type: "run_note", kind: "publication_blocked", summary: oneLine(summary), at: clock4() });
|
|
53921
|
+
shell.note("quiet", `\u26A0\uFE0F ${summary}`);
|
|
53922
|
+
} else if (leftBehind) {
|
|
53750
53923
|
events.publish({
|
|
53751
53924
|
type: "run_note",
|
|
53752
53925
|
kind: "work_left_behind",
|
|
@@ -53762,7 +53935,7 @@ async function runLoop(deps, ctx) {
|
|
|
53762
53935
|
at: clock4()
|
|
53763
53936
|
});
|
|
53764
53937
|
}
|
|
53765
|
-
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted) {
|
|
53938
|
+
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted && !(existingPrPublication !== void 0 && "blocked" in existingPrPublication)) {
|
|
53766
53939
|
const instance = coordinator && deps.coordinatorInstances ? await deps.coordinatorInstances.get(coordinator.parentInstanceId).catch(() => void 0) : void 0;
|
|
53767
53940
|
const requester = instance?.userId === msg.userId ? instance : msg;
|
|
53768
53941
|
const requestedLogin = identitySeam !== void 0 ? await identitySeam.requestedLogin(requester.userId).catch(() => void 0) : void 0;
|
|
@@ -54033,6 +54206,7 @@ var init_runLoop = __esm({
|
|
|
54033
54206
|
"use strict";
|
|
54034
54207
|
init_actor();
|
|
54035
54208
|
init_predicate();
|
|
54209
|
+
init_contract2();
|
|
54036
54210
|
init_decisionRecordReservation();
|
|
54037
54211
|
init_profile();
|
|
54038
54212
|
init_provider();
|
|
@@ -54048,6 +54222,7 @@ var init_runLoop = __esm({
|
|
|
54048
54222
|
init_resume();
|
|
54049
54223
|
init_githubPulls();
|
|
54050
54224
|
init_repoContext();
|
|
54225
|
+
init_existingPrPublication();
|
|
54051
54226
|
init_prDescription();
|
|
54052
54227
|
init_handoff();
|
|
54053
54228
|
init_reviewVerdict();
|
|
@@ -54202,7 +54377,7 @@ async function shipPreflight(input) {
|
|
|
54202
54377
|
`\u{1F6AB} ${where}'s head branch lives on a fork, not on \`${repo}\` \u2014 ship cannot drive it.`
|
|
54203
54378
|
);
|
|
54204
54379
|
}
|
|
54205
|
-
const branch2 = facts2.headRef
|
|
54380
|
+
const branch2 = facts2.headRef;
|
|
54206
54381
|
if (!branch2) {
|
|
54207
54382
|
return refuse2(
|
|
54208
54383
|
"ship_preflight_head_unknown",
|
|
@@ -54211,10 +54386,34 @@ async function shipPreflight(input) {
|
|
|
54211
54386
|
`\u{1F6AB} Could not determine ${where}'s head branch, so ship cannot bind the thread's worktree to it \u2014 refusing fail-closed.`
|
|
54212
54387
|
);
|
|
54213
54388
|
}
|
|
54214
|
-
const
|
|
54389
|
+
const headSha = facts2.headSha;
|
|
54390
|
+
if (headSha === void 0 || !/^[0-9a-f]{40}$/i.test(headSha)) {
|
|
54391
|
+
return refuse2(
|
|
54392
|
+
"ship_preflight_head_unknown",
|
|
54393
|
+
"full head commit unknown",
|
|
54394
|
+
"not started (head commit unknown)",
|
|
54395
|
+
`\u{1F6AB} Could not determine ${where}'s full head commit, so ship cannot fence publication to it \u2014 refusing fail-closed.`
|
|
54396
|
+
);
|
|
54397
|
+
}
|
|
54398
|
+
if (facts2.baseRef === void 0) {
|
|
54399
|
+
return refuse2(
|
|
54400
|
+
"ship_preflight_head_unknown",
|
|
54401
|
+
"base branch unknown",
|
|
54402
|
+
"not started (base branch unknown)",
|
|
54403
|
+
`\u{1F6AB} Could not determine ${where}'s base branch, so ship cannot bind publication to it \u2014 refusing fail-closed.`
|
|
54404
|
+
);
|
|
54405
|
+
}
|
|
54406
|
+
const ownBase = facts2.baseRef;
|
|
54215
54407
|
const ownBaseExists = ownBase !== void 0 && input.refExists ? await input.refExists(repo, ownBase).catch(() => void 0) : void 0;
|
|
54216
|
-
|
|
54217
|
-
|
|
54408
|
+
if (ownBaseExists === false) {
|
|
54409
|
+
return refuse2(
|
|
54410
|
+
"ship_preflight_head_unknown",
|
|
54411
|
+
"pull request base branch missing",
|
|
54412
|
+
"not started (base branch missing)",
|
|
54413
|
+
`\u{1F6AB} ${where}'s base branch \`${ownBase}\` is not present in \`${repo}\`, so ship cannot establish an exact publication binding \u2014 refusing fail-closed.`
|
|
54414
|
+
);
|
|
54415
|
+
}
|
|
54416
|
+
const base = resolveBaseRef([ownBase], info?.defaultBranch);
|
|
54218
54417
|
const autoMerge = facts2.autoMergeEnabled !== void 0 ? { autoMergeEnabled: facts2.autoMergeEnabled } : {};
|
|
54219
54418
|
if (task) {
|
|
54220
54419
|
return {
|
|
@@ -54223,8 +54422,7 @@ async function shipPreflight(input) {
|
|
|
54223
54422
|
repo,
|
|
54224
54423
|
branch: branch2,
|
|
54225
54424
|
base,
|
|
54226
|
-
adopt: { pr: repoCtx.pr, ...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {} },
|
|
54227
|
-
...baseFallback !== void 0 ? { baseFallback } : {},
|
|
54425
|
+
adopt: { pr: repoCtx.pr, headSha, ...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {} },
|
|
54228
54426
|
...autoMerge
|
|
54229
54427
|
}
|
|
54230
54428
|
};
|
|
@@ -54237,10 +54435,9 @@ async function shipPreflight(input) {
|
|
|
54237
54435
|
base,
|
|
54238
54436
|
resume: {
|
|
54239
54437
|
pr: repoCtx.pr,
|
|
54240
|
-
headSha
|
|
54438
|
+
headSha,
|
|
54241
54439
|
...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {}
|
|
54242
54440
|
},
|
|
54243
|
-
...baseFallback !== void 0 ? { baseFallback } : {},
|
|
54244
54441
|
...autoMerge
|
|
54245
54442
|
}
|
|
54246
54443
|
};
|
|
@@ -54432,15 +54629,27 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
|
|
|
54432
54629
|
const previous = carried?.get(u.id);
|
|
54433
54630
|
const taskKey = p.recordTasks?.[u.id];
|
|
54434
54631
|
const record3 = taskKey !== void 0 ? await reserve(p.identity.repo, taskKey, previous?.record) : previous?.record;
|
|
54632
|
+
const branch2 = p.entryBranch ?? u.branch;
|
|
54633
|
+
const existing = p.adopt ?? p.resume;
|
|
54634
|
+
const publication = existing !== void 0 && p.entryBranch !== void 0 ? {
|
|
54635
|
+
repo: p.identity.repo,
|
|
54636
|
+
pr: existing.pr,
|
|
54637
|
+
headRef: p.entryBranch,
|
|
54638
|
+
baseRef: p.base,
|
|
54639
|
+
expectedHeadSha: existing.headSha,
|
|
54640
|
+
publicationRef: p.entryBranch,
|
|
54641
|
+
owner: { instanceId, unit: u.id }
|
|
54642
|
+
} : void 0;
|
|
54435
54643
|
return {
|
|
54436
54644
|
instanceId,
|
|
54437
54645
|
unit: u.id,
|
|
54438
54646
|
slug: u.slug,
|
|
54439
54647
|
title: u.title,
|
|
54440
|
-
branch:
|
|
54648
|
+
branch: branch2,
|
|
54441
54649
|
dependsOn: u.dependsOn,
|
|
54442
54650
|
rounds: [],
|
|
54443
54651
|
...p.resume !== void 0 ? { resume: p.resume } : {},
|
|
54652
|
+
...publication !== void 0 ? { publication } : {},
|
|
54444
54653
|
...previous?.lastPush !== void 0 ? { lastPush: previous.lastPush } : {},
|
|
54445
54654
|
...record3 !== void 0 ? { record: record3 } : {}
|
|
54446
54655
|
};
|
|
@@ -54577,75 +54786,103 @@ async function handOffToCoordinatorUnchecked(deps, input) {
|
|
|
54577
54786
|
}
|
|
54578
54787
|
async function start(deps, input, instance, units, where, write) {
|
|
54579
54788
|
const log = deps.log ?? console.log;
|
|
54580
|
-
const
|
|
54581
|
-
if (
|
|
54582
|
-
|
|
54789
|
+
const reservation = await input.beforeStart?.();
|
|
54790
|
+
if (reservation?.ok === false)
|
|
54791
|
+
return { status: "aborted", reply: reservation.refusal.text, refusal: reservation.refusal };
|
|
54792
|
+
let started = false;
|
|
54793
|
+
try {
|
|
54794
|
+
const put = write === "replace" ? await deps.instances.replace(instance) : await deps.instances.put(instance);
|
|
54795
|
+
if (!put.ok) {
|
|
54796
|
+
if (put.reason === "unavailable")
|
|
54797
|
+
return refused5(
|
|
54798
|
+
"plan_history_unavailable",
|
|
54799
|
+
"\u26A0\uFE0F The plan runner needs run history on the state Worker (`runHistory.worker`): the instance record could not be written, so nothing ran."
|
|
54800
|
+
);
|
|
54583
54801
|
return refused5(
|
|
54584
|
-
"
|
|
54585
|
-
|
|
54802
|
+
"plan_runner_conflict",
|
|
54803
|
+
`\u{1F6AB} A runner for \`${instance.id}\` was just recorded by another request, so this request started nothing; the recorded runner owns the pipeline.`
|
|
54586
54804
|
);
|
|
54587
|
-
|
|
54588
|
-
|
|
54589
|
-
|
|
54590
|
-
);
|
|
54591
|
-
}
|
|
54592
|
-
const rows = await deps.instances.putUnits(units);
|
|
54593
|
-
if (!rows.ok)
|
|
54594
|
-
return refused5(
|
|
54595
|
-
"plan_history_unavailable",
|
|
54596
|
-
"\u26A0\uFE0F The plan runner needs run history on the state Worker: the unit rows could not be written, so nothing ran."
|
|
54597
|
-
);
|
|
54598
|
-
const accepted = [...input.msg.images ?? [], ...input.msg.documents ?? []];
|
|
54599
|
-
if (instance.plan?.path === void 0 && accepted.length > 0) {
|
|
54600
|
-
const unit = units[0];
|
|
54601
|
-
const seeded = await deps.instances.appendEvent(
|
|
54602
|
-
{ instanceId: instance.id, unit: unit.unit },
|
|
54603
|
-
{
|
|
54604
|
-
id: `${instance.id}:${unit.unit}:ship-request`,
|
|
54605
|
-
sender: input.msg.userId,
|
|
54606
|
-
...input.msg.userName !== void 0 ? { senderName: input.msg.userName } : {},
|
|
54607
|
-
text: "Attachments from the ship request.",
|
|
54608
|
-
attachments: accepted,
|
|
54609
|
-
mode: "steer",
|
|
54610
|
-
at: input.now
|
|
54611
|
-
}
|
|
54612
|
-
);
|
|
54613
|
-
if (!seeded.ok)
|
|
54805
|
+
}
|
|
54806
|
+
const rows = await deps.instances.putUnits(units);
|
|
54807
|
+
if (!rows.ok)
|
|
54614
54808
|
return refused5(
|
|
54615
54809
|
"plan_history_unavailable",
|
|
54616
|
-
"\u26A0\uFE0F The plan runner needs run history on the state Worker: the
|
|
54810
|
+
"\u26A0\uFE0F The plan runner needs run history on the state Worker: the unit rows could not be written, so nothing ran."
|
|
54617
54811
|
);
|
|
54618
|
-
|
|
54619
|
-
|
|
54620
|
-
|
|
54621
|
-
|
|
54622
|
-
|
|
54623
|
-
|
|
54624
|
-
|
|
54625
|
-
|
|
54626
|
-
|
|
54627
|
-
|
|
54628
|
-
|
|
54629
|
-
|
|
54630
|
-
|
|
54631
|
-
|
|
54632
|
-
instanceId: instance.id
|
|
54633
|
-
};
|
|
54634
|
-
}
|
|
54635
|
-
case "duplicate": {
|
|
54636
|
-
const status3 = answer.status !== void 0 ? `, status: ${answer.status}` : "";
|
|
54637
|
-
return refused5(
|
|
54638
|
-
"plan_instance_orphaned",
|
|
54639
|
-
`\u{1F6AB} A Workflow instance \`${instance.id}\` already exists on the platform${status3} but the state Worker knew nothing of it \u2014 a person decides; re-issuing will not resume it.`
|
|
54812
|
+
const accepted = [...input.msg.images ?? [], ...input.msg.documents ?? []];
|
|
54813
|
+
if (instance.plan?.path === void 0 && accepted.length > 0) {
|
|
54814
|
+
const unit = units[0];
|
|
54815
|
+
const seeded = await deps.instances.appendEvent(
|
|
54816
|
+
{ instanceId: instance.id, unit: unit.unit },
|
|
54817
|
+
{
|
|
54818
|
+
id: `${instance.id}:${unit.unit}:ship-request`,
|
|
54819
|
+
sender: input.msg.userId,
|
|
54820
|
+
...input.msg.userName !== void 0 ? { senderName: input.msg.userName } : {},
|
|
54821
|
+
text: "Attachments from the ship request.",
|
|
54822
|
+
attachments: accepted,
|
|
54823
|
+
mode: "steer",
|
|
54824
|
+
at: input.now
|
|
54825
|
+
}
|
|
54640
54826
|
);
|
|
54827
|
+
if (!seeded.ok)
|
|
54828
|
+
return refused5(
|
|
54829
|
+
"plan_history_unavailable",
|
|
54830
|
+
"\u26A0\uFE0F The plan runner needs run history on the state Worker: the ship request's attachments could not be written, so nothing ran."
|
|
54831
|
+
);
|
|
54641
54832
|
}
|
|
54642
|
-
|
|
54643
|
-
|
|
54644
|
-
|
|
54645
|
-
|
|
54646
|
-
|
|
54647
|
-
|
|
54648
|
-
|
|
54833
|
+
if (reservation?.ok === true) {
|
|
54834
|
+
const unit = units[0];
|
|
54835
|
+
if (unit === void 0)
|
|
54836
|
+
return refused5(
|
|
54837
|
+
"setup_failed",
|
|
54838
|
+
"\u26A0\uFE0F The plan runner had no durable unit to own at its final start gate, so nothing ran."
|
|
54839
|
+
);
|
|
54840
|
+
try {
|
|
54841
|
+
await reservation.commit({ instanceId: instance.id, unit: unit.unit });
|
|
54842
|
+
} catch (err2) {
|
|
54843
|
+
return refused5(
|
|
54844
|
+
"setup_failed",
|
|
54845
|
+
`\u26A0\uFE0F The plan runner could not transfer existing-pull-request ownership to its new attempt (${describe7(err2)}), so nothing ran.`
|
|
54846
|
+
);
|
|
54847
|
+
}
|
|
54848
|
+
}
|
|
54849
|
+
let answer;
|
|
54850
|
+
try {
|
|
54851
|
+
answer = await deps.create(instance.id);
|
|
54852
|
+
} catch (err2) {
|
|
54853
|
+
answer = { kind: "unanswered", reason: describe7(err2) };
|
|
54854
|
+
}
|
|
54855
|
+
switch (answer.kind) {
|
|
54856
|
+
case "created": {
|
|
54857
|
+
if (reservation?.ok === true) reservation.complete();
|
|
54858
|
+
started = true;
|
|
54859
|
+
log(`[ship] ${input.msg.threadKey}: handed to the plan runner ${instance.id} (${units.length} unit(s))`);
|
|
54860
|
+
const replaced = write === "replace" ? ["the records of an earlier attempt that never started were replaced"] : [];
|
|
54861
|
+
return {
|
|
54862
|
+
status: "completed",
|
|
54863
|
+
reply: handedOff([...where, ...replaced]),
|
|
54864
|
+
instanceId: instance.id
|
|
54865
|
+
};
|
|
54866
|
+
}
|
|
54867
|
+
case "duplicate": {
|
|
54868
|
+
const status3 = answer.status !== void 0 ? `, status: ${answer.status}` : "";
|
|
54869
|
+
return refused5(
|
|
54870
|
+
"plan_instance_orphaned",
|
|
54871
|
+
`\u{1F6AB} A Workflow instance \`${instance.id}\` already exists on the platform${status3} but the state Worker knew nothing of it \u2014 a person decides; re-issuing will not resume it.`
|
|
54872
|
+
);
|
|
54873
|
+
}
|
|
54874
|
+
case "failed":
|
|
54875
|
+
case "unanswered":
|
|
54876
|
+
log(`[ship] ${input.msg.threadKey}: the plan runner ${instance.id} could not be started \u2014 ${answer.reason}`);
|
|
54877
|
+
return refused5(
|
|
54878
|
+
"plan_start_failed",
|
|
54879
|
+
`\u26A0\uFE0F This is a bug: the plan runner could not be started (${answer.reason}), nothing ran, and no automatic start retry was scheduled.`
|
|
54880
|
+
);
|
|
54881
|
+
}
|
|
54882
|
+
const unreachable3 = answer;
|
|
54883
|
+
return unreachable3;
|
|
54884
|
+
} finally {
|
|
54885
|
+
if (!started && reservation?.ok === true) await reservation.abort();
|
|
54649
54886
|
}
|
|
54650
54887
|
}
|
|
54651
54888
|
function handedOff(facts2) {
|
|
@@ -55006,6 +55243,7 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
55006
55243
|
...msg.authenticatedAs !== void 0 ? { authenticatedAs: msg.authenticatedAs } : {},
|
|
55007
55244
|
...msg.postedBy !== void 0 ? { postedBy: msg.postedBy } : {},
|
|
55008
55245
|
...entry.resume !== void 0 ? { pr: entry.resume.pr } : {},
|
|
55246
|
+
verbosity: ctx.verbosity,
|
|
55009
55247
|
profile
|
|
55010
55248
|
},
|
|
55011
55249
|
card: card.handle ?? null,
|
|
@@ -55098,6 +55336,7 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
55098
55336
|
entry,
|
|
55099
55337
|
requestText: directives.text,
|
|
55100
55338
|
...ctx.reissuePlanId !== void 0 ? { reissuePlanId: ctx.reissuePlanId } : {},
|
|
55339
|
+
...ctx.beforeCoordinatorStart !== void 0 ? { beforeStart: ctx.beforeCoordinatorStart } : {},
|
|
55101
55340
|
msg,
|
|
55102
55341
|
agentSource: ctx.agentSource,
|
|
55103
55342
|
runId: run2.id,
|
|
@@ -55349,7 +55588,13 @@ function carriedCoordinatorTag(row2, events) {
|
|
|
55349
55588
|
if (typeof parentInstanceId !== "string" || typeof idempotencyKey !== "string") return void 0;
|
|
55350
55589
|
const tag = events.find((e) => e.type === "coordinator_tag");
|
|
55351
55590
|
const base = tag?.type === "coordinator_tag" ? tag.base : void 0;
|
|
55352
|
-
|
|
55591
|
+
const publication = tag?.type === "coordinator_tag" ? tag.publication : void 0;
|
|
55592
|
+
return {
|
|
55593
|
+
parentInstanceId,
|
|
55594
|
+
idempotencyKey,
|
|
55595
|
+
...base !== void 0 ? { base } : {},
|
|
55596
|
+
...publication !== void 0 ? { publication } : {}
|
|
55597
|
+
};
|
|
55353
55598
|
}
|
|
55354
55599
|
function lostWorkspaceNote(why, restarts) {
|
|
55355
55600
|
const lost = `resumed after a restart: the run's workspace could not be re-attached (${why})`;
|
|
@@ -55885,6 +56130,29 @@ function continuationBudgetOf(instance, unit) {
|
|
|
55885
56130
|
if (remainingRounds <= 0) return { kind: "exhausted", budget: "review-round" };
|
|
55886
56131
|
return { kind: "remaining", caps: { maxRounds: remainingRounds, maxMinutes: remainingMinutes } };
|
|
55887
56132
|
}
|
|
56133
|
+
async function legacyReviewedHeadOf(runs, instance, unit, prNumber) {
|
|
56134
|
+
const listed = await runs.listUnitRuns(unitKeyOf({ instanceId: instance.id, unit: unit.unit }), { kind: "all" }).catch(() => void 0);
|
|
56135
|
+
if (listed === void 0 || !listed.ok || listed.value.unit !== unitKeyOf({ instanceId: instance.id, unit: unit.unit }) || listed.value.instanceId !== instance.id || listed.value.id !== unit.unit || listed.value.instance.id !== instance.id || listed.value.instance.repo.toLowerCase() !== instance.repo.toLowerCase())
|
|
56136
|
+
return void 0;
|
|
56137
|
+
const prefix = `${instance.id}:${unit.unit}/`;
|
|
56138
|
+
const owned = listed.value.runs.filter(
|
|
56139
|
+
(run2) => run2.finished && run2.status === "completed" && run2.parentInstanceId === instance.id && run2.idempotencyKey?.startsWith(prefix) === true && run2.userId === instance.userId && run2.repo?.toLowerCase() === instance.repo.toLowerCase()
|
|
56140
|
+
);
|
|
56141
|
+
const coding = new Set(
|
|
56142
|
+
owned.flatMap(
|
|
56143
|
+
(run2) => run2.agent === "coding" && run2.pr?.number === prNumber && typeof run2.headSha === "string" && FULL_SHA2.test(run2.headSha) ? [run2.headSha.toLowerCase()] : []
|
|
56144
|
+
)
|
|
56145
|
+
);
|
|
56146
|
+
const approved = new Set(
|
|
56147
|
+
owned.flatMap((run2) => {
|
|
56148
|
+
const head = run2.reviewHead;
|
|
56149
|
+
const post = run2.reviewPost;
|
|
56150
|
+
return run2.agent === "review" && typeof head === "string" && FULL_SHA2.test(head) && run2.verdict?.verdict === "approve" && run2.verdict.head?.toLowerCase() === head.toLowerCase() && post?.posted === true && post.target.repo.toLowerCase() === instance.repo.toLowerCase() && post.target.number === prNumber && post.head.toLowerCase() === head.toLowerCase() && post.verdict === "approve" ? [head.toLowerCase()] : [];
|
|
56151
|
+
})
|
|
56152
|
+
);
|
|
56153
|
+
const exact = [...approved].filter((head) => coding.has(head));
|
|
56154
|
+
return exact.length === 1 ? exact[0] : void 0;
|
|
56155
|
+
}
|
|
55888
56156
|
async function dispatch(deps, msg, io, opts = {}) {
|
|
55889
56157
|
const ended = { status: "completed" };
|
|
55890
56158
|
const resume = opts.resume;
|
|
@@ -55969,6 +56237,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55969
56237
|
let stoppedWhileAttaching;
|
|
55970
56238
|
let resumeRowClosed = false;
|
|
55971
56239
|
let restartRequest;
|
|
56240
|
+
let releaseLegacyOwnership;
|
|
55972
56241
|
const reservationHooks = {
|
|
55973
56242
|
onStop: (mode) => void registered?.control.requestStop(mode),
|
|
55974
56243
|
onFenced: () => {
|
|
@@ -55987,6 +56256,8 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55987
56256
|
let operatorRepo;
|
|
55988
56257
|
let reissuePlanId;
|
|
55989
56258
|
let reissueCaps;
|
|
56259
|
+
let beforeCoordinatorStart;
|
|
56260
|
+
let reserveLegacyOwnership;
|
|
55990
56261
|
let operatorThread;
|
|
55991
56262
|
let pageOwner;
|
|
55992
56263
|
if (operatorMode !== "off") {
|
|
@@ -56204,7 +56475,19 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
56204
56475
|
await recordPendingOperator();
|
|
56205
56476
|
return ended;
|
|
56206
56477
|
}
|
|
56207
|
-
|
|
56478
|
+
let expectedHead = owner.unit.lastPush;
|
|
56479
|
+
const recoverLegacyBinding = owner.unit.ending?.kind === "merge_ready" && (expectedHead === void 0 || owner.unit.publication === void 0);
|
|
56480
|
+
if (recoverLegacyBinding) {
|
|
56481
|
+
const recovered = await legacyReviewedHeadOf(runsService, instance, owner.unit, recordedPr.number);
|
|
56482
|
+
if (recovered === void 0 || expectedHead !== void 0 && expectedHead.toLowerCase() !== recovered) {
|
|
56483
|
+
await io.reply(
|
|
56484
|
+
`Unit ${owner.unit.unit} has no single exact reviewed head in its durable child records, so continuation did not start. Nothing else ran.`
|
|
56485
|
+
);
|
|
56486
|
+
await recordPendingOperator();
|
|
56487
|
+
return ended;
|
|
56488
|
+
}
|
|
56489
|
+
expectedHead = recovered;
|
|
56490
|
+
}
|
|
56208
56491
|
if (expectedHead === void 0) {
|
|
56209
56492
|
await io.reply(
|
|
56210
56493
|
`Unit ${owner.unit.unit} does not retain the pull request's durable expected head, so continuation did not start. Nothing else ran.`
|
|
@@ -56243,6 +56526,140 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
56243
56526
|
await recordPendingOperator();
|
|
56244
56527
|
return ended;
|
|
56245
56528
|
}
|
|
56529
|
+
if (recoverLegacyBinding) {
|
|
56530
|
+
const baseRef = instance.base;
|
|
56531
|
+
if (baseRef === void 0 || facts2.baseRef !== baseRef) {
|
|
56532
|
+
await io.reply(
|
|
56533
|
+
`${instance.repo}#${recordedPr.number} no longer has the pipeline's verifiable base ref, so continuation did not start. Nothing else ran.`
|
|
56534
|
+
);
|
|
56535
|
+
await recordPendingOperator();
|
|
56536
|
+
return ended;
|
|
56537
|
+
}
|
|
56538
|
+
const existingBinding = owner.unit.publication;
|
|
56539
|
+
if (existingBinding !== void 0 && (existingBinding.repo.toLowerCase() !== instance.repo.toLowerCase() || existingBinding.pr !== recordedPr.number || existingBinding.headRef !== owner.unit.branch || existingBinding.baseRef !== baseRef || existingBinding.expectedHeadSha.toLowerCase() !== expectedHead || existingBinding.publicationRef !== owner.unit.branch || existingBinding.owner.instanceId !== owner.instanceId || existingBinding.owner.unit !== owner.unit.unit)) {
|
|
56540
|
+
await io.reply(
|
|
56541
|
+
`Unit ${owner.unit.unit}'s durable publication binding no longer matches this pipeline, so continuation did not start. Nothing else ran.`
|
|
56542
|
+
);
|
|
56543
|
+
await recordPendingOperator();
|
|
56544
|
+
return ended;
|
|
56545
|
+
}
|
|
56546
|
+
let currentOwner;
|
|
56547
|
+
const runnerOwnership = deps.runnerOwnership;
|
|
56548
|
+
try {
|
|
56549
|
+
if (runnerOwnership === void 0) throw new Error("runner ownership is unavailable");
|
|
56550
|
+
currentOwner = runnerOwnership.owner(instance.repo, recordedPr.number);
|
|
56551
|
+
} catch {
|
|
56552
|
+
await io.reply(
|
|
56553
|
+
`Runner ownership for ${instance.repo}#${recordedPr.number} could not be verified, so continuation did not start. Nothing else ran.`
|
|
56554
|
+
);
|
|
56555
|
+
await recordPendingOperator();
|
|
56556
|
+
return ended;
|
|
56557
|
+
}
|
|
56558
|
+
if (currentOwner !== void 0 && (currentOwner.instanceId !== owner.instanceId || currentOwner.unit !== owner.unit.unit)) {
|
|
56559
|
+
await io.reply(
|
|
56560
|
+
`${instance.repo}#${recordedPr.number} is owned by another runner, so continuation of ${owner.instanceId}:${owner.unit.unit} did not start. Nothing else ran.`
|
|
56561
|
+
);
|
|
56562
|
+
await recordPendingOperator();
|
|
56563
|
+
return ended;
|
|
56564
|
+
}
|
|
56565
|
+
const legacyUnit = owner.unit;
|
|
56566
|
+
const coordinatorInstances = deps.coordinatorInstances;
|
|
56567
|
+
const reservationOwner = { instanceId: owner.instanceId, unit: owner.unit.unit };
|
|
56568
|
+
const recoveredUnit = {
|
|
56569
|
+
...legacyUnit,
|
|
56570
|
+
lastPush: expectedHead,
|
|
56571
|
+
publication: {
|
|
56572
|
+
repo: instance.repo,
|
|
56573
|
+
pr: recordedPr.number,
|
|
56574
|
+
headRef: legacyUnit.branch,
|
|
56575
|
+
baseRef,
|
|
56576
|
+
expectedHeadSha: expectedHead,
|
|
56577
|
+
publicationRef: legacyUnit.branch,
|
|
56578
|
+
owner: reservationOwner
|
|
56579
|
+
}
|
|
56580
|
+
};
|
|
56581
|
+
let token2;
|
|
56582
|
+
let transferredOwner;
|
|
56583
|
+
releaseLegacyOwnership = () => {
|
|
56584
|
+
if (token2 !== void 0) {
|
|
56585
|
+
const released = runnerOwnership.releaseReservation(instance.repo, recordedPr.number, token2);
|
|
56586
|
+
if (released) token2 = void 0;
|
|
56587
|
+
return released;
|
|
56588
|
+
}
|
|
56589
|
+
if (transferredOwner !== void 0) {
|
|
56590
|
+
const released = runnerOwnership.release(instance.repo, recordedPr.number, transferredOwner);
|
|
56591
|
+
if (released) transferredOwner = void 0;
|
|
56592
|
+
return released;
|
|
56593
|
+
}
|
|
56594
|
+
return true;
|
|
56595
|
+
};
|
|
56596
|
+
reserveLegacyOwnership = () => {
|
|
56597
|
+
token2 = runnerOwnership.reserve(instance.repo, recordedPr.number, reservationOwner);
|
|
56598
|
+
if (token2 !== void 0) return void 0;
|
|
56599
|
+
const competing = runnerOwnership.owner(instance.repo, recordedPr.number);
|
|
56600
|
+
const sameContinuation = competing?.instanceId === reservationOwner.instanceId && competing.unit === reservationOwner.unit;
|
|
56601
|
+
return refusalOf(
|
|
56602
|
+
"setup_failed",
|
|
56603
|
+
sameContinuation ? `Unit ${legacyUnit.unit} changed while its legacy continuation was being reserved, so continuation did not start. Nothing else ran.` : `${instance.repo}#${recordedPr.number} is owned by another runner, so continuation of ${owner.instanceId}:${legacyUnit.unit} did not start. Nothing else ran.`
|
|
56604
|
+
);
|
|
56605
|
+
};
|
|
56606
|
+
beforeCoordinatorStart = async () => {
|
|
56607
|
+
const activeToken = token2;
|
|
56608
|
+
if (activeToken === void 0)
|
|
56609
|
+
return {
|
|
56610
|
+
ok: false,
|
|
56611
|
+
refusal: refusalOf(
|
|
56612
|
+
"setup_failed",
|
|
56613
|
+
`Unit ${legacyUnit.unit}'s ownership reservation was lost, so continuation did not start. Nothing else ran.`
|
|
56614
|
+
)
|
|
56615
|
+
};
|
|
56616
|
+
const persisted = await coordinatorInstances.claimLegacyContinuation(legacyUnit, recoveredUnit).catch(() => void 0);
|
|
56617
|
+
if (persisted?.ok !== true) {
|
|
56618
|
+
const released = releaseLegacyOwnership?.() ?? true;
|
|
56619
|
+
const reason2 = persisted?.reason === "stale" ? `Unit ${legacyUnit.unit} changed while its legacy continuation was being reserved, so continuation did not start.` : `Unit ${legacyUnit.unit}'s recovered continuation binding could not be stored, so continuation did not start.`;
|
|
56620
|
+
const text = released ? `${reason2} Nothing else ran.` : `${reason2} Its ownership reservation could not be released. Nothing else ran.`;
|
|
56621
|
+
return { ok: false, refusal: refusalOf("setup_failed", text) };
|
|
56622
|
+
}
|
|
56623
|
+
const reservedOwner = runnerOwnership.owner(instance.repo, recordedPr.number);
|
|
56624
|
+
if (reservedOwner?.instanceId !== reservationOwner.instanceId || reservedOwner.unit !== reservationOwner.unit) {
|
|
56625
|
+
const restored = await coordinatorInstances.claimLegacyContinuation(recoveredUnit, legacyUnit).catch(() => void 0);
|
|
56626
|
+
const released = releaseLegacyOwnership?.() ?? true;
|
|
56627
|
+
const rollbackFailure = restored?.ok === true ? void 0 : restored?.reason ?? "unavailable";
|
|
56628
|
+
const cleanupFailure = released ? "" : " and its ownership reservation could not be released";
|
|
56629
|
+
const text = rollbackFailure === void 0 ? `${instance.repo}#${recordedPr.number} changed runner ownership while its legacy continuation was being reserved${cleanupFailure}, so continuation did not start. Nothing else ran.` : `${instance.repo}#${recordedPr.number} changed runner ownership while its legacy continuation was being reserved, and the exact legacy row could not be restored (${rollbackFailure})${cleanupFailure}; continuation did not start. Nothing else ran.`;
|
|
56630
|
+
return { ok: false, refusal: refusalOf("setup_failed", text) };
|
|
56631
|
+
}
|
|
56632
|
+
owner.unit = recoveredUnit;
|
|
56633
|
+
return {
|
|
56634
|
+
ok: true,
|
|
56635
|
+
commit: async (nextOwner) => {
|
|
56636
|
+
if (!runnerOwnership.transferReservation(
|
|
56637
|
+
instance.repo,
|
|
56638
|
+
recordedPr.number,
|
|
56639
|
+
activeToken,
|
|
56640
|
+
reservationOwner,
|
|
56641
|
+
nextOwner
|
|
56642
|
+
))
|
|
56643
|
+
throw new Error("legacy continuation ownership reservation or current owner changed");
|
|
56644
|
+
token2 = void 0;
|
|
56645
|
+
transferredOwner = nextOwner;
|
|
56646
|
+
},
|
|
56647
|
+
complete: () => {
|
|
56648
|
+
transferredOwner = void 0;
|
|
56649
|
+
},
|
|
56650
|
+
abort: async () => {
|
|
56651
|
+
const restored = await coordinatorInstances.claimLegacyContinuation(recoveredUnit, legacyUnit).catch(() => void 0);
|
|
56652
|
+
const released = releaseLegacyOwnership?.() ?? true;
|
|
56653
|
+
owner.unit = legacyUnit;
|
|
56654
|
+
const failures = [
|
|
56655
|
+
...restored?.ok === true ? [] : [`legacy continuation rollback failed (${restored?.reason ?? "unavailable"})`],
|
|
56656
|
+
...released ? [] : ["legacy continuation ownership cleanup failed"]
|
|
56657
|
+
];
|
|
56658
|
+
if (failures.length > 0) throw new Error(failures.join("; "));
|
|
56659
|
+
}
|
|
56660
|
+
};
|
|
56661
|
+
};
|
|
56662
|
+
}
|
|
56246
56663
|
reissuePlanId = instance.plan.id;
|
|
56247
56664
|
reissueCaps = continuationBudget.caps;
|
|
56248
56665
|
const durableRequest = parseDirectives(original);
|
|
@@ -56295,6 +56712,12 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
56295
56712
|
});
|
|
56296
56713
|
if (profileGate.kind === "refused") return ended;
|
|
56297
56714
|
const { profile } = profileGate;
|
|
56715
|
+
const legacyOwnershipRefusal = reserveLegacyOwnership?.();
|
|
56716
|
+
if (legacyOwnershipRefusal !== void 0) {
|
|
56717
|
+
await io.reply(legacyOwnershipRefusal.text);
|
|
56718
|
+
await recordPendingOperator();
|
|
56719
|
+
return ended;
|
|
56720
|
+
}
|
|
56298
56721
|
const earlyRepoTarget = agent.name === "review" ? resolveRepoTarget(deps, {
|
|
56299
56722
|
msg,
|
|
56300
56723
|
history,
|
|
@@ -56473,6 +56896,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
56473
56896
|
card,
|
|
56474
56897
|
directives,
|
|
56475
56898
|
...reissuePlanId !== void 0 ? { reissuePlanId } : {},
|
|
56899
|
+
...beforeCoordinatorStart !== void 0 ? { beforeCoordinatorStart } : {},
|
|
56476
56900
|
...reissueCaps !== void 0 ? { reissueCaps: { ...reissueCaps, maxMinutes: Math.min(reissueCaps.maxMinutes, profile.minutes) } } : {},
|
|
56477
56901
|
sticky,
|
|
56478
56902
|
history,
|
|
@@ -57185,6 +57609,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
57185
57609
|
});
|
|
57186
57610
|
} finally {
|
|
57187
57611
|
clearInterval(setupHeartbeat);
|
|
57612
|
+
releaseLegacyOwnership?.();
|
|
57188
57613
|
ending.drain(void 0);
|
|
57189
57614
|
if (registered) deps.runBearers?.revoke(registered.id);
|
|
57190
57615
|
if (resume && ledgerRun && !runLoopStarted && !resumeRowClosed) {
|
|
@@ -57372,7 +57797,7 @@ async function dispatchClick(deps, click) {
|
|
|
57372
57797
|
activeRuns--;
|
|
57373
57798
|
}
|
|
57374
57799
|
}
|
|
57375
|
-
var activeRuns;
|
|
57800
|
+
var activeRuns, FULL_SHA2;
|
|
57376
57801
|
var init_dispatcher = __esm({
|
|
57377
57802
|
"../../src/core/dispatcher.ts"() {
|
|
57378
57803
|
"use strict";
|
|
@@ -57438,6 +57863,7 @@ var init_dispatcher = __esm({
|
|
|
57438
57863
|
init_types5();
|
|
57439
57864
|
init_decisionRecordReservation();
|
|
57440
57865
|
activeRuns = 0;
|
|
57866
|
+
FULL_SHA2 = /^[0-9a-f]{40}$/i;
|
|
57441
57867
|
}
|
|
57442
57868
|
});
|
|
57443
57869
|
|
|
@@ -65712,7 +66138,8 @@ async function handle(deps, { client, statusClient }, ev, intake) {
|
|
|
65712
66138
|
span,
|
|
65713
66139
|
{
|
|
65714
66140
|
staging: deps.artifacts !== void 0,
|
|
65715
|
-
maxBytesPerMessage: deps.config.config.artifacts?.inbound?.maxBytesPerMessage ?? ARTIFACT_DEFAULTS.maxBytesPerMessage
|
|
66141
|
+
maxBytesPerMessage: deps.config.config.artifacts?.inbound?.maxBytesPerMessage ?? ARTIFACT_DEFAULTS.maxBytesPerMessage,
|
|
66142
|
+
verbosityFor: (channelId, userId, request2) => deps.config.verbosityFor(channelId, userId, request2)
|
|
65716
66143
|
},
|
|
65717
66144
|
relayAppsOf(deps),
|
|
65718
66145
|
intake
|
|
@@ -65794,10 +66221,25 @@ async function receiveSlackMessage(client, ev, span, policy, relayApps, intake)
|
|
|
65794
66221
|
threadRuns = gated.thread;
|
|
65795
66222
|
}
|
|
65796
66223
|
}
|
|
65797
|
-
|
|
65798
|
-
|
|
65799
|
-
|
|
65800
|
-
|
|
66224
|
+
const requester = await resolveSlackRequester(
|
|
66225
|
+
client,
|
|
66226
|
+
{
|
|
66227
|
+
channel: ev.channel,
|
|
66228
|
+
ts: ev.ts,
|
|
66229
|
+
threadTs: ev.threadTs,
|
|
66230
|
+
...ev.user !== void 0 ? { user: ev.user } : {},
|
|
66231
|
+
text: ev.rawText ?? ev.text,
|
|
66232
|
+
...ev.poster !== void 0 ? { poster: ev.poster } : {}
|
|
66233
|
+
},
|
|
66234
|
+
relayApps
|
|
66235
|
+
);
|
|
66236
|
+
span.setAttrs({ requester: requester.resolvedBy });
|
|
66237
|
+
const verbosity2 = policy.verbosityFor?.(`${PLATFORM3}:${ev.channel}`, requester.userId, parseDirectives(ev.text).verbosity) ?? policy.verbosity ?? DEFAULT_VERBOSITY;
|
|
66238
|
+
if (shows(verbosity2, "verbose"))
|
|
66239
|
+
client.reactions.add({ channel: ev.channel, timestamp: ev.ts, name: ACK_EMOJI }).catch((err2) => {
|
|
66240
|
+
if (!err2.message.includes("already_reacted")) console.error(`[ack] ${err2.message}`);
|
|
66241
|
+
});
|
|
66242
|
+
const delayNote = ev.caughtUp && shows(verbosity2, "verbose") ? new SlackIO(client, ev).reply(catchUpDelayNote(ev.ts, systemClock())).catch((err2) => {
|
|
65801
66243
|
console.error(`[catch-up] ${ev.channel}:${ev.ts} delay note failed: ${err2.message}`);
|
|
65802
66244
|
}) : Promise.resolve();
|
|
65803
66245
|
const [imagePass, documentPass] = await Promise.all([
|
|
@@ -65820,19 +66262,6 @@ async function receiveSlackMessage(client, ev, span, policy, relayApps, intake)
|
|
|
65820
66262
|
});
|
|
65821
66263
|
const unsupported = imagePass.skippedFiles.filter((f) => documentPass.skippedFiles.includes(f));
|
|
65822
66264
|
const { staged, skipped } = stagedFiles(unsupported, { ...policy, messageId: ev.ts });
|
|
65823
|
-
const requester = await resolveSlackRequester(
|
|
65824
|
-
client,
|
|
65825
|
-
{
|
|
65826
|
-
channel: ev.channel,
|
|
65827
|
-
ts: ev.ts,
|
|
65828
|
-
threadTs: ev.threadTs,
|
|
65829
|
-
...ev.user !== void 0 ? { user: ev.user } : {},
|
|
65830
|
-
text: ev.rawText ?? ev.text,
|
|
65831
|
-
...ev.poster !== void 0 ? { poster: ev.poster } : {}
|
|
65832
|
-
},
|
|
65833
|
-
relayApps
|
|
65834
|
-
);
|
|
65835
|
-
span.setAttrs({ requester: requester.resolvedBy });
|
|
65836
66265
|
const [channelName, userName, team] = await Promise.all([
|
|
65837
66266
|
resolveChannelName(client, ev.channel),
|
|
65838
66267
|
requester.slackUserId !== void 0 ? resolveUserName(client, requester.slackUserId) : Promise.resolve(requester.userName),
|
|
@@ -66152,6 +66581,8 @@ var init_slack = __esm({
|
|
|
66152
66581
|
init_intake();
|
|
66153
66582
|
init_thread();
|
|
66154
66583
|
init_operator();
|
|
66584
|
+
init_directives();
|
|
66585
|
+
init_verbosity();
|
|
66155
66586
|
init_confirmations();
|
|
66156
66587
|
init_threadTurns();
|
|
66157
66588
|
init_statusBudget();
|
|
@@ -66337,14 +66768,15 @@ ${file.text}`));
|
|
|
66337
66768
|
io
|
|
66338
66769
|
};
|
|
66339
66770
|
}
|
|
66340
|
-
async status(initial) {
|
|
66771
|
+
async status(initial, display = {}) {
|
|
66341
66772
|
const statusClient = this.opts.statusClient ?? this.client;
|
|
66342
66773
|
const budget = this.opts.statusBudget ?? processStatusBudget;
|
|
66343
|
-
const
|
|
66774
|
+
const shimmerVisible = shows(display.verbosity ?? DEFAULT_VERBOSITY, "verbose");
|
|
66775
|
+
const setShimmer = (status3 = LOADING_PHRASES[0]) => statusClient.assistant.threads.setStatus({
|
|
66344
66776
|
channel_id: this.ev.channel,
|
|
66345
66777
|
thread_ts: this.ev.threadTs,
|
|
66346
|
-
status:
|
|
66347
|
-
loading_messages: LOADING_PHRASES
|
|
66778
|
+
status: status3,
|
|
66779
|
+
...status3 === "" ? {} : { loading_messages: LOADING_PHRASES }
|
|
66348
66780
|
}).catch((err2) => console.error(`[shimmer] ${err2.message}`));
|
|
66349
66781
|
let ts;
|
|
66350
66782
|
if (this.opts.existingCard) {
|
|
@@ -66369,11 +66801,17 @@ ${file.text}`));
|
|
|
66369
66801
|
liveCards.add(liveCardKey(this.ev.channel, ts));
|
|
66370
66802
|
const card = `${this.ev.channel}:${ts}`;
|
|
66371
66803
|
const shimmerKey = `${this.ev.channel}:${this.ev.threadTs}`;
|
|
66372
|
-
|
|
66373
|
-
|
|
66374
|
-
|
|
66375
|
-
|
|
66376
|
-
|
|
66804
|
+
let shimmerTimer;
|
|
66805
|
+
if (shimmerVisible) {
|
|
66806
|
+
shimmerOwners.set(shimmerKey, card);
|
|
66807
|
+
await setShimmer();
|
|
66808
|
+
shimmerTimer = setInterval(() => {
|
|
66809
|
+
if (shimmerOwners.get(shimmerKey) === card) void setShimmer();
|
|
66810
|
+
}, 75e3);
|
|
66811
|
+
} else {
|
|
66812
|
+
shimmerOwners.delete(shimmerKey);
|
|
66813
|
+
await setShimmer("");
|
|
66814
|
+
}
|
|
66377
66815
|
budget.open(card);
|
|
66378
66816
|
const edit = (frame2) => statusClient.chat.update({ channel: this.ev.channel, ts, ...render2(frame2) });
|
|
66379
66817
|
let dropped = 0;
|
|
@@ -66402,7 +66840,7 @@ ${file.text}`));
|
|
|
66402
66840
|
});
|
|
66403
66841
|
},
|
|
66404
66842
|
done: async (frame2) => {
|
|
66405
|
-
clearInterval(shimmerTimer);
|
|
66843
|
+
if (shimmerTimer !== void 0) clearInterval(shimmerTimer);
|
|
66406
66844
|
liveCards.delete(liveCardKey(this.ev.channel, ts));
|
|
66407
66845
|
budget.close(card);
|
|
66408
66846
|
const waitMs = budget.takeTerminal(this.ev.channel);
|
|
@@ -66414,7 +66852,7 @@ ${file.text}`));
|
|
|
66414
66852
|
if (dropped > 0) console.log(`[slack] card ${card}: ${dropped} progress frames dropped by the status budget`);
|
|
66415
66853
|
if (shimmerOwners.get(shimmerKey) !== card) return;
|
|
66416
66854
|
shimmerOwners.delete(shimmerKey);
|
|
66417
|
-
await
|
|
66855
|
+
await setShimmer("").catch(() => {
|
|
66418
66856
|
});
|
|
66419
66857
|
}
|
|
66420
66858
|
};
|
|
@@ -71187,7 +71625,12 @@ async function launchResumes(deps, resumable, opts) {
|
|
|
71187
71625
|
startedAt: row2.startedAt,
|
|
71188
71626
|
now: deps.clock ?? Date.now
|
|
71189
71627
|
});
|
|
71190
|
-
|
|
71628
|
+
const verbosity2 = row2.meta.verbosity ?? deps.config.verbosityFor(
|
|
71629
|
+
row2.meta.channelId,
|
|
71630
|
+
row2.meta.userId,
|
|
71631
|
+
parseDirectives(inputTextOf(run2.events)).verbosity
|
|
71632
|
+
);
|
|
71633
|
+
await io2.status(shell.live({ detail: [rehostCardDetail(run2)] }), { verbosity: verbosity2 });
|
|
71191
71634
|
} catch (err2) {
|
|
71192
71635
|
warn(
|
|
71193
71636
|
`[resume] ${row2.runId} ${row2.threadKey}: restart card could not be written: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
@@ -71266,6 +71709,7 @@ var PI_READ_ONLY_TOOLS;
|
|
|
71266
71709
|
var init_resumeLaunch = __esm({
|
|
71267
71710
|
"../../src/core/resumeLaunch.ts"() {
|
|
71268
71711
|
"use strict";
|
|
71712
|
+
init_directives();
|
|
71269
71713
|
init_inboxMessage();
|
|
71270
71714
|
init_resume();
|
|
71271
71715
|
init_runRegistry();
|
|
@@ -71300,22 +71744,73 @@ var init_runnerOwnership = __esm({
|
|
|
71300
71744
|
recovered = /* @__PURE__ */ new Set();
|
|
71301
71745
|
claimedOwners = /* @__PURE__ */ new Map();
|
|
71302
71746
|
recoveredOwners = /* @__PURE__ */ new Map();
|
|
71747
|
+
reservations = /* @__PURE__ */ new Map();
|
|
71303
71748
|
localRecoveredInstances = /* @__PURE__ */ new Set();
|
|
71304
71749
|
recoveryComplete;
|
|
71305
71750
|
constructor(requiresRecovery) {
|
|
71306
71751
|
this.recoveryComplete = !requiresRecovery;
|
|
71307
71752
|
}
|
|
71753
|
+
/** Claim without displacing a different live runner. The read and write are
|
|
71754
|
+
* synchronous, so a caller owns the process-local fence before its next
|
|
71755
|
+
* awaited durable transition. Repeating the same owner's claim is idempotent. */
|
|
71308
71756
|
claim(repo, prNumber, owner) {
|
|
71309
71757
|
const key = runnerOwnedPullKey(repo, prNumber);
|
|
71758
|
+
const current = this.claimedOwners.get(key) ?? this.recoveredOwners.get(key);
|
|
71759
|
+
const alreadyOwned = this.claimed.has(key) || this.recovered.has(key);
|
|
71760
|
+
if (this.reservations.has(key)) return false;
|
|
71761
|
+
if (alreadyOwned && (owner === void 0 || current === void 0 || current.instanceId !== owner.instanceId || current.unit !== owner.unit))
|
|
71762
|
+
return false;
|
|
71310
71763
|
this.claimed.add(key);
|
|
71311
71764
|
if (owner !== void 0) this.claimedOwners.set(key, owner);
|
|
71765
|
+
return true;
|
|
71312
71766
|
}
|
|
71313
|
-
|
|
71767
|
+
/** Exclusively reserve an unowned pull request across an awaited durable
|
|
71768
|
+
* transition. Even the same runner identity cannot reserve it twice: the
|
|
71769
|
+
* returned token, not the owner fields, identifies the one caller. */
|
|
71770
|
+
reserve(repo, prNumber, owner) {
|
|
71771
|
+
const key = runnerOwnedPullKey(repo, prNumber);
|
|
71772
|
+
if (this.claimed.has(key) || this.recovered.has(key) || this.reservations.has(key)) return void 0;
|
|
71773
|
+
const token2 = Symbol(key);
|
|
71774
|
+
this.reservations.set(key, token2);
|
|
71775
|
+
this.claimed.add(key);
|
|
71776
|
+
this.claimedOwners.set(key, owner);
|
|
71777
|
+
return token2;
|
|
71778
|
+
}
|
|
71779
|
+
/** Move an exclusive recovery reservation to the durable attempt that is
|
|
71780
|
+
* about to start. Both the unforgeable token and the currently recorded
|
|
71781
|
+
* owner must still match; a stale caller can neither displace nor adopt a
|
|
71782
|
+
* successor. The transfer consumes the reservation but retains ownership. */
|
|
71783
|
+
transferReservation(repo, prNumber, token2, currentOwner, nextOwner) {
|
|
71314
71784
|
const key = runnerOwnedPullKey(repo, prNumber);
|
|
71785
|
+
const current = this.claimedOwners.get(key);
|
|
71786
|
+
if (this.reservations.get(key) !== token2 || !this.claimed.has(key) || current?.instanceId !== currentOwner.instanceId || current.unit !== currentOwner.unit)
|
|
71787
|
+
return false;
|
|
71788
|
+
this.reservations.delete(key);
|
|
71789
|
+
this.claimedOwners.set(key, nextOwner);
|
|
71790
|
+
return true;
|
|
71791
|
+
}
|
|
71792
|
+
releaseReservation(repo, prNumber, token2) {
|
|
71793
|
+
const key = runnerOwnedPullKey(repo, prNumber);
|
|
71794
|
+
if (this.reservations.get(key) !== token2) return false;
|
|
71795
|
+
this.reservations.delete(key);
|
|
71796
|
+
this.claimed.delete(key);
|
|
71797
|
+
this.claimedOwners.delete(key);
|
|
71798
|
+
return true;
|
|
71799
|
+
}
|
|
71800
|
+
/** Release unconditionally for the runner's ordinary end, or conditionally
|
|
71801
|
+
* for a provisional claim whose cleanup must not erase a successor. */
|
|
71802
|
+
release(repo, prNumber, owner) {
|
|
71803
|
+
const key = runnerOwnedPullKey(repo, prNumber);
|
|
71804
|
+
if (owner !== void 0) {
|
|
71805
|
+
const current = this.claimedOwners.get(key) ?? this.recoveredOwners.get(key);
|
|
71806
|
+
if (current?.instanceId !== owner.instanceId || current.unit !== owner.unit) return false;
|
|
71807
|
+
}
|
|
71808
|
+
this.reservations.delete(key);
|
|
71315
71809
|
this.claimed.delete(key);
|
|
71316
71810
|
this.recovered.delete(key);
|
|
71317
71811
|
this.claimedOwners.delete(key);
|
|
71318
71812
|
this.recoveredOwners.delete(key);
|
|
71813
|
+
return true;
|
|
71319
71814
|
}
|
|
71320
71815
|
owns(repo, prNumber) {
|
|
71321
71816
|
if (!this.recoveryComplete) throw new Error("runner ownership recovery is still in progress");
|
|
@@ -72242,7 +72737,7 @@ function watched(io, on) {
|
|
|
72242
72737
|
on.replied(text);
|
|
72243
72738
|
await io.reply(text);
|
|
72244
72739
|
},
|
|
72245
|
-
status: (initial) => io.status(initial),
|
|
72740
|
+
status: (initial, display) => io.status(initial, display),
|
|
72246
72741
|
history: () => io.history(),
|
|
72247
72742
|
runStarted: (started) => {
|
|
72248
72743
|
io.runStarted?.(started);
|
|
@@ -72316,6 +72811,33 @@ The thread since the last step:
|
|
|
72316
72811
|
${foldThreadEvents(folded)}` };
|
|
72317
72812
|
}
|
|
72318
72813
|
const carried = foldThreadAttachments(folded);
|
|
72814
|
+
const existingPrCodingTarget = req.preset === "coding" && row2 !== void 0 && (row2.publication !== void 0 || row2.pr !== void 0 || row2.resume !== void 0 || parsePlanBranch(row2.branch) === void 0);
|
|
72815
|
+
if (existingPrCodingTarget) {
|
|
72816
|
+
if (row2.publication === void 0)
|
|
72817
|
+
return json3(409, {
|
|
72818
|
+
ok: false,
|
|
72819
|
+
error: "publication_binding_missing",
|
|
72820
|
+
message: "the existing pull request has no durable publication binding",
|
|
72821
|
+
at
|
|
72822
|
+
});
|
|
72823
|
+
try {
|
|
72824
|
+
const owner = deps.runnerOwnership?.owner(instance.repo, row2.publication.pr);
|
|
72825
|
+
if (owner === void 0 || owner.instanceId !== row2.publication.owner.instanceId || owner.unit !== row2.publication.owner.unit)
|
|
72826
|
+
return json3(409, {
|
|
72827
|
+
ok: false,
|
|
72828
|
+
error: "publication_ownership_changed",
|
|
72829
|
+
message: "the durable existing-PR publication owner is no longer the runner's sole owner",
|
|
72830
|
+
at
|
|
72831
|
+
});
|
|
72832
|
+
} catch (err2) {
|
|
72833
|
+
return json3(409, {
|
|
72834
|
+
ok: false,
|
|
72835
|
+
error: "publication_ownership_unknown",
|
|
72836
|
+
message: describe12(err2),
|
|
72837
|
+
at
|
|
72838
|
+
});
|
|
72839
|
+
}
|
|
72840
|
+
}
|
|
72319
72841
|
const msg = {
|
|
72320
72842
|
channelId: instance.channelId,
|
|
72321
72843
|
userId: instance.userId,
|
|
@@ -72357,10 +72879,16 @@ ${foldThreadEvents(folded)}` };
|
|
|
72357
72879
|
lastReply = text;
|
|
72358
72880
|
}
|
|
72359
72881
|
});
|
|
72882
|
+
const roundExpectedHead = req.brief?.kind === "findings" || req.brief?.kind === "rebase" ? req.brief.headSha : req.brief?.kind === "contract" ? req.brief.continue?.from : void 0;
|
|
72883
|
+
const publication = req.preset === "coding" && row2?.publication !== void 0 ? {
|
|
72884
|
+
...row2.publication,
|
|
72885
|
+
...roundExpectedHead !== void 0 ? { expectedHeadSha: roundExpectedHead } : {}
|
|
72886
|
+
} : void 0;
|
|
72360
72887
|
const tag = {
|
|
72361
72888
|
parentInstanceId: instance.id,
|
|
72362
72889
|
idempotencyKey: key,
|
|
72363
|
-
...instance.base !== void 0 ? { base: instance.base } : {}
|
|
72890
|
+
...instance.base !== void 0 ? { base: instance.base } : {},
|
|
72891
|
+
...publication !== void 0 ? { publication } : {}
|
|
72364
72892
|
};
|
|
72365
72893
|
let dispatching;
|
|
72366
72894
|
try {
|
|
@@ -72851,9 +73379,19 @@ async function prCheck(body, deps) {
|
|
|
72851
73379
|
const unit = await unitRowOf(deps, instance, body.unit);
|
|
72852
73380
|
if (!unit.ok) return unit.response;
|
|
72853
73381
|
const branch2 = unit.row?.branch ?? instance.branch;
|
|
72854
|
-
const remember = async (pr) => {
|
|
72855
|
-
if (unit.row
|
|
72856
|
-
|
|
73382
|
+
const remember = async (pr, facts2) => {
|
|
73383
|
+
if (!unit.row) return;
|
|
73384
|
+
const publication = unit.row.publication ?? (facts2?.state === "open" && facts2.sameRepoHead === true && facts2.headBranchExists === true && typeof facts2.headRef === "string" && typeof facts2.baseRef === "string" && typeof facts2.headSha === "string" && /^[0-9a-f]{40}$/i.test(facts2.headSha) ? {
|
|
73385
|
+
repo: instance.repo,
|
|
73386
|
+
pr: pr.number,
|
|
73387
|
+
headRef: facts2.headRef,
|
|
73388
|
+
baseRef: facts2.baseRef,
|
|
73389
|
+
expectedHeadSha: facts2.headSha,
|
|
73390
|
+
publicationRef: facts2.headRef,
|
|
73391
|
+
owner: { instanceId: instance.id, unit: unit.row.unit }
|
|
73392
|
+
} : void 0);
|
|
73393
|
+
if (unit.row.pr?.number !== pr.number || unit.row.pr.url !== pr.url || unit.row.publication === void 0 && publication !== void 0)
|
|
73394
|
+
await deps.instances.putUnits([{ ...unit.row, pr, ...publication !== void 0 ? { publication } : {} }]);
|
|
72857
73395
|
};
|
|
72858
73396
|
try {
|
|
72859
73397
|
if (follow !== void 0) {
|
|
@@ -72862,8 +73400,12 @@ async function prCheck(body, deps) {
|
|
|
72862
73400
|
const fallbackUrl = unit.row?.pr?.url ?? `https://github.com/${instance.repo}/pull/${follow}`;
|
|
72863
73401
|
const state2 = pullRequestState(follow, fallbackUrl, facts2);
|
|
72864
73402
|
if (state2 === void 0) throw new Error(headBranchStateError(instance.repo, follow));
|
|
72865
|
-
await remember({ number: follow, url: String(state2.url) });
|
|
73403
|
+
await remember({ number: follow, url: String(state2.url) }, facts2);
|
|
72866
73404
|
if (state2.state !== "open") return json3(200, { ok: true, ...state2, at });
|
|
73405
|
+
deps.runnerOwnership?.claim(instance.repo, follow, {
|
|
73406
|
+
instanceId: instance.id,
|
|
73407
|
+
unit: unit.row?.unit ?? body.unit
|
|
73408
|
+
});
|
|
72867
73409
|
const prRef = { repo: instance.repo, number: follow };
|
|
72868
73410
|
const headSha = facts2.headSha;
|
|
72869
73411
|
const approved = entry && headSha !== void 0 ? await reviewPostedAt(deps, prRef, "approve", headSha) : void 0;
|
|
@@ -72894,13 +73436,13 @@ async function prCheck(body, deps) {
|
|
|
72894
73436
|
}
|
|
72895
73437
|
const open2 = await deps.findOpenPrByHead(instance.repo, branch2);
|
|
72896
73438
|
if (open2) {
|
|
72897
|
-
await remember({ number: open2.number, url: open2.htmlUrl });
|
|
72898
73439
|
deps.runnerOwnership?.claim(instance.repo, open2.number, {
|
|
72899
73440
|
instanceId: instance.id,
|
|
72900
73441
|
unit: unit.row?.unit ?? body.unit
|
|
72901
73442
|
});
|
|
72902
73443
|
const liveFacts = await deps.fetchPrFacts({ repo: instance.repo, number: open2.number });
|
|
72903
73444
|
if (liveFacts === void 0) throw new Error(`could not read ${instance.repo}#${open2.number}`);
|
|
73445
|
+
await remember({ number: open2.number, url: open2.htmlUrl }, liveFacts);
|
|
72904
73446
|
const current = pullRequestState(open2.number, open2.htmlUrl, liveFacts);
|
|
72905
73447
|
if (current === void 0) throw new Error(headBranchStateError(instance.repo, open2.number));
|
|
72906
73448
|
if (current.state !== "open") return json3(200, { ok: true, ...current, at });
|
|
@@ -73209,12 +73751,13 @@ async function drawCard(deps, instance, units, close) {
|
|
|
73209
73751
|
source: instance.addressSeveritySource ?? "org"
|
|
73210
73752
|
} : void 0
|
|
73211
73753
|
);
|
|
73754
|
+
const display = { verbosity: instance.verbosity ?? DEFAULT_VERBOSITY };
|
|
73212
73755
|
if (!close) {
|
|
73213
|
-
await io.status(shell.live({ detail }));
|
|
73756
|
+
await io.status(shell.live({ detail }), display);
|
|
73214
73757
|
return;
|
|
73215
73758
|
}
|
|
73216
73759
|
shell.freeze(clock4());
|
|
73217
|
-
const handle2 = await io.status(shell.live({ detail }));
|
|
73760
|
+
const handle2 = await io.status(shell.live({ detail }), display);
|
|
73218
73761
|
await handle2.done(shell.close({ kind: "done", icon: close.icon, detail: detail.join("\n") }));
|
|
73219
73762
|
}
|
|
73220
73763
|
function parseGate(raw) {
|
|
@@ -75274,6 +75817,7 @@ async function runBot() {
|
|
|
75274
75817
|
) : new NullMetricsService();
|
|
75275
75818
|
const sweepState = { roundSpent: /* @__PURE__ */ new Set() };
|
|
75276
75819
|
const runnerOwnership = new RunnerOwnershipFence(capabilities.runLedger);
|
|
75820
|
+
deps.runnerOwnership = runnerOwnership;
|
|
75277
75821
|
const pullsWiring = /* @__PURE__ */ (() => {
|
|
75278
75822
|
const state = sweepState;
|
|
75279
75823
|
const chains = /* @__PURE__ */ new Map();
|