@coreplane/switchboard 1.261.0 → 1.261.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/deploy/cloudflare-memory/worker.ts +1 -1
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/coordinator/contract.ts +39 -0
- package/dist/assets/src/core/coordinator/driver.ts +17 -5
- package/dist/assets/src/core/reviewVerdict.ts +33 -6
- 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 +333 -72
- package/dist/assets/src/core/ship/renewal.ts +1 -1
- package/dist/assets/src/core/types.ts +6 -2
- package/dist/cli.js +1264 -662
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -195,7 +195,9 @@ async function ensureWorkArea(at, version2, workers, deps) {
|
|
|
195
195
|
deps.log(`[deploy] materialised the shipped tree (version ${version2}) under ${at.workArea}`);
|
|
196
196
|
}
|
|
197
197
|
if (plan3.install.length > 0) {
|
|
198
|
-
deps.log(
|
|
198
|
+
deps.log(
|
|
199
|
+
`[deploy] npm ci --include-workspace-root --workspace ${plan3.install.join(" --workspace ")} under ${at.workArea}`
|
|
200
|
+
);
|
|
199
201
|
const r = await deps.install(at.workArea, plan3.install);
|
|
200
202
|
if (r.code !== 0)
|
|
201
203
|
return {
|
|
@@ -241,10 +243,22 @@ function packageSourceOnHost(at = OPERATOR_ROOT) {
|
|
|
241
243
|
function cliVersionOnHost(at = OPERATOR_ROOT) {
|
|
242
244
|
return at.mode === "package" ? packageSourceOnHost(at).version : packageVersion();
|
|
243
245
|
}
|
|
246
|
+
function workAreaNpmCiArgs(workspaces) {
|
|
247
|
+
return [
|
|
248
|
+
"ci",
|
|
249
|
+
"--include-workspace-root",
|
|
250
|
+
"--no-audit",
|
|
251
|
+
"--no-fund",
|
|
252
|
+
...workspaces.flatMap((w) => ["--workspace", w])
|
|
253
|
+
];
|
|
254
|
+
}
|
|
244
255
|
function npmCi(cwd, workspaces) {
|
|
245
256
|
return new Promise((resolve13) => {
|
|
246
|
-
const
|
|
247
|
-
|
|
257
|
+
const child = spawn("npm", workAreaNpmCiArgs(workspaces), {
|
|
258
|
+
cwd,
|
|
259
|
+
env: process.env,
|
|
260
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
261
|
+
});
|
|
248
262
|
let output = "";
|
|
249
263
|
child.stdout.on("data", (c) => output += c.toString());
|
|
250
264
|
child.stderr.on("data", (c) => output += c.toString());
|
|
@@ -3264,7 +3278,7 @@ function renewalDecision(input) {
|
|
|
3264
3278
|
};
|
|
3265
3279
|
}
|
|
3266
3280
|
function renderRenewal(decision, grant2, options = {}) {
|
|
3267
|
-
const recourse = options.idle ? "the next reply in this thread continues the unit" : "
|
|
3281
|
+
const recourse = options.idle ? "the next reply in this thread continues the unit" : "start ship again with the original task";
|
|
3268
3282
|
const senders = options.senders?.length ? `, with ${options.senders.length} message${options.senders.length === 1 ? "" : "s"} from ${options.senders.join(", ")}` : "";
|
|
3269
3283
|
if (decision.renew)
|
|
3270
3284
|
return `budget renewed, ${decision.segment - 1} of ${grant2.renewals}, continues ${decision.from !== void 0 ? decision.from.slice(0, 7) : "the branch's head"}${senders}`;
|
|
@@ -3564,11 +3578,11 @@ function buildReviewPostBody(answer, verdict, target) {
|
|
|
3564
3578
|
...target ? [`head \`${target.head.slice(0, 7)}\``] : [],
|
|
3565
3579
|
verdict ? findingCounts(verdict.findings) : "the run ended without a submit_verdict call"
|
|
3566
3580
|
];
|
|
3567
|
-
const
|
|
3581
|
+
const fixedParts = [verdictLine(verdict), `> [!${calloutKind(verdict)}]
|
|
3568
3582
|
> ${facts2.join(" \xB7 ")}`];
|
|
3569
3583
|
const findings = verdict?.findings ?? [];
|
|
3570
3584
|
if (findings.length > 0) {
|
|
3571
|
-
|
|
3585
|
+
fixedParts.push(
|
|
3572
3586
|
[
|
|
3573
3587
|
"| Severity | Finding | Where |",
|
|
3574
3588
|
"| --- | --- | --- |",
|
|
@@ -3578,15 +3592,25 @@ function buildReviewPostBody(answer, verdict, target) {
|
|
|
3578
3592
|
].join("\n")
|
|
3579
3593
|
);
|
|
3580
3594
|
}
|
|
3595
|
+
const marker = verdictMarker(verdict, target);
|
|
3596
|
+
const fixedBody = [...fixedParts, marker].join("\n\n");
|
|
3581
3597
|
const prose2 = answer.trim();
|
|
3582
|
-
if (prose2)
|
|
3598
|
+
if (!prose2) return fixedBody;
|
|
3599
|
+
const details2 = (text, truncated) => `<details>
|
|
3583
3600
|
<summary>Full review</summary>
|
|
3584
3601
|
|
|
3585
|
-
${
|
|
3602
|
+
${text}${truncated ? `${text ? "\n\n" : ""}${REVIEW_TRUNCATION_NOTE}` : ""}
|
|
3586
3603
|
|
|
3587
|
-
</details
|
|
3588
|
-
|
|
3589
|
-
|
|
3604
|
+
</details>`;
|
|
3605
|
+
const render3 = (text, truncated) => [...fixedParts, details2(text, truncated), marker].join("\n\n");
|
|
3606
|
+
const full = render3(prose2, false);
|
|
3607
|
+
if ([...full].length <= MAX_REVIEW_POST_CODE_POINTS) return full;
|
|
3608
|
+
const clippedShell = render3("", true);
|
|
3609
|
+
const proseSeparatorCodePoints = 2;
|
|
3610
|
+
const proseBudget = MAX_REVIEW_POST_CODE_POINTS - [...clippedShell].length - proseSeparatorCodePoints;
|
|
3611
|
+
if (proseBudget > 0) return render3([...prose2].slice(0, proseBudget).join(""), true);
|
|
3612
|
+
if ([...clippedShell].length <= MAX_REVIEW_POST_CODE_POINTS) return clippedShell;
|
|
3613
|
+
return full;
|
|
3590
3614
|
}
|
|
3591
3615
|
function buildReviewChannelReply(input) {
|
|
3592
3616
|
const { answer, verdict, posted, liveUrl } = input;
|
|
@@ -3675,7 +3699,7 @@ function redactVerdict(v, redact = redactSecrets) {
|
|
|
3675
3699
|
function redactDispositions(dispositions, redact = redactSecrets) {
|
|
3676
3700
|
return dispositions.map((d) => ({ findingId: d.findingId, disposition: d.disposition, note: redact(d.note) }));
|
|
3677
3701
|
}
|
|
3678
|
-
var FINDING_SEVERITIES, ADDRESS_SEVERITIES, DEFAULT_ADDRESS_SEVERITY, isAddressSeverity, severityRank, LGTM_TOKEN, CHANGES_TOKEN, NO_VERDICT_LINE, escapeMarkdownTableCell, unescapeMarkdownTableCell, isRepoPath, VERDICT_KINDS, DISPOSITION_KINDS, isRecordLike, REVIEW_POST_HEAD;
|
|
3702
|
+
var FINDING_SEVERITIES, ADDRESS_SEVERITIES, DEFAULT_ADDRESS_SEVERITY, isAddressSeverity, severityRank, LGTM_TOKEN, CHANGES_TOKEN, NO_VERDICT_LINE, MAX_REVIEW_POST_CODE_POINTS, REVIEW_TRUNCATION_NOTE, escapeMarkdownTableCell, unescapeMarkdownTableCell, isRepoPath, VERDICT_KINDS, DISPOSITION_KINDS, isRecordLike, REVIEW_POST_HEAD;
|
|
3679
3703
|
var init_reviewVerdict = __esm({
|
|
3680
3704
|
"../../src/core/reviewVerdict.ts"() {
|
|
3681
3705
|
"use strict";
|
|
@@ -3690,6 +3714,8 @@ var init_reviewVerdict = __esm({
|
|
|
3690
3714
|
LGTM_TOKEN = "LGTM:";
|
|
3691
3715
|
CHANGES_TOKEN = "Changes requested:";
|
|
3692
3716
|
NO_VERDICT_LINE = "No verdict submitted \u2014 not approving.";
|
|
3717
|
+
MAX_REVIEW_POST_CODE_POINTS = 65e3;
|
|
3718
|
+
REVIEW_TRUNCATION_NOTE = "_(review truncated to fit GitHub's review size limit)_";
|
|
3693
3719
|
escapeMarkdownTableCell = (text) => oneLine2(text).replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
3694
3720
|
unescapeMarkdownTableCell = (text) => text.replace(/\\([\\|])/g, "$1");
|
|
3695
3721
|
isRepoPath = (file) => /^[^\s`]+$/.test(file) && !/^[a-z][a-z0-9+.-]*:\/\//i.test(file);
|
|
@@ -3711,10 +3737,10 @@ function interruptionCauseOfWords(words) {
|
|
|
3711
3737
|
return void 0;
|
|
3712
3738
|
}
|
|
3713
3739
|
function shipInterruptedNote(prUrl3, cause, idle = false) {
|
|
3714
|
-
const
|
|
3715
|
-
const
|
|
3716
|
-
const
|
|
3717
|
-
return `${opening} ${
|
|
3740
|
+
const opening = cause === "container_replaced" ? "\u26A0\uFE0F Work stopped before the next review because the repository container was replaced and the run could not continue." : cause === "sandbox_fault" ? "\u26A0\uFE0F Work stopped before the next review because the sandbox failed and the run could not continue." : cause === "bot_restart" ? "\u26A0\uFE0F Work stopped before the next review because Switchboard restarted and the run could not continue." : "\u26A0\uFE0F Work stopped before the next review because the run could not continue.";
|
|
3741
|
+
const saved = prUrl3 ? `The existing pull request is ${prUrl3}; Switchboard did not verify the stopped run's latest commit there.` : "No open pull request was verified for the stopped run's latest commit.";
|
|
3742
|
+
const next = idle ? "The unit remains live; a reply in this thread is recorded as its continuation action." : "No review was started. Next action: start ship again after the remote branch and pull request state can be verified.";
|
|
3743
|
+
return `${opening} ${saved} ${next}`;
|
|
3718
3744
|
}
|
|
3719
3745
|
function planIdOf(planPath) {
|
|
3720
3746
|
const base = planPath.split("/").pop() ?? "";
|
|
@@ -4160,6 +4186,7 @@ var init_runEvents = __esm({
|
|
|
4160
4186
|
"ledger_untracked",
|
|
4161
4187
|
"rebind_refused",
|
|
4162
4188
|
"work_left_behind",
|
|
4189
|
+
"publication_blocked",
|
|
4163
4190
|
"workspace_torn_down",
|
|
4164
4191
|
"pr_not_opened",
|
|
4165
4192
|
"pr_head_unverified",
|
|
@@ -5766,6 +5793,7 @@ function isCoordinatorUnit(v) {
|
|
|
5766
5793
|
if (r.issue !== void 0 && !isFinite(r.issue)) return false;
|
|
5767
5794
|
if (r.pr !== void 0 && !isPr(r.pr)) return false;
|
|
5768
5795
|
if (r.resume !== void 0 && !isResume(r.resume)) return false;
|
|
5796
|
+
if (r.publication !== void 0 && !isPublication(r.publication)) return false;
|
|
5769
5797
|
if (r.record !== void 0 && (typeof r.record !== "string" || !/^\d{4}$/.test(r.record))) return false;
|
|
5770
5798
|
if (r.lastPush !== void 0 && !isText(r.lastPush)) return false;
|
|
5771
5799
|
if (r.segments !== void 0 && (!Array.isArray(r.segments) || !r.segments.every(isSegment))) return false;
|
|
@@ -5829,7 +5857,7 @@ async function sendChildSignal(workflow, signal2) {
|
|
|
5829
5857
|
return { kind: "failed", instance, type, reason: err2 instanceof Error ? err2.message : String(err2) };
|
|
5830
5858
|
}
|
|
5831
5859
|
}
|
|
5832
|
-
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;
|
|
5833
5861
|
var init_contract2 = __esm({
|
|
5834
5862
|
"../../src/core/coordinator/contract.ts"() {
|
|
5835
5863
|
"use strict";
|
|
@@ -5869,6 +5897,8 @@ var init_contract2 = __esm({
|
|
|
5869
5897
|
isObject = (v) => typeof v === "object" && v !== null;
|
|
5870
5898
|
isPr = (v) => isObject(v) && isFinite(v.number) && isText(v.url, 2048);
|
|
5871
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);
|
|
5872
5902
|
isThread = (v) => isObject(v) && isText(v.threadKey) && isOptionalText(v.sourceUrl);
|
|
5873
5903
|
isRoundGate = (v) => isObject(v) && isAddressSeverity(v.level) && Array.isArray(v.findings) && v.findings.every((f) => typeof f === "string");
|
|
5874
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));
|
|
@@ -6553,15 +6583,15 @@ function capEvent(event, maxBytes) {
|
|
|
6553
6583
|
}
|
|
6554
6584
|
return { event: capped4, bytes: size };
|
|
6555
6585
|
}
|
|
6556
|
-
function fitRecordToBudget(
|
|
6557
|
-
const cappedAll =
|
|
6558
|
-
const measure = (evs) => utf8ByteLength(JSON.stringify({ ...
|
|
6559
|
-
const whole = { ...
|
|
6586
|
+
function fitRecordToBudget(record3, maxBytes = MAX_RECORD_BYTES) {
|
|
6587
|
+
const cappedAll = record3.events.map((e) => capEvent(e, MAX_EVENT_BYTES));
|
|
6588
|
+
const measure = (evs) => utf8ByteLength(JSON.stringify({ ...record3, events: evs, storedEventCount: evs.length, truncated: true }));
|
|
6589
|
+
const whole = { ...record3, events: cappedAll.map((c) => c.event), storedEventCount: cappedAll.length };
|
|
6560
6590
|
if (utf8ByteLength(JSON.stringify(whole)) <= maxBytes) return whole;
|
|
6561
6591
|
const baseline = measure([]);
|
|
6562
6592
|
const capped4 = dropSpanPairsFromTheMiddle(cappedAll, maxBytes - baseline);
|
|
6563
6593
|
const events = capped4.map((c) => c.event);
|
|
6564
|
-
if (measure(events) <= maxBytes) return { ...
|
|
6594
|
+
if (measure(events) <= maxBytes) return { ...record3, events, storedEventCount: events.length, truncated: true };
|
|
6565
6595
|
const sizes = capped4.map((c) => c.bytes + 1);
|
|
6566
6596
|
let remaining = maxBytes - measure([]);
|
|
6567
6597
|
let head = 0;
|
|
@@ -6578,7 +6608,7 @@ function fitRecordToBudget(record2, maxBytes = MAX_RECORD_BYTES) {
|
|
|
6578
6608
|
while (kept.length > 0 && measure(kept) > maxBytes) {
|
|
6579
6609
|
kept.splice(Math.floor(kept.length / 2), 1);
|
|
6580
6610
|
}
|
|
6581
|
-
return { ...
|
|
6611
|
+
return { ...record3, events: kept, storedEventCount: kept.length, truncated: true };
|
|
6582
6612
|
}
|
|
6583
6613
|
function headLength(events) {
|
|
6584
6614
|
let n2 = 0;
|
|
@@ -6696,9 +6726,9 @@ function dominantFriction(byCategory) {
|
|
|
6696
6726
|
}
|
|
6697
6727
|
return best;
|
|
6698
6728
|
}
|
|
6699
|
-
function pointOf(
|
|
6700
|
-
if (
|
|
6701
|
-
const usage =
|
|
6729
|
+
function pointOf(record3, prices) {
|
|
6730
|
+
if (record3.provisional === true) return void 0;
|
|
6731
|
+
const usage = record3.usage ?? emptyUsage();
|
|
6702
6732
|
const priced = llmUsdOfUsage(usage, prices);
|
|
6703
6733
|
let inputTokens = 0;
|
|
6704
6734
|
let outputTokens = 0;
|
|
@@ -6710,27 +6740,27 @@ function pointOf(record2, prices) {
|
|
|
6710
6740
|
cacheReadTokens += m.cacheReadTokens;
|
|
6711
6741
|
cacheWriteTokens += m.cacheWriteTokens;
|
|
6712
6742
|
}
|
|
6713
|
-
const shape =
|
|
6743
|
+
const shape = record3.diagnosis.shape;
|
|
6714
6744
|
const blobs = {
|
|
6715
6745
|
schema: POINT_SCHEMA,
|
|
6716
|
-
agent:
|
|
6717
|
-
preset:
|
|
6718
|
-
model:
|
|
6719
|
-
status:
|
|
6720
|
-
"failure kind":
|
|
6721
|
-
"dominant friction": dominantFriction(
|
|
6722
|
-
channel:
|
|
6723
|
-
repository:
|
|
6724
|
-
"machine class":
|
|
6725
|
-
"route class":
|
|
6726
|
-
reply:
|
|
6727
|
-
lineage:
|
|
6728
|
-
requester:
|
|
6729
|
-
identity:
|
|
6730
|
-
"run id":
|
|
6746
|
+
agent: record3.agent ?? "",
|
|
6747
|
+
preset: record3.profile?.preset ?? "",
|
|
6748
|
+
model: record3.model ?? "",
|
|
6749
|
+
status: record3.status,
|
|
6750
|
+
"failure kind": record3.failure?.kind ?? "",
|
|
6751
|
+
"dominant friction": dominantFriction(record3.diagnosis.byCategory),
|
|
6752
|
+
channel: record3.channelId,
|
|
6753
|
+
repository: record3.repo ?? "",
|
|
6754
|
+
"machine class": record3.profile?.machine ?? "",
|
|
6755
|
+
"route class": record3.route !== void 0 ? "routed" : "chosen",
|
|
6756
|
+
reply: record3.replyOk === true ? "ok" : record3.replyOk === false ? "failed" : "none",
|
|
6757
|
+
lineage: record3.parentRunId !== void 0 ? "child" : "root",
|
|
6758
|
+
requester: record3.userId,
|
|
6759
|
+
identity: record3.profile?.identity ?? "",
|
|
6760
|
+
"run id": record3.id
|
|
6731
6761
|
};
|
|
6732
6762
|
const doubles = {
|
|
6733
|
-
wall:
|
|
6763
|
+
wall: record3.finishedAt - (record3.receivedAt ?? record3.startedAt),
|
|
6734
6764
|
"getting ready": shape?.gettingReadyMs ?? 0,
|
|
6735
6765
|
thinking: shape?.thinkingMs ?? 0,
|
|
6736
6766
|
tools: shape?.toolsMs ?? 0,
|
|
@@ -6744,15 +6774,15 @@ function pointOf(record2, prices) {
|
|
|
6744
6774
|
"cache read tokens": cacheReadTokens,
|
|
6745
6775
|
"cache write tokens": cacheWriteTokens,
|
|
6746
6776
|
dollars: priced.usd,
|
|
6747
|
-
steps:
|
|
6748
|
-
"tool calls": isFinite_2(
|
|
6749
|
-
events:
|
|
6777
|
+
steps: record3.stepCount ?? 0,
|
|
6778
|
+
"tool calls": isFinite_2(record3.diagnosis.toolCalls) ? record3.diagnosis.toolCalls : 0,
|
|
6779
|
+
events: record3.eventCount,
|
|
6750
6780
|
"unpriced tokens": priced.unpricedTokens,
|
|
6751
|
-
minutes:
|
|
6752
|
-
"finished at":
|
|
6781
|
+
minutes: record3.profile?.minutes ?? 0,
|
|
6782
|
+
"finished at": record3.finishedAt
|
|
6753
6783
|
};
|
|
6754
6784
|
return {
|
|
6755
|
-
indexes: [capBlob(
|
|
6785
|
+
indexes: [capBlob(record3.agent ?? UNKNOWN_AGENT)],
|
|
6756
6786
|
blobs: POINT_COLUMNS.blobs.map((n2) => capBlob(blobs[n2])),
|
|
6757
6787
|
doubles: POINT_COLUMNS.doubles.map((n2) => doubles[n2])
|
|
6758
6788
|
};
|
|
@@ -7102,9 +7132,9 @@ function makeSsrfLookup(resolve13) {
|
|
|
7102
7132
|
const family = (ip) => ip.includes(":") ? 6 : 4;
|
|
7103
7133
|
resolve13(hostname3).then(
|
|
7104
7134
|
(ips) => {
|
|
7105
|
-
const
|
|
7106
|
-
if (
|
|
7107
|
-
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}`));
|
|
7108
7138
|
return;
|
|
7109
7139
|
}
|
|
7110
7140
|
if (ips.length === 0) {
|
|
@@ -10390,12 +10420,8 @@ function containerApplicationName(script, className) {
|
|
|
10390
10420
|
return `${script}-${className.toLowerCase()}`;
|
|
10391
10421
|
}
|
|
10392
10422
|
function bindLiveGate(gate, script, healthUrl) {
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
healthUrl,
|
|
10396
|
-
bearerEnv: gate.bearerEnv,
|
|
10397
|
-
containerApp: containerApplicationName(script, gate.containerClass)
|
|
10398
|
-
};
|
|
10423
|
+
const containerApp = containerApplicationName(script, gate.containerClass);
|
|
10424
|
+
return gate.kind === "bot" ? { kind: "bot", healthUrl, containerApp } : { kind: "sandbox", healthUrl, bearerEnv: gate.bearerEnv, containerApp };
|
|
10399
10425
|
}
|
|
10400
10426
|
function workersFor(profile) {
|
|
10401
10427
|
return WORKER_SPECS.flatMap(({ preflight, liveGate, ...spec }) => {
|
|
@@ -10410,8 +10436,8 @@ function workersFor(profile) {
|
|
|
10410
10436
|
script,
|
|
10411
10437
|
baseUrl,
|
|
10412
10438
|
healthUrl,
|
|
10413
|
-
//
|
|
10414
|
-
...preflight ? { preflight: liveGate?.kind === "
|
|
10439
|
+
// The bot preflight reads the same URL as its application + health live gate.
|
|
10440
|
+
...preflight ? { preflight: liveGate?.kind === "bot" ? { ...preflight, healthUrl } : preflight } : {},
|
|
10415
10441
|
...liveGate ? { liveGate: bindLiveGate(liveGate, script, healthUrl) } : {}
|
|
10416
10442
|
}
|
|
10417
10443
|
];
|
|
@@ -10522,7 +10548,7 @@ function formatPlan(plan3) {
|
|
|
10522
10548
|
plan3.steps.forEach((s, i) => {
|
|
10523
10549
|
const pf = s.retryOnPreflightRefusal ? ` \u2014 preflight (retry every ${plan3.pollMs / 1e3}s up to ${(s.waitMaxMs ?? plan3.waitMaxMs) / 6e4} min)` : s.forcedBy ? ` \u2014 preflight FORCED (${s.forcedBy}=1)` : "";
|
|
10524
10550
|
const env = s.requiredEnv.length > 0 ? ` \u2014 needs ${s.requiredEnv.map((r) => r.anyOf.length === 1 ? r.anyOf[0] : `one of ${r.anyOf.join(" / ")}`).join(", ")}` : "";
|
|
10525
|
-
const live = s.liveGate ? s.liveGate.kind === "
|
|
10551
|
+
const live = s.liveGate ? s.liveGate.kind === "bot" ? ` \u2014 then wait until live (${s.liveGate.containerApp} advances to the deployed image + ${s.liveGate.healthUrl} build.commit exactly == HEAD)` : ` \u2014 then wait until live (${s.liveGate.healthUrl} build.commit == HEAD + every running ${s.liveGate.containerApp} instance on the app version + an /exec probe answers ok from one)` : "";
|
|
10526
10552
|
const cap = s.capabilities.length > 0 ? ` \u2014 credential must pass ${s.capabilities.map((c) => `\`${c.command.join(" ")}\` (${c.needs})`).join(" and ")}` : "";
|
|
10527
10553
|
lines.push(
|
|
10528
10554
|
` ${i + 1}. ${s.name} (${s.script}) \u2014 ${s.dir}: ${s.command.join(" ")}${pf}${env}${cap}${live}
|
|
@@ -10593,7 +10619,7 @@ function classifyDeployOutput(exitCode, output) {
|
|
|
10593
10619
|
const firstError = lines.find((l) => isErrorish(l) && !/^npm ERR!/i.test(l)) ?? lines.find(isErrorish);
|
|
10594
10620
|
return { kind: "failed", reason: firstError ?? `exit ${exitCode}` };
|
|
10595
10621
|
}
|
|
10596
|
-
var UNSET_ENV, DEPLOY_FORCE_ENV, SANDBOX_CONTAINER_CLASS, SANDBOX_BEARER_ENV, R2_CAPABILITY, RESIDENT_BEARER_ENVS, RESIDENT_WAIT_MAX_MS, CONTAINERS_CAPABILITY, WORKER_DIRS, WORKER_SPECS, DEPLOY_ORDER, CONFIG_DOCUMENT_KEY, hasImage, ANSI_SEQUENCE;
|
|
10622
|
+
var UNSET_ENV, DEPLOY_FORCE_ENV, BOT_CONTAINER_CLASS, SANDBOX_CONTAINER_CLASS, SANDBOX_BEARER_ENV, R2_CAPABILITY, RESIDENT_BEARER_ENVS, RESIDENT_WAIT_MAX_MS, CONTAINERS_CAPABILITY, WORKER_DIRS, WORKER_SPECS, DEPLOY_ORDER, CONFIG_DOCUMENT_KEY, hasImage, ANSI_SEQUENCE;
|
|
10597
10623
|
var init_plan = __esm({
|
|
10598
10624
|
"../../src/deploy/plan.ts"() {
|
|
10599
10625
|
"use strict";
|
|
@@ -10605,6 +10631,7 @@ var init_plan = __esm({
|
|
|
10605
10631
|
init_profile2();
|
|
10606
10632
|
UNSET_ENV = ["CLOUDFLARE_ACCOUNT_ID"];
|
|
10607
10633
|
DEPLOY_FORCE_ENV = "SWITCHBOARD_DEPLOY_FORCE";
|
|
10634
|
+
BOT_CONTAINER_CLASS = "SwitchboardServer";
|
|
10608
10635
|
SANDBOX_CONTAINER_CLASS = "SwitchboardSandbox";
|
|
10609
10636
|
SANDBOX_BEARER_ENV = "SANDBOX_TOKEN";
|
|
10610
10637
|
R2_CAPABILITY = {
|
|
@@ -10665,10 +10692,10 @@ var init_plan = __esm({
|
|
|
10665
10692
|
]
|
|
10666
10693
|
},
|
|
10667
10694
|
preflight: { forceEnv: DEPLOY_FORCE_ENV, baseUrlEnv: "SWITCHBOARD_BASE_URL" },
|
|
10668
|
-
liveGate: { kind: "
|
|
10695
|
+
liveGate: { kind: "bot", containerClass: BOT_CONTAINER_CLASS },
|
|
10669
10696
|
// The preflight reads the container application and the deploy pushes the image: both need Containers.
|
|
10670
10697
|
capabilities: [CONTAINERS_CAPABILITY],
|
|
10671
|
-
why: "container shim \u2014 preflight refuses only over a rollout in progress (runs in flight hand off); done only when the
|
|
10698
|
+
why: "container shim \u2014 preflight refuses only over a rollout in progress (runs in flight hand off); done only when the application advances to the deployed image and /healthz serves the exact commit. A rotated bot secret needs no build: `wrangler secret put` alone leaves the running container on its old env \u2014 `deploy restart` restarts it on the current application target"
|
|
10672
10699
|
},
|
|
10673
10700
|
{
|
|
10674
10701
|
name: "resident",
|
|
@@ -11788,11 +11815,11 @@ var init_runStoreWorker = __esm({
|
|
|
11788
11815
|
fetchImpl;
|
|
11789
11816
|
abandoned() {
|
|
11790
11817
|
}
|
|
11791
|
-
async put(
|
|
11792
|
-
if (!RUN_ID_PATTERN.test(
|
|
11793
|
-
throw new PermanentStoreError(`run store: refusing to put malformed id ${JSON.stringify(
|
|
11794
|
-
const body = { storeKey: this.opts.storeKey, record:
|
|
11795
|
-
const point = pointOf(
|
|
11818
|
+
async put(record3, trace) {
|
|
11819
|
+
if (!RUN_ID_PATTERN.test(record3.id))
|
|
11820
|
+
throw new PermanentStoreError(`run store: refusing to put malformed id ${JSON.stringify(record3.id)}`);
|
|
11821
|
+
const body = { storeKey: this.opts.storeKey, record: record3 };
|
|
11822
|
+
const point = pointOf(record3, this.opts.prices);
|
|
11796
11823
|
if (point !== void 0) body.point = point;
|
|
11797
11824
|
if (this.opts.policy) {
|
|
11798
11825
|
body.policy = this.opts.policy;
|
|
@@ -11962,6 +11989,66 @@ var init_supersede = __esm({
|
|
|
11962
11989
|
}
|
|
11963
11990
|
});
|
|
11964
11991
|
|
|
11992
|
+
// ../../src/deploy/botLiveGate.ts
|
|
11993
|
+
function judge(input) {
|
|
11994
|
+
if ("error" in input.app)
|
|
11995
|
+
return waiting(`container application ${input.containerApp} unreadable: ${input.app.error}`);
|
|
11996
|
+
const app = input.app.value;
|
|
11997
|
+
let rollout;
|
|
11998
|
+
if (input.target) {
|
|
11999
|
+
if (input.target.image !== null && app.image !== input.target.image)
|
|
12000
|
+
return waiting(
|
|
12001
|
+
`container application ${input.containerApp} still targets ${app.image ?? "an unknown image"}; expected ${input.target.image}`
|
|
12002
|
+
);
|
|
12003
|
+
if ("error" in input.before)
|
|
12004
|
+
return waiting(
|
|
12005
|
+
`container application ${input.containerApp} pre-deploy version unreadable (${input.before.error}); cannot prove the application version advanced`
|
|
12006
|
+
);
|
|
12007
|
+
if (app.version <= input.before.value.version)
|
|
12008
|
+
return waiting(
|
|
12009
|
+
`container application ${input.containerApp} is still at pre-deploy version ${input.before.value.version}; expected a newer application version`
|
|
12010
|
+
);
|
|
12011
|
+
rollout = ` at version ${app.version} (up from ${input.before.value.version})`;
|
|
12012
|
+
} else {
|
|
12013
|
+
rollout = ` at version ${app.version} (Worker-only deploy; no application change printed)`;
|
|
12014
|
+
}
|
|
12015
|
+
if ("error" in input.health) return waiting(`health: GET /healthz failed: ${input.health.error}`);
|
|
12016
|
+
if (input.health.status !== 200)
|
|
12017
|
+
return waiting(
|
|
12018
|
+
`health: GET /healthz \u2192 HTTP ${input.health.status}; expected the deployed container's exact build identity`
|
|
12019
|
+
);
|
|
12020
|
+
if (!input.health.body || input.health.body.ok !== true)
|
|
12021
|
+
return waiting("health: /healthz is not ready or did not answer with JSON");
|
|
12022
|
+
const commit = servedCommit(input.health.body);
|
|
12023
|
+
if (!commit) return waiting("health: /healthz carries no build identity");
|
|
12024
|
+
if (commit !== input.expectedCommit)
|
|
12025
|
+
return waiting(`health: serving commit ${commit}, expected exact ${input.expectedCommit}`);
|
|
12026
|
+
if (input.health.body.draining === true)
|
|
12027
|
+
return waiting("health: the exact deployed commit answers but its container is draining");
|
|
12028
|
+
const target = input.target?.image ?? app.image ?? "an unchanged image";
|
|
12029
|
+
return {
|
|
12030
|
+
kind: "live",
|
|
12031
|
+
summary: `container application ${input.containerApp} targets ${target}${rollout}; /healthz serves exact ${commit}`
|
|
12032
|
+
};
|
|
12033
|
+
}
|
|
12034
|
+
function decideBotLive(input, deadlineMs = LIVE_GATE_DEADLINE_MS) {
|
|
12035
|
+
const decision = judge(input);
|
|
12036
|
+
if (decision.kind !== "waiting" || input.elapsedMs < deadlineMs) return decision;
|
|
12037
|
+
return {
|
|
12038
|
+
kind: "failed",
|
|
12039
|
+
reason: `${decision.reason} \u2014 still not live after ${Math.round(input.elapsedMs / MINUTE_MS)} min (deadline ${deadlineMs / MINUTE_MS} min)`
|
|
12040
|
+
};
|
|
12041
|
+
}
|
|
12042
|
+
var waiting;
|
|
12043
|
+
var init_botLiveGate = __esm({
|
|
12044
|
+
"../../src/deploy/botLiveGate.ts"() {
|
|
12045
|
+
"use strict";
|
|
12046
|
+
init_budgets();
|
|
12047
|
+
init_liveGate();
|
|
12048
|
+
waiting = (reason2) => ({ kind: "waiting", reason: reason2 });
|
|
12049
|
+
}
|
|
12050
|
+
});
|
|
12051
|
+
|
|
11965
12052
|
// ../../src/execution/sandboxErrors.ts
|
|
11966
12053
|
function fleetBusyRunEndedLine(run2, thread, err2) {
|
|
11967
12054
|
if (typeof err2 !== "object" || err2 === null) return null;
|
|
@@ -12048,51 +12135,51 @@ function rolloutAdvanced(app, before, target) {
|
|
|
12048
12135
|
reason: `rollout: pre-deploy version unreadable (${before.error}); application image ${app.image === null ? "?" : shortImage(app.image)} is not the deploy's ${shortImage(target.image)}`
|
|
12049
12136
|
};
|
|
12050
12137
|
}
|
|
12051
|
-
function
|
|
12138
|
+
function judge2(input) {
|
|
12052
12139
|
const worker = decideWorker(input.health, input.deployedCommit);
|
|
12053
|
-
if (!worker.ok) return worker.fatal ? { kind: "failed", reason: worker.reason } :
|
|
12054
|
-
if (input.app === null) return
|
|
12055
|
-
if ("error" in input.app) return
|
|
12140
|
+
if (!worker.ok) return worker.fatal ? { kind: "failed", reason: worker.reason } : waiting2(worker.reason);
|
|
12141
|
+
if (input.app === null) return waiting2("rollout: not read yet");
|
|
12142
|
+
if ("error" in input.app) return waiting2(`rollout: ${input.app.error}`);
|
|
12056
12143
|
const app = input.app.value;
|
|
12057
12144
|
const appVersion = app.version;
|
|
12058
12145
|
let advancedFrom = null;
|
|
12059
12146
|
if (input.target) {
|
|
12060
12147
|
const advanced = rolloutAdvanced(app, input.before, input.target);
|
|
12061
|
-
if (!advanced.ok) return
|
|
12148
|
+
if (!advanced.ok) return waiting2(advanced.reason);
|
|
12062
12149
|
advancedFrom = advanced.from;
|
|
12063
12150
|
}
|
|
12064
|
-
if (input.instances === null) return
|
|
12065
|
-
if ("error" in input.instances) return
|
|
12151
|
+
if (input.instances === null) return waiting2("rollout: instances not read yet");
|
|
12152
|
+
if ("error" in input.instances) return waiting2(`rollout: ${input.instances.error}`);
|
|
12066
12153
|
const running = input.instances.value.filter((i) => i.state === "running");
|
|
12067
12154
|
const rolloutSummary = advancedFrom === null ? `Worker-only deploy \u2014 no container change (${running.length} running instance(s) on version ${appVersion})` : `rollout complete (${running.length} running instance(s) on version ${appVersion}, ${advancedFrom})`;
|
|
12068
12155
|
const stale = running.filter((i) => i.version !== appVersion);
|
|
12069
12156
|
if (stale.length > 0) {
|
|
12070
12157
|
const versions = [...new Set(stale.map((i) => i.version === null ? "?" : String(i.version)))].join("/");
|
|
12071
|
-
return
|
|
12158
|
+
return waiting2(
|
|
12072
12159
|
`rollout in progress \u2014 ${stale.length} of ${running.length} running instance(s) still on version ${versions}, app version ${appVersion}`
|
|
12073
12160
|
);
|
|
12074
12161
|
}
|
|
12075
|
-
if (input.probe === null) return
|
|
12076
|
-
if ("error" in input.probe) return
|
|
12162
|
+
if (input.probe === null) return waiting2("probe: not sent yet");
|
|
12163
|
+
if ("error" in input.probe) return waiting2(`probe: ${input.probe.error}`);
|
|
12077
12164
|
const body = input.probe.body;
|
|
12078
12165
|
if (body.reason === FLEET_BUSY_REASON)
|
|
12079
|
-
return
|
|
12166
|
+
return waiting2("probe: fleet busy \u2014 no free instance for the probe thread (max_instances reached)");
|
|
12080
12167
|
if (typeof body.error === "string" && CONTAINER_STARTING.test(body.error))
|
|
12081
|
-
return
|
|
12168
|
+
return waiting2("probe: container starting");
|
|
12082
12169
|
if ("error" in body) {
|
|
12083
12170
|
const failed = body.error === "" ? "/exec failed with an EMPTY error" : `/exec failed \u2014 ${typeof body.error === "string" ? body.error : JSON.stringify(body.error)}`;
|
|
12084
|
-
return
|
|
12171
|
+
return waiting2(`probe: ${failed} \u2014 the probe's container may still run the previous image`);
|
|
12085
12172
|
}
|
|
12086
12173
|
if (body.exitCode !== 0) {
|
|
12087
12174
|
const stderr = typeof body.stderr === "string" ? body.stderr.trim() : "";
|
|
12088
|
-
return
|
|
12175
|
+
return waiting2(`probe: \`${PROBE_COMMAND}\` exited ${String(body.exitCode)}${stderr ? `: ${stderr}` : ""}`);
|
|
12089
12176
|
}
|
|
12090
12177
|
const stdout = typeof body.stdout === "string" ? body.stdout.trim() : "";
|
|
12091
|
-
if (stdout !== "ok") return
|
|
12178
|
+
if (stdout !== "ok") return waiting2(`probe: \`${PROBE_COMMAND}\` printed ${JSON.stringify(stdout)}`);
|
|
12092
12179
|
const mine = input.instances.value.find((i) => i.name === input.probeThreadKey);
|
|
12093
|
-
if (!mine) return
|
|
12180
|
+
if (!mine) return waiting2(`probe instance ${input.probeThreadKey} not listed yet`);
|
|
12094
12181
|
if (mine.version !== appVersion)
|
|
12095
|
-
return
|
|
12182
|
+
return waiting2(
|
|
12096
12183
|
`probe instance ${input.probeThreadKey} is on version ${mine.version === null ? "?" : mine.version}, app version ${appVersion} \u2014 the probe landed on a previous image`
|
|
12097
12184
|
);
|
|
12098
12185
|
return {
|
|
@@ -12101,7 +12188,7 @@ function judge(input) {
|
|
|
12101
12188
|
};
|
|
12102
12189
|
}
|
|
12103
12190
|
function decideSandboxLive(input, deadlineMs = LIVE_GATE_DEADLINE_MS) {
|
|
12104
|
-
const d =
|
|
12191
|
+
const d = judge2(input);
|
|
12105
12192
|
if (d.kind !== "waiting" || input.elapsedMs < deadlineMs) return d;
|
|
12106
12193
|
return {
|
|
12107
12194
|
kind: "failed",
|
|
@@ -12175,7 +12262,7 @@ function parseExecStream(text) {
|
|
|
12175
12262
|
return { error: `/exec answered no JSON document: ${JSON.stringify(text.trim().slice(0, 120))}` };
|
|
12176
12263
|
}
|
|
12177
12264
|
}
|
|
12178
|
-
var PROBE_COMMAND, PROBE_TIMEOUT_MS, CONTAINER_STARTING,
|
|
12265
|
+
var PROBE_COMMAND, PROBE_TIMEOUT_MS, CONTAINER_STARTING, waiting2, isRecord2, ANSI_SEQUENCE2, SECTION_GUTTER;
|
|
12179
12266
|
var init_sandboxLiveGate = __esm({
|
|
12180
12267
|
"../../src/deploy/sandboxLiveGate.ts"() {
|
|
12181
12268
|
"use strict";
|
|
@@ -12184,7 +12271,7 @@ var init_sandboxLiveGate = __esm({
|
|
|
12184
12271
|
PROBE_COMMAND = "echo ok";
|
|
12185
12272
|
PROBE_TIMEOUT_MS = 6e4;
|
|
12186
12273
|
CONTAINER_STARTING = /Container is starting/i;
|
|
12187
|
-
|
|
12274
|
+
waiting2 = (reason2) => ({ kind: "waiting", reason: reason2 });
|
|
12188
12275
|
isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
12189
12276
|
ANSI_SEQUENCE2 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
12190
12277
|
SECTION_GUTTER = /^[\s│├╭╰|]+/;
|
|
@@ -12510,26 +12597,6 @@ async function fetchHealthz(url) {
|
|
|
12510
12597
|
const r = await readHealthz(url);
|
|
12511
12598
|
return "body" in r ? r.body : void 0;
|
|
12512
12599
|
}
|
|
12513
|
-
async function waitUntilLive(step, healthUrl, expectedCommit, io, clock4, previousStartedAt) {
|
|
12514
|
-
const started = clock4();
|
|
12515
|
-
for (; ; ) {
|
|
12516
|
-
const elapsed2 = clock4() - started;
|
|
12517
|
-
const body = await fetchHealthz(healthUrl);
|
|
12518
|
-
const d = decideLive(body, expectedCommit, elapsed2, LIVE_GATE_DEADLINE_MS, {
|
|
12519
|
-
...previousStartedAt !== void 0 ? { previousStartedAt } : {}
|
|
12520
|
-
});
|
|
12521
|
-
if (d.kind === "live") return { live: true, detail: `commit ${d.commit.slice(0, 7)}`, waitedMs: elapsed2 };
|
|
12522
|
-
if (d.kind === "timeout")
|
|
12523
|
-
return {
|
|
12524
|
-
live: false,
|
|
12525
|
-
reason: `${d.reason} \u2014 gave up after ${Math.round(elapsed2 / 6e4)} min (drain deadline ${LIVE_GATE_DEADLINE_MS / 6e4} min)`
|
|
12526
|
-
};
|
|
12527
|
-
io.log(
|
|
12528
|
-
`[deploy:all] ${step.name}: deployed, not live yet \u2014 ${d.reason} (${Math.floor(elapsed2 / 6e4)}m ${Math.floor(elapsed2 % 6e4 / 1e3)}s)`
|
|
12529
|
-
);
|
|
12530
|
-
await sleep(LIVE_GATE_POLL_MS);
|
|
12531
|
-
}
|
|
12532
|
-
}
|
|
12533
12600
|
async function wranglerJson(dir, args) {
|
|
12534
12601
|
const r = await run("npx", ["wrangler", ...args], { cwd: workerDir(dir), unset: UNSET_ENV });
|
|
12535
12602
|
if (r.code !== 0)
|
|
@@ -12550,6 +12617,29 @@ async function resolveContainerAppId(dir, containerApp) {
|
|
|
12550
12617
|
containerAppIds.set(containerApp, id);
|
|
12551
12618
|
return { value: id };
|
|
12552
12619
|
}
|
|
12620
|
+
async function waitUntilBotLive(step, gate, expectedCommit, rollout, io, deps = defaultSandboxGateDeps) {
|
|
12621
|
+
const started = deps.now();
|
|
12622
|
+
for (; ; ) {
|
|
12623
|
+
const elapsed2 = deps.now() - started;
|
|
12624
|
+
const app = await deps.readAppState(step.dir, gate.containerApp);
|
|
12625
|
+
const health = await deps.readHealth(gate.healthUrl);
|
|
12626
|
+
const decision = decideBotLive({
|
|
12627
|
+
containerApp: gate.containerApp,
|
|
12628
|
+
health,
|
|
12629
|
+
app,
|
|
12630
|
+
before: rollout.before,
|
|
12631
|
+
target: rollout.target,
|
|
12632
|
+
expectedCommit,
|
|
12633
|
+
elapsedMs: elapsed2
|
|
12634
|
+
});
|
|
12635
|
+
if (decision.kind === "live") return { live: true, detail: decision.summary, waitedMs: deps.now() - started };
|
|
12636
|
+
if (decision.kind === "failed") return { live: false, reason: decision.reason };
|
|
12637
|
+
io.log(
|
|
12638
|
+
`[deploy:all] ${step.name}: deployed, not live yet \u2014 ${decision.reason} (${Math.floor(elapsed2 / 6e4)}m ${Math.floor(elapsed2 % 6e4 / 1e3)}s)`
|
|
12639
|
+
);
|
|
12640
|
+
await deps.sleep(LIVE_GATE_POLL_MS);
|
|
12641
|
+
}
|
|
12642
|
+
}
|
|
12553
12643
|
async function waitUntilSandboxLive(step, gate, expectedCommit, rollout, io, deps = defaultSandboxGateDeps) {
|
|
12554
12644
|
const bearer = deps.env[gate.bearerEnv];
|
|
12555
12645
|
if (!bearer)
|
|
@@ -12590,7 +12680,7 @@ async function waitUntilSandboxLive(step, gate, expectedCommit, rollout, io, dep
|
|
|
12590
12680
|
async function readAppBeforeUpload(step, gate, io, deps) {
|
|
12591
12681
|
const before = await deps.readAppState(step.dir, gate.containerApp);
|
|
12592
12682
|
io.log(
|
|
12593
|
-
"value" in before ? `[deploy:all] ${step.name}: container application at version ${before.value.version}${before.value.image ? ` (image ${shortImage(before.value.image)})` : ""} before the upload` : `[deploy:all] ${step.name}: could not read the container application before the upload \u2014 ${before.error}; the gate will need the deploy's image to show`
|
|
12683
|
+
"value" in before ? `[deploy:all] ${step.name}: container application at version ${before.value.version}${before.value.image ? ` (image ${shortImage(before.value.image)})` : ""} before the upload` : gate.kind === "sandbox" ? `[deploy:all] ${step.name}: could not read the container application before the upload \u2014 ${before.error}; the gate will need the deploy's image to show` : `[deploy:all] ${step.name}: could not read the container application before the upload \u2014 ${before.error}; the gate requires a readable pre-deploy version to prove advancement`
|
|
12594
12684
|
);
|
|
12595
12685
|
return before;
|
|
12596
12686
|
}
|
|
@@ -12666,8 +12756,7 @@ async function deployStepLoop(step, plan3, expectedCommit, io, deps, exec, root,
|
|
|
12666
12756
|
for (; ; ) {
|
|
12667
12757
|
io.log(`
|
|
12668
12758
|
[deploy:all] \u25B6 ${step.name} (${step.script}) \u2014 ${step.dir}: ${step.command.join(" ")}`);
|
|
12669
|
-
const
|
|
12670
|
-
const previousStartedAt = step.liveGate && step.liveGate.kind !== "sandbox" ? await fetchHealthz(step.liveGate.healthUrl).then((b) => b ? servedStartedAt(b) : void 0) : void 0;
|
|
12759
|
+
const application = step.liveGate ? { before: await readAppBeforeUpload(step, step.liveGate, io, deps) } : void 0;
|
|
12671
12760
|
const r = await exec(step, io);
|
|
12672
12761
|
const outcome = classifyDeployOutput(r.code, r.output);
|
|
12673
12762
|
if (outcome.kind === "deployed") {
|
|
@@ -12680,15 +12769,20 @@ async function deployStepLoop(step, plan3, expectedCommit, io, deps, exec, root,
|
|
|
12680
12769
|
);
|
|
12681
12770
|
const liveGate = step.liveGate;
|
|
12682
12771
|
const gate = await root.span("deploy.wait_live", async (wait2) => {
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12772
|
+
if (!application) throw new Error(`${step.name}: live gate has no application state`);
|
|
12773
|
+
const target = rolloutTargetFromDeployOutput(r.output);
|
|
12774
|
+
const from = "value" in application.before ? `version ${application.before.value.version}` : "its pre-deploy version";
|
|
12775
|
+
io.log(
|
|
12776
|
+
target ? `[deploy:all] ${step.name}: wrangler printed a container change \u2014 ${target.image ? `image ${shortImage(target.image)}` : "configuration only, image unchanged"}; the application must leave ${from}` : `[deploy:all] ${step.name}: wrangler printed no container change \u2014 Worker-only deploy, no rollout expected`
|
|
12777
|
+
);
|
|
12778
|
+
const g = liveGate.kind === "sandbox" ? await waitUntilSandboxLive(
|
|
12779
|
+
step,
|
|
12780
|
+
liveGate,
|
|
12781
|
+
expectedCommit,
|
|
12782
|
+
{ before: application.before, target },
|
|
12783
|
+
io,
|
|
12784
|
+
deps
|
|
12785
|
+
) : await waitUntilBotLive(step, liveGate, expectedCommit, { before: application.before, target }, io, deps);
|
|
12692
12786
|
wait2.setAttrs(g.live ? { outcome: "live", waitedMs: g.waitedMs } : { outcome: "not_live" });
|
|
12693
12787
|
return g;
|
|
12694
12788
|
});
|
|
@@ -13105,6 +13199,7 @@ var init_run = __esm({
|
|
|
13105
13199
|
init_planeDeploy();
|
|
13106
13200
|
init_wranglerTemplate();
|
|
13107
13201
|
init_supersede();
|
|
13202
|
+
init_botLiveGate();
|
|
13108
13203
|
init_sandboxLiveGate();
|
|
13109
13204
|
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
13110
13205
|
workerDir = (dir) => workPath(OPERATOR_ROOT, dir);
|
|
@@ -17364,7 +17459,7 @@ async function branchHeadSubject(repo, branch2) {
|
|
|
17364
17459
|
if (typeof message4 !== "string" || message4.trim() === "") return void 0;
|
|
17365
17460
|
return message4.split("\n", 1)[0].trim();
|
|
17366
17461
|
}
|
|
17367
|
-
async function
|
|
17462
|
+
async function readHeadRef(repo, branch2, headers) {
|
|
17368
17463
|
let res;
|
|
17369
17464
|
try {
|
|
17370
17465
|
res = await fetch(`https://api.github.com/repos/${repo}/git/ref/heads/${encodeGithubRef(branch2)}`, {
|
|
@@ -17372,13 +17467,17 @@ async function headRefTipSha(repo, branch2, headers) {
|
|
|
17372
17467
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
17373
17468
|
});
|
|
17374
17469
|
} catch {
|
|
17375
|
-
return
|
|
17470
|
+
return { kind: "unverified" };
|
|
17376
17471
|
}
|
|
17377
|
-
if (
|
|
17472
|
+
if (res.status === 404) return { kind: "missing" };
|
|
17473
|
+
if (!res.ok) return { kind: "unverified" };
|
|
17378
17474
|
const data = await res.json().catch(() => null);
|
|
17379
|
-
|
|
17380
|
-
|
|
17381
|
-
|
|
17475
|
+
const sha = data?.object?.sha;
|
|
17476
|
+
return data?.object?.type === "commit" && typeof sha === "string" && /^[0-9a-f]{40}$/.test(sha) ? { kind: "verified", sha } : { kind: "unverified" };
|
|
17477
|
+
}
|
|
17478
|
+
async function headRefTipSha(repo, branch2, headers) {
|
|
17479
|
+
const read = await readHeadRef(repo, branch2, headers);
|
|
17480
|
+
return read.kind === "verified" ? read.sha : void 0;
|
|
17382
17481
|
}
|
|
17383
17482
|
function preferRefTip(where, prSha, refTip, branch2) {
|
|
17384
17483
|
if (refTip === void 0) return prSha;
|
|
@@ -17428,8 +17527,11 @@ async function fetchPullRequestFacts(pr) {
|
|
|
17428
17527
|
const prSha = typeof data.head?.sha === "string" && /^[0-9a-f]{40}$/.test(data.head.sha) ? data.head.sha : void 0;
|
|
17429
17528
|
const sameRepoHead = headRepo === pr.repo.toLowerCase();
|
|
17430
17529
|
const headRef = typeof data.head?.ref === "string" && data.head.ref ? data.head.ref : void 0;
|
|
17431
|
-
const
|
|
17432
|
-
const
|
|
17530
|
+
const headRead = sameRepoHead && headRef !== void 0 ? await readHeadRef(pr.repo, headRef, headers) : void 0;
|
|
17531
|
+
const headBranchExists = headRead?.kind === "verified" ? true : headRead?.kind === "missing" ? false : void 0;
|
|
17532
|
+
const refTip = headRead?.kind === "verified" ? headRead.sha : void 0;
|
|
17533
|
+
const sha = sameRepoHead && headRef !== void 0 ? preferRefTip(`${pr.repo}#${pr.number}`, prSha, refTip, headRef) : prSha;
|
|
17534
|
+
const verifiedHead = sameRepoHead && headRef !== void 0 && prSha !== void 0 && refTip === prSha ? { repo: pr.repo, ref: headRef, sha: refTip } : void 0;
|
|
17433
17535
|
let closedBy = typeof data.closed_by?.login === "string" ? data.closed_by.login : void 0;
|
|
17434
17536
|
if (data.state === "closed" && !(typeof data.merged_at === "string" && data.merged_at) && closedBy === void 0) {
|
|
17435
17537
|
try {
|
|
@@ -17454,6 +17556,7 @@ async function fetchPullRequestFacts(pr) {
|
|
|
17454
17556
|
} : {},
|
|
17455
17557
|
...headRef !== void 0 ? { headRef } : {},
|
|
17456
17558
|
...sha ? { headSha: sha } : {},
|
|
17559
|
+
...verifiedHead !== void 0 ? { verifiedHead } : {},
|
|
17457
17560
|
...headBranchExists !== void 0 ? { headBranchExists } : {},
|
|
17458
17561
|
sameRepoHead,
|
|
17459
17562
|
...typeof data.base?.ref === "string" && data.base.ref ? { baseRef: data.base.ref } : {},
|
|
@@ -21081,8 +21184,8 @@ function pageEvents(events, opts) {
|
|
|
21081
21184
|
if (page2.length < rest.length && page2.length > 0) out.nextAfterSeq = page2[page2.length - 1].seq;
|
|
21082
21185
|
return out;
|
|
21083
21186
|
}
|
|
21084
|
-
function toListItem(
|
|
21085
|
-
const { events: _events, ...rest } =
|
|
21187
|
+
function toListItem(record3, bytes) {
|
|
21188
|
+
const { events: _events, ...rest } = record3;
|
|
21086
21189
|
return { ...rest, bytes };
|
|
21087
21190
|
}
|
|
21088
21191
|
function retentionPolicyOf(cfg) {
|
|
@@ -21233,19 +21336,19 @@ var init_runStore = __esm({
|
|
|
21233
21336
|
}
|
|
21234
21337
|
abandoned() {
|
|
21235
21338
|
}
|
|
21236
|
-
async put(
|
|
21237
|
-
if (!isValidRunId(
|
|
21339
|
+
async put(record3) {
|
|
21340
|
+
if (!isValidRunId(record3.id))
|
|
21238
21341
|
return { ok: true, retained: this.readIndex().length, stored: false, rewritten: false };
|
|
21239
21342
|
this.ensureDir();
|
|
21240
|
-
const content = JSON.stringify(
|
|
21343
|
+
const content = JSON.stringify(record3);
|
|
21241
21344
|
const bytes = utf8ByteLength(content);
|
|
21242
21345
|
const index = this.readIndex();
|
|
21243
|
-
const prev = index.find((r) => r.id ===
|
|
21244
|
-
const rewritten = prev !== void 0 && !sameStoredVersion(prev, { ...
|
|
21245
|
-
this.writeAtomic(this.recordPath(
|
|
21246
|
-
const item = toListItem(
|
|
21247
|
-
const kept = this.compact([...index.filter((r) => r.id !==
|
|
21248
|
-
return { ok: true, retained: kept.length, stored: kept.some((r) => r.id ===
|
|
21346
|
+
const prev = index.find((r) => r.id === record3.id);
|
|
21347
|
+
const rewritten = prev !== void 0 && !sameStoredVersion(prev, { ...record3, bytes });
|
|
21348
|
+
this.writeAtomic(this.recordPath(record3.id), content);
|
|
21349
|
+
const item = toListItem(record3, bytes);
|
|
21350
|
+
const kept = this.compact([...index.filter((r) => r.id !== record3.id), item]);
|
|
21351
|
+
return { ok: true, retained: kept.length, stored: kept.some((r) => r.id === record3.id), rewritten };
|
|
21249
21352
|
}
|
|
21250
21353
|
/** Retention is decided from the index alone (no per-file stat); the one
|
|
21251
21354
|
* file that matters is then read directly — a torn or missing file lands
|
|
@@ -21272,16 +21375,16 @@ var init_runStore = __esm({
|
|
|
21272
21375
|
return selectListItems(this.retainedIntact(), opts);
|
|
21273
21376
|
}
|
|
21274
21377
|
async events(id, opts) {
|
|
21275
|
-
const
|
|
21276
|
-
return
|
|
21378
|
+
const record3 = await this.get(id);
|
|
21379
|
+
return record3 ? pageEvents(record3.events, opts) : null;
|
|
21277
21380
|
}
|
|
21278
21381
|
async usage(query) {
|
|
21279
21382
|
const items = this.retainedIntact();
|
|
21280
21383
|
const records = [];
|
|
21281
21384
|
for (const item of items) {
|
|
21282
21385
|
if (item.finishedAt < query.sinceMs || item.finishedAt >= query.untilMs) continue;
|
|
21283
|
-
const
|
|
21284
|
-
if (
|
|
21386
|
+
const record3 = await this.get(item.id);
|
|
21387
|
+
if (record3) records.push(record3);
|
|
21285
21388
|
}
|
|
21286
21389
|
const parent = (id) => {
|
|
21287
21390
|
const p = items.find((i) => i.id === id);
|
|
@@ -21999,24 +22102,24 @@ function predicateWith(rules, actor, action, resourceType, kind) {
|
|
|
21999
22102
|
function predicateFor(actor, action, resourceType, kind) {
|
|
22000
22103
|
return predicateWith(POLICY, actor, action, resourceType, kind);
|
|
22001
22104
|
}
|
|
22002
|
-
function matchesPredicate(predicate,
|
|
22105
|
+
function matchesPredicate(predicate, record3) {
|
|
22003
22106
|
switch (predicate.kind) {
|
|
22004
22107
|
case "none":
|
|
22005
22108
|
return false;
|
|
22006
22109
|
case "all":
|
|
22007
22110
|
return true;
|
|
22008
22111
|
case "channels-in":
|
|
22009
|
-
return
|
|
22112
|
+
return record3.channelId !== void 0 && holds(predicate.channelIds, record3.channelId);
|
|
22010
22113
|
case "user-is":
|
|
22011
|
-
return
|
|
22114
|
+
return record3.userId !== void 0 && record3.userId === predicate.userId;
|
|
22012
22115
|
case "repos-in":
|
|
22013
|
-
return
|
|
22116
|
+
return record3.repo !== void 0 && holds(predicate.repos, record3.repo);
|
|
22014
22117
|
case "visibility-in":
|
|
22015
|
-
return predicate.visibilities.has(
|
|
22118
|
+
return predicate.visibilities.has(record3.channelVisibility ?? "unknown");
|
|
22016
22119
|
case "or":
|
|
22017
|
-
return predicate.of.some((p) => matchesPredicate(p,
|
|
22120
|
+
return predicate.of.some((p) => matchesPredicate(p, record3));
|
|
22018
22121
|
case "and":
|
|
22019
|
-
return predicate.of.length > 0 && predicate.of.every((p) => matchesPredicate(p,
|
|
22122
|
+
return predicate.of.length > 0 && predicate.of.every((p) => matchesPredicate(p, record3));
|
|
22020
22123
|
}
|
|
22021
22124
|
return false;
|
|
22022
22125
|
}
|
|
@@ -25602,9 +25705,9 @@ function endingCauseWords(cause) {
|
|
|
25602
25705
|
function isReissueSteerText(text) {
|
|
25603
25706
|
return /^the model provider .+ is answering again — re-issue the held turn and continue$/.test(text.trim());
|
|
25604
25707
|
}
|
|
25605
|
-
function waitingWords(
|
|
25606
|
-
if (
|
|
25607
|
-
return
|
|
25708
|
+
function waitingWords(waiting3) {
|
|
25709
|
+
if (waiting3.length === 0) return "its turn";
|
|
25710
|
+
return waiting3.map(
|
|
25608
25711
|
(c) => c.kind === "thread_free" ? "the thread's live run" : c.kind === "deploy_settled" ? "the pending deploy" : c.kind === "seat" ? `a seat on ${c.resident}` : c.kind === "memory" ? `memory on ${c.resident}` : c.kind === "provider_up" ? `the ${c.provider} provider` : `the ${c.window} window`
|
|
25609
25712
|
).join(", then ");
|
|
25610
25713
|
}
|
|
@@ -28876,23 +28979,23 @@ var init_turnEffort = __esm({
|
|
|
28876
28979
|
function tokenize2(text) {
|
|
28877
28980
|
return text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
28878
28981
|
}
|
|
28879
|
-
function keywordMatch(
|
|
28982
|
+
function keywordMatch(record3, query) {
|
|
28880
28983
|
const queryTokens = typeof query === "string" ? tokenize2(query) : query;
|
|
28881
28984
|
if (queryTokens.length === 0) return 0;
|
|
28882
|
-
const recordTokens = /* @__PURE__ */ new Set([...tokenize2(
|
|
28985
|
+
const recordTokens = /* @__PURE__ */ new Set([...tokenize2(record3.keywords.join(" ")), ...tokenize2(record3.text)]);
|
|
28883
28986
|
let hits = 0;
|
|
28884
28987
|
for (const token2 of queryTokens) {
|
|
28885
28988
|
if (recordTokens.has(token2)) hits++;
|
|
28886
28989
|
}
|
|
28887
28990
|
return hits / queryTokens.length;
|
|
28888
28991
|
}
|
|
28889
|
-
function recencyScore(
|
|
28890
|
-
const ts =
|
|
28992
|
+
function recencyScore(record3, now, tau = RECENCY_TAU_MS) {
|
|
28993
|
+
const ts = record3.lastUsedAt ?? record3.createdAt;
|
|
28891
28994
|
const age = Math.max(0, now - ts);
|
|
28892
28995
|
return Math.exp(-age / tau);
|
|
28893
28996
|
}
|
|
28894
|
-
function scoreRecord(
|
|
28895
|
-
return weights.keyword * keywordMatch(
|
|
28997
|
+
function scoreRecord(record3, query, now, weights = DEFAULT_WEIGHTS, tau = RECENCY_TAU_MS) {
|
|
28998
|
+
return weights.keyword * keywordMatch(record3, query) + weights.recency * recencyScore(record3, now, tau);
|
|
28896
28999
|
}
|
|
28897
29000
|
function estimateTokens(text) {
|
|
28898
29001
|
return Math.ceil(text.length / 4);
|
|
@@ -28900,11 +29003,11 @@ function estimateTokens(text) {
|
|
|
28900
29003
|
function applyBudget(records, budget) {
|
|
28901
29004
|
const out = [];
|
|
28902
29005
|
let tokens = 0;
|
|
28903
|
-
for (const
|
|
29006
|
+
for (const record3 of records) {
|
|
28904
29007
|
if (out.length >= budget.maxRecords) break;
|
|
28905
|
-
const cost = estimateTokens(renderMemoryBullet(
|
|
29008
|
+
const cost = estimateTokens(renderMemoryBullet(record3));
|
|
28906
29009
|
if (out.length > 0 && tokens + cost > budget.maxTokens) break;
|
|
28907
|
-
out.push(
|
|
29010
|
+
out.push(record3);
|
|
28908
29011
|
tokens += cost;
|
|
28909
29012
|
}
|
|
28910
29013
|
return out;
|
|
@@ -28915,8 +29018,8 @@ function memoryBlockPrefix(resource) {
|
|
|
28915
29018
|
function sanitizeMemoryField(s) {
|
|
28916
29019
|
return s.replace(/[\x00-\x1f\x7f-\x9f\u2028\u2029]/g, " ").replace(/\s+/g, " ").trim().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
28917
29020
|
}
|
|
28918
|
-
function renderMemoryBullet(
|
|
28919
|
-
return `- ${sanitizeMemoryField(
|
|
29021
|
+
function renderMemoryBullet(record3) {
|
|
29022
|
+
return `- ${sanitizeMemoryField(record3.text)} (source: ${sanitizeMemoryField(record3.sourceThreadKey)})`;
|
|
28920
29023
|
}
|
|
28921
29024
|
function renderMemoryBlock(resource, records) {
|
|
28922
29025
|
const bullets = records.map(renderMemoryBullet);
|
|
@@ -29502,7 +29605,7 @@ function parseConfirmation(v) {
|
|
|
29502
29605
|
function isRefusal(v) {
|
|
29503
29606
|
return typeof v === "string" && CONFIRMATION_REFUSALS.includes(v);
|
|
29504
29607
|
}
|
|
29505
|
-
function
|
|
29608
|
+
function judge3(rows, id, actorIds, now, kind) {
|
|
29506
29609
|
const row2 = rows.get(id);
|
|
29507
29610
|
if (!row2) return { ok: false, refused: "used" };
|
|
29508
29611
|
if (kind === "consume" && row2.expiresAt <= now) {
|
|
@@ -29572,13 +29675,13 @@ var init_confirmations = __esm({
|
|
|
29572
29675
|
}
|
|
29573
29676
|
async consume(id, actorIds) {
|
|
29574
29677
|
const rows = this.read();
|
|
29575
|
-
const out =
|
|
29678
|
+
const out = judge3(rows, id, actorIds, this.clock(), "consume");
|
|
29576
29679
|
this.write(rows);
|
|
29577
29680
|
return out;
|
|
29578
29681
|
}
|
|
29579
29682
|
async cancel(id, actorIds) {
|
|
29580
29683
|
const rows = this.read();
|
|
29581
|
-
const out =
|
|
29684
|
+
const out = judge3(rows, id, actorIds, this.clock(), "cancel");
|
|
29582
29685
|
this.write(rows);
|
|
29583
29686
|
return out;
|
|
29584
29687
|
}
|
|
@@ -29586,7 +29689,7 @@ var init_confirmations = __esm({
|
|
|
29586
29689
|
const rows = this.read();
|
|
29587
29690
|
const id = threadRowId(rows, threadKey);
|
|
29588
29691
|
if (id === void 0) return { ok: false, refused: "used" };
|
|
29589
|
-
const out =
|
|
29692
|
+
const out = judge3(rows, id, actorIds, this.clock(), "cancel");
|
|
29590
29693
|
this.write(rows);
|
|
29591
29694
|
return out;
|
|
29592
29695
|
}
|
|
@@ -30067,16 +30170,16 @@ async function reflect(deps) {
|
|
|
30067
30170
|
dropped.push(`${placement.from} (${placement.reason})`);
|
|
30068
30171
|
continue;
|
|
30069
30172
|
}
|
|
30070
|
-
let
|
|
30173
|
+
let record3 = plain7;
|
|
30071
30174
|
if (placement.narrowed) {
|
|
30072
|
-
const { supersedes: _supersedes, restates: _restates, ...withoutPointers } =
|
|
30073
|
-
|
|
30175
|
+
const { supersedes: _supersedes, restates: _restates, ...withoutPointers } = record3;
|
|
30176
|
+
record3 = withoutPointers;
|
|
30074
30177
|
const line4 = `${placement.narrowed.from} \u2192 ${placement.target.kind} (${placement.narrowed.reason})`;
|
|
30075
30178
|
narrowed.set(line4, (narrowed.get(line4) ?? 0) + 1);
|
|
30076
30179
|
}
|
|
30077
30180
|
let batch = byScope.get(placement.target.key);
|
|
30078
30181
|
if (!batch) byScope.set(placement.target.key, batch = []);
|
|
30079
|
-
batch.push(
|
|
30182
|
+
batch.push(record3);
|
|
30080
30183
|
}
|
|
30081
30184
|
if (narrowed.size > 0)
|
|
30082
30185
|
warn(`write narrowed by policy: ${[...narrowed].map(([line4, n2]) => `${n2}\xD7 ${line4}`).join(", ")}`);
|
|
@@ -31907,8 +32010,8 @@ ${line4}`
|
|
|
31907
32010
|
const list = await call(() => api.residents());
|
|
31908
32011
|
if (list.status !== 200) throw residentFailure(list);
|
|
31909
32012
|
const residents = list.data.residents ?? [];
|
|
31910
|
-
const
|
|
31911
|
-
if (!
|
|
32013
|
+
const record3 = residents.find((rec2) => rec2.resource === repoResourceId(args.slug));
|
|
32014
|
+
if (!record3) {
|
|
31912
32015
|
const slugs = residents.map(
|
|
31913
32016
|
(rec2) => String(rec2.resource ?? "").replace(/^repo:/, "").toLowerCase()
|
|
31914
32017
|
).filter(Boolean);
|
|
@@ -31928,7 +32031,7 @@ ${line4}`
|
|
|
31928
32031
|
${line4}` : ""}`
|
|
31929
32032
|
);
|
|
31930
32033
|
}
|
|
31931
|
-
body.commands = { ...obj(
|
|
32034
|
+
body.commands = { ...obj(record3.commands), ...commands };
|
|
31932
32035
|
}
|
|
31933
32036
|
if (options.ref !== void 0) body.defaultRef = options.ref;
|
|
31934
32037
|
const r = await call(() => api.reconfigure(body));
|
|
@@ -31953,11 +32056,11 @@ ${line4}` : ""}`
|
|
|
31953
32056
|
|
|
31954
32057
|
// ../../src/core/reviewAbridge.ts
|
|
31955
32058
|
import { resolve as resolve10 } from "node:path";
|
|
31956
|
-
function meatArtifactOf(
|
|
31957
|
-
return
|
|
32059
|
+
function meatArtifactOf(record3) {
|
|
32060
|
+
return record3.events.find((e) => isReadingDiff(e) && e.poweredBy === "meat");
|
|
31958
32061
|
}
|
|
31959
|
-
function gitArtifactOf(
|
|
31960
|
-
return
|
|
32062
|
+
function gitArtifactOf(record3) {
|
|
32063
|
+
return record3.events.find((e) => isReadingDiff(e) && e.poweredBy === "git");
|
|
31961
32064
|
}
|
|
31962
32065
|
function summarize(a) {
|
|
31963
32066
|
return {
|
|
@@ -31971,16 +32074,16 @@ function summarize(a) {
|
|
|
31971
32074
|
};
|
|
31972
32075
|
}
|
|
31973
32076
|
async function appendReviewArtifact(store, runId3, artifact, at) {
|
|
31974
|
-
const
|
|
31975
|
-
if (!
|
|
31976
|
-
const kept =
|
|
31977
|
-
const replaced = kept.length !==
|
|
31978
|
-
const seq = Math.max(0, ...storedEventSeqs(
|
|
32077
|
+
const record3 = await store.get(runId3);
|
|
32078
|
+
if (!record3) throw new Error("the run's record is gone \u2014 nothing to append to");
|
|
32079
|
+
const kept = record3.events.filter((e) => !(isReadingDiff(e) && e.poweredBy === "meat"));
|
|
32080
|
+
const replaced = kept.length !== record3.events.length;
|
|
32081
|
+
const seq = Math.max(0, ...storedEventSeqs(record3.events), ...record3.events.map((e) => e.seq ?? 0)) + 1;
|
|
31979
32082
|
const event = { type: "review_artifact", artifact: "reading_diff", ...artifact, at, seq };
|
|
31980
32083
|
const next = fitRecordToBudget({
|
|
31981
|
-
...
|
|
32084
|
+
...record3,
|
|
31982
32085
|
events: [...kept, event],
|
|
31983
|
-
eventCount:
|
|
32086
|
+
eventCount: record3.eventCount + (replaced ? 0 : 1),
|
|
31984
32087
|
storedEventCount: kept.length + 1
|
|
31985
32088
|
});
|
|
31986
32089
|
if (!next.events.includes(event))
|
|
@@ -32069,19 +32172,19 @@ var init_reviewAbridge = __esm({
|
|
|
32069
32172
|
async decide(req) {
|
|
32070
32173
|
const job = this.jobs.get(req.runId);
|
|
32071
32174
|
if (job) return { state: "running", startedAt: job.startedAt };
|
|
32072
|
-
const
|
|
32073
|
-
if (!
|
|
32074
|
-
const existing = meatArtifactOf(
|
|
32175
|
+
const record3 = await this.deps.store.get(req.runId);
|
|
32176
|
+
if (!record3) throw new AbridgeRefusal("not_found", "run not found");
|
|
32177
|
+
const existing = meatArtifactOf(record3);
|
|
32075
32178
|
if (existing && !req.force) return { state: "done", reused: true, artifact: summarize(existing) };
|
|
32076
32179
|
const failed = this.failures.get(req.runId);
|
|
32077
32180
|
if (failed && !req.force) return { state: "failed", ...failed };
|
|
32078
|
-
const git2 = gitArtifactOf(
|
|
32181
|
+
const git2 = gitArtifactOf(record3);
|
|
32079
32182
|
if (!git2)
|
|
32080
32183
|
throw new AbridgeRefusal("conflict", `run ${req.runId} carries no reading diff \u2014 only PR review runs record one`);
|
|
32081
32184
|
const startedAt = this.deps.clock();
|
|
32082
32185
|
const model = req.model ?? this.deps.defaultModel();
|
|
32083
32186
|
this.failures.delete(req.runId);
|
|
32084
|
-
const done = this.produce(
|
|
32187
|
+
const done = this.produce(record3, git2, model).finally(() => this.jobs.delete(req.runId));
|
|
32085
32188
|
this.jobs.set(req.runId, { startedAt, done });
|
|
32086
32189
|
return { state: "running", startedAt };
|
|
32087
32190
|
}
|
|
@@ -32091,8 +32194,8 @@ var init_reviewAbridge = __esm({
|
|
|
32091
32194
|
if (job) return { state: "running", startedAt: job.startedAt };
|
|
32092
32195
|
const failed = this.failures.get(runId3);
|
|
32093
32196
|
if (failed) return { state: "failed", ...failed };
|
|
32094
|
-
const
|
|
32095
|
-
const existing =
|
|
32197
|
+
const record3 = await this.deps.store.get(runId3);
|
|
32198
|
+
const existing = record3 ? meatArtifactOf(record3) : void 0;
|
|
32096
32199
|
return existing ? { state: "done", reused: true, artifact: summarize(existing) } : { state: "absent" };
|
|
32097
32200
|
}
|
|
32098
32201
|
/** The running job's final state, or the current state when none runs. */
|
|
@@ -32123,10 +32226,10 @@ var init_reviewAbridge = __esm({
|
|
|
32123
32226
|
if (!(err2 instanceof AbridgeRefusal)) this.deps.warn(`[reading-diff] ${runId3} abridge failed: ${describe(err2)}`);
|
|
32124
32227
|
}
|
|
32125
32228
|
}
|
|
32126
|
-
async produce(
|
|
32229
|
+
async produce(record3, git2, model) {
|
|
32127
32230
|
const at = () => this.deps.clock();
|
|
32128
32231
|
try {
|
|
32129
|
-
const input = await this.input(
|
|
32232
|
+
const input = await this.input(record3, git2);
|
|
32130
32233
|
const meat = await this.deps.meat({ diff: input.diff, model, timeoutMs: this.deps.timeoutMs() });
|
|
32131
32234
|
if (!meat.ok) throw new Error(meat.reason);
|
|
32132
32235
|
const capped4 = capDiff(sanitizeArtifactText(meat.result.diff));
|
|
@@ -32142,12 +32245,12 @@ var init_reviewAbridge = __esm({
|
|
|
32142
32245
|
inputBytes: input.bytes,
|
|
32143
32246
|
model
|
|
32144
32247
|
};
|
|
32145
|
-
const stored = await appendReviewArtifact(this.deps.store,
|
|
32146
|
-
return this.conclude(
|
|
32248
|
+
const stored = await appendReviewArtifact(this.deps.store, record3.id, artifact, at());
|
|
32249
|
+
return this.conclude(record3.id, { state: "done", reused: false, artifact: summarize(stored) });
|
|
32147
32250
|
} catch (err2) {
|
|
32148
32251
|
const failed = { reason: redactSecrets(describe(err2)), at: at() };
|
|
32149
|
-
this.failures.set(
|
|
32150
|
-
return this.conclude(
|
|
32252
|
+
this.failures.set(record3.id, failed);
|
|
32253
|
+
return this.conclude(record3.id, { state: "failed", ...failed });
|
|
32151
32254
|
}
|
|
32152
32255
|
}
|
|
32153
32256
|
conclude(runId3, state) {
|
|
@@ -32156,8 +32259,8 @@ var init_reviewAbridge = __esm({
|
|
|
32156
32259
|
}
|
|
32157
32260
|
/** The complete diff: GitHub's compare of base...head first; the recorded
|
|
32158
32261
|
* git diff only when that fails AND it is whole. */
|
|
32159
|
-
async input(
|
|
32160
|
-
const meta =
|
|
32262
|
+
async input(record3, git2) {
|
|
32263
|
+
const meta = record3.events.find((e) => e.type === "run_meta");
|
|
32161
32264
|
let compareFailure;
|
|
32162
32265
|
if (!meta?.repo || !meta.headSha) compareFailure = "the run records no repo and head to compare";
|
|
32163
32266
|
else {
|
|
@@ -32176,7 +32279,7 @@ var init_reviewAbridge = __esm({
|
|
|
32176
32279
|
}
|
|
32177
32280
|
}
|
|
32178
32281
|
if (git2.truncated) throw new Error(`diff unavailable: ${compareFailure} and the recorded diff is truncated`);
|
|
32179
|
-
this.deps.warn(`[reading-diff] ${
|
|
32282
|
+
this.deps.warn(`[reading-diff] ${record3.id} using the recorded diff: ${compareFailure}`);
|
|
32180
32283
|
return { diff: git2.diff, source: "recorded", bytes: Buffer.byteLength(git2.diff) };
|
|
32181
32284
|
}
|
|
32182
32285
|
};
|
|
@@ -32882,18 +32985,20 @@ function createRunsService(deps) {
|
|
|
32882
32985
|
try {
|
|
32883
32986
|
const row2 = await ledger.planeQueued(id);
|
|
32884
32987
|
if (!row2) return null;
|
|
32988
|
+
const stampedChannel = row2.request.channelId;
|
|
32885
32989
|
const channelAt = row2.threadKey.lastIndexOf(":");
|
|
32886
|
-
const
|
|
32990
|
+
const channelId = typeof stampedChannel === "string" ? stampedChannel : channelAt > 0 ? row2.threadKey.slice(0, channelAt) : void 0;
|
|
32991
|
+
const waiting3 = waitingWords(row2.conditions);
|
|
32887
32992
|
return {
|
|
32888
32993
|
id: row2.runId,
|
|
32889
|
-
...
|
|
32994
|
+
...channelId !== void 0 ? { channelId } : {},
|
|
32890
32995
|
userId: row2.requester,
|
|
32891
32996
|
threadKey: row2.threadKey,
|
|
32892
32997
|
startedAt: row2.queuedAt,
|
|
32893
32998
|
finished: false,
|
|
32894
32999
|
eventCount: 0,
|
|
32895
|
-
queued: { state: row2.state, position: row2.position, waiting:
|
|
32896
|
-
activity: row2.state === "waiting" ? `queued at position ${row2.position} \u2014 waiting on ${
|
|
33000
|
+
queued: { state: row2.state, position: row2.position, waiting: waiting3 },
|
|
33001
|
+
activity: row2.state === "waiting" ? `queued at position ${row2.position} \u2014 waiting on ${waiting3}` : row2.state === "admitted" ? "admitted \u2014 starting" : "withdrawn"
|
|
32897
33002
|
};
|
|
32898
33003
|
} catch (err2) {
|
|
32899
33004
|
warn(`[runs] plane queued read failed for ${id}: ${describe13(err2)}`);
|
|
@@ -32978,7 +33083,7 @@ function createRunsService(deps) {
|
|
|
32978
33083
|
if (row2) {
|
|
32979
33084
|
const finishedAt = snap?.finishedAt ?? at;
|
|
32980
33085
|
const m = row2.meta;
|
|
32981
|
-
const
|
|
33086
|
+
const record3 = assembleRunRecord({
|
|
32982
33087
|
run: { id, ...summary?.label !== void 0 ? { label: summary.label } : {} },
|
|
32983
33088
|
snap,
|
|
32984
33089
|
agent: m.agent ?? "ship",
|
|
@@ -33006,7 +33111,7 @@ function createRunsService(deps) {
|
|
|
33006
33111
|
}),
|
|
33007
33112
|
seal
|
|
33008
33113
|
});
|
|
33009
|
-
const done = await ledger.finish(id, row2.ownerGen,
|
|
33114
|
+
const done = await ledger.finish(id, row2.ownerGen, record3);
|
|
33010
33115
|
if (!done.ok) warn(`[runs] run ledger finish refused for ${id} (${done.reason ?? "unknown"})`);
|
|
33011
33116
|
}
|
|
33012
33117
|
} catch (err2) {
|
|
@@ -33104,12 +33209,12 @@ function createRunsService(deps) {
|
|
|
33104
33209
|
const queued = await queuedRun(id);
|
|
33105
33210
|
return queued ? { ok: true, value: queued } : notFound;
|
|
33106
33211
|
}
|
|
33107
|
-
const
|
|
33108
|
-
if (!
|
|
33212
|
+
const record3 = await storeGet(id);
|
|
33213
|
+
if (!record3) {
|
|
33109
33214
|
const queued = await queuedRun(id);
|
|
33110
33215
|
return queued ? { ok: true, value: queued } : notFound;
|
|
33111
33216
|
}
|
|
33112
|
-
const { events, ...rest } =
|
|
33217
|
+
const { events, ...rest } = record3;
|
|
33113
33218
|
const view = persistedView(rest, prices);
|
|
33114
33219
|
view.events = events;
|
|
33115
33220
|
return { ok: true, value: view };
|
|
@@ -34990,11 +35095,11 @@ var init_runLedgerWorker = __esm({
|
|
|
34990
35095
|
this.checkIds(runId3, gen);
|
|
34991
35096
|
return this.writeTurns(runId3, gen, turns, session);
|
|
34992
35097
|
}
|
|
34993
|
-
async step(runId3, gen,
|
|
35098
|
+
async step(runId3, gen, record3, turns, session) {
|
|
34994
35099
|
this.checkIds(runId3, gen);
|
|
34995
35100
|
const written = await this.writeTurns(runId3, gen, turns, session);
|
|
34996
35101
|
if (!written.ok) return written;
|
|
34997
|
-
return this.fenceResult(await this.post("/runs/step", { storeKey: this.opts.storeKey, runId: runId3, gen, record:
|
|
35102
|
+
return this.fenceResult(await this.post("/runs/step", { storeKey: this.opts.storeKey, runId: runId3, gen, record: record3 }));
|
|
34998
35103
|
}
|
|
34999
35104
|
checkSessionKey(key) {
|
|
35000
35105
|
if (!SESSION_KEY_PATTERN.test(key))
|
|
@@ -35165,20 +35270,20 @@ var init_runLedgerWorker = __esm({
|
|
|
35165
35270
|
this.checkIds(runId3, gen);
|
|
35166
35271
|
return this.fenceResult(await this.post("/runs/finishing", { storeKey: this.opts.storeKey, runId: runId3, gen }));
|
|
35167
35272
|
}
|
|
35168
|
-
async finish(runId3, gen,
|
|
35273
|
+
async finish(runId3, gen, record3) {
|
|
35169
35274
|
this.checkIds(runId3, gen);
|
|
35170
|
-
const point = pointOf(
|
|
35275
|
+
const point = pointOf(record3, this.opts.prices);
|
|
35171
35276
|
const r = await this.post("/runs/finish", {
|
|
35172
35277
|
storeKey: this.opts.storeKey,
|
|
35173
35278
|
runId: runId3,
|
|
35174
35279
|
gen,
|
|
35175
|
-
record:
|
|
35280
|
+
record: record3,
|
|
35176
35281
|
...point !== void 0 ? { point } : {}
|
|
35177
35282
|
});
|
|
35178
35283
|
const f = this.fenceResult(r);
|
|
35179
35284
|
if (!f.ok) return f;
|
|
35180
|
-
if (
|
|
35181
|
-
await this.post("/runs/session/clear-owner", { key:
|
|
35285
|
+
if (record3.session)
|
|
35286
|
+
await this.post("/runs/session/clear-owner", { key: record3.session.key, runId: runId3, gen }).catch(() => {
|
|
35182
35287
|
});
|
|
35183
35288
|
return { ok: true, stored: r.data.stored === true };
|
|
35184
35289
|
}
|
|
@@ -35877,6 +35982,19 @@ function runPageLink(id) {
|
|
|
35877
35982
|
if (!base) return void 0;
|
|
35878
35983
|
return `${base.replace(/\/+$/, "")}/runs/${encodeURIComponent(id)}`;
|
|
35879
35984
|
}
|
|
35985
|
+
function threadPageLink(threadKey, publicBaseUrl = process.env.PUBLIC_BASE_URL) {
|
|
35986
|
+
if (!publicBaseUrl?.trim()) return void 0;
|
|
35987
|
+
try {
|
|
35988
|
+
const base = new URL(publicBaseUrl.trim());
|
|
35989
|
+
if (!["https:", "http:"].includes(base.protocol) || base.username || base.password) return void 0;
|
|
35990
|
+
base.search = "";
|
|
35991
|
+
base.hash = "";
|
|
35992
|
+
base.pathname = `${base.pathname.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadKey).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)}`;
|
|
35993
|
+
return base.href;
|
|
35994
|
+
} catch {
|
|
35995
|
+
return void 0;
|
|
35996
|
+
}
|
|
35997
|
+
}
|
|
35880
35998
|
function artifactLink(id, key, token2) {
|
|
35881
35999
|
const page2 = runPageLink(id);
|
|
35882
36000
|
if (!page2) return void 0;
|
|
@@ -36019,7 +36137,7 @@ function cardActivity(e) {
|
|
|
36019
36137
|
if (e.type === "tool_call" && e.command !== void 0) return { kind: "command", tool: e.tool, command: e.command };
|
|
36020
36138
|
return { kind: "line", text: activityLine(e) };
|
|
36021
36139
|
}
|
|
36022
|
-
function
|
|
36140
|
+
function compactActivity(activity) {
|
|
36023
36141
|
if (activity?.kind === "command") return { kind: "line", text: `\u2192 ${activity.tool}` };
|
|
36024
36142
|
return activity;
|
|
36025
36143
|
}
|
|
@@ -36124,7 +36242,9 @@ function cardLines(trace, opts) {
|
|
|
36124
36242
|
});
|
|
36125
36243
|
return { ...shape ? { shape } : {}, ...opts.queued ? { queued: opts.queued } : {} };
|
|
36126
36244
|
}
|
|
36127
|
-
async function replyCommandOutput(io, parsed, text) {
|
|
36245
|
+
async function replyCommandOutput(io, parsed, text, display = {}) {
|
|
36246
|
+
if (parsed.kind === "invoke" && parsed.id === "steer.run" && display.ok === true && !shows(display.verbosity ?? "quiet", "verbose"))
|
|
36247
|
+
return;
|
|
36128
36248
|
if (!io.attach || text.length <= LONG_COMMAND_REPLY_CHARS) return io.reply(text);
|
|
36129
36249
|
const nl = text.indexOf("\n");
|
|
36130
36250
|
const lead = nl === -1 ? text : text.slice(0, nl);
|
|
@@ -36554,9 +36674,9 @@ function recordPrOf(ctx) {
|
|
|
36554
36674
|
}
|
|
36555
36675
|
function inheritedPr(thread, records, repo) {
|
|
36556
36676
|
const turn = thread.pr && thread.pr.repo === repo ? { repo, number: thread.pr.number, source: "turn", at: thread.prAt } : void 0;
|
|
36557
|
-
const
|
|
36558
|
-
if (!turn || !
|
|
36559
|
-
return turn.at !== void 0 &&
|
|
36677
|
+
const record3 = records?.pr && records.pr.repo === repo ? { repo, number: records.pr.number, source: "record", at: records.pr.at } : void 0;
|
|
36678
|
+
if (!turn || !record3) return turn ?? record3;
|
|
36679
|
+
return turn.at !== void 0 && record3.at !== void 0 && turn.at > record3.at ? turn : record3;
|
|
36560
36680
|
}
|
|
36561
36681
|
async function openPrHeadSha(pr) {
|
|
36562
36682
|
const head = await prHead(pr).catch(() => void 0);
|
|
@@ -37164,7 +37284,7 @@ async function classifyMove(fetchPrCommits, q) {
|
|
|
37164
37284
|
async function runReviewPostStep(input) {
|
|
37165
37285
|
const { agent, repoCtx, verdict, carried, logKey } = input;
|
|
37166
37286
|
const { reviewHead, observedHead } = input.heads;
|
|
37167
|
-
const
|
|
37287
|
+
const record3 = (outcome) => {
|
|
37168
37288
|
if (input.publish && agent.name === "review" && !input.hardStopped) {
|
|
37169
37289
|
const where = outcome.posted ? void 0 : repoCtx.repo && repoCtx.pr ? `${repoCtx.repo}#${repoCtx.pr}` : void 0;
|
|
37170
37290
|
input.publish(
|
|
@@ -37263,7 +37383,7 @@ ${carriedFooter(carried)}` : rendered;
|
|
|
37263
37383
|
console.error(`[review-post] ${logKey} failed for ${where}: ${reason2}`);
|
|
37264
37384
|
await input.reply(`\u2139\uFE0F Review not posted to ${where}: ${reason2} \u2014 this verdict is Slack-only.`).catch(() => {
|
|
37265
37385
|
});
|
|
37266
|
-
return
|
|
37386
|
+
return record3({ posted: false, reason: reason2 });
|
|
37267
37387
|
}
|
|
37268
37388
|
console.log(
|
|
37269
37389
|
`[review-post] ${logKey} \u2192 ${where} (${verdict?.verdict ?? "no verdict"})${carried ? ` carried ${carried.reviewed.slice(0, 7)} \u2192 ${pinned.slice(0, 7)}` : ""}`
|
|
@@ -37279,14 +37399,14 @@ ${carriedFooter(carried)}` : rendered;
|
|
|
37279
37399
|
await input.reply(moved).catch(() => {
|
|
37280
37400
|
});
|
|
37281
37401
|
}
|
|
37282
|
-
return
|
|
37402
|
+
return record3({
|
|
37283
37403
|
posted: true,
|
|
37284
37404
|
target: { repo: postTarget.repo, number: postTarget.number },
|
|
37285
37405
|
head: pinned,
|
|
37286
37406
|
...verdict !== void 0 ? { verdict: verdict.verdict } : {}
|
|
37287
37407
|
});
|
|
37288
37408
|
}
|
|
37289
|
-
return
|
|
37409
|
+
return record3({ posted: false, reason: skipReason });
|
|
37290
37410
|
}
|
|
37291
37411
|
var init_reviewRound = __esm({
|
|
37292
37412
|
"../../src/core/reviewRound.ts"() {
|
|
@@ -37643,11 +37763,11 @@ var init_authorize2 = __esm({
|
|
|
37643
37763
|
});
|
|
37644
37764
|
|
|
37645
37765
|
// ../../src/core/runHistoryWriter.ts
|
|
37646
|
-
async function putOnce(sink,
|
|
37766
|
+
async function putOnce(sink, record3) {
|
|
37647
37767
|
try {
|
|
37648
|
-
return await sink.put(
|
|
37768
|
+
return await sink.put(record3);
|
|
37649
37769
|
} catch (err2) {
|
|
37650
|
-
sink.abandoned(
|
|
37770
|
+
sink.abandoned(record3, describe2(err2));
|
|
37651
37771
|
throw err2;
|
|
37652
37772
|
}
|
|
37653
37773
|
}
|
|
@@ -37671,28 +37791,28 @@ function createRunHistoryWriter(opts) {
|
|
|
37671
37791
|
opts.warn(`[run-history] onPersisted hook failed for ${id}: ${describe2(err2)}`);
|
|
37672
37792
|
}
|
|
37673
37793
|
};
|
|
37674
|
-
const attemptAll = async (
|
|
37794
|
+
const attemptAll = async (record3, flag2, sink, span) => {
|
|
37675
37795
|
const attempts = RUN_HISTORY_RETRY_DELAYS_MS.length + 1;
|
|
37676
37796
|
const giveUp = async (why) => {
|
|
37677
37797
|
failures++;
|
|
37678
37798
|
try {
|
|
37679
|
-
sink.abandoned(
|
|
37799
|
+
sink.abandoned(record3, why);
|
|
37680
37800
|
} catch (err2) {
|
|
37681
|
-
opts.warn(`[run-history] abandoned hook failed for ${
|
|
37801
|
+
opts.warn(`[run-history] abandoned hook failed for ${record3.id}: ${describe2(err2)}`);
|
|
37682
37802
|
}
|
|
37683
37803
|
if (sink === opts.store || flag2?.superseded) return;
|
|
37684
37804
|
try {
|
|
37685
|
-
await opts.store.put(
|
|
37686
|
-
if (!flag2) persisted(
|
|
37805
|
+
await opts.store.put(record3, span ? { span } : void 0);
|
|
37806
|
+
if (!flag2) persisted(record3.id);
|
|
37687
37807
|
} catch (err2) {
|
|
37688
|
-
opts.warn(`[run-history] ${
|
|
37808
|
+
opts.warn(`[run-history] ${record3.id} store fallback after give-up failed: ${describe2(err2)}`);
|
|
37689
37809
|
}
|
|
37690
37810
|
};
|
|
37691
37811
|
for (let attempt = 1; ; attempt++) {
|
|
37692
37812
|
if (flag2?.superseded) return;
|
|
37693
37813
|
try {
|
|
37694
|
-
await sink.put(
|
|
37695
|
-
if (!flag2) persisted(
|
|
37814
|
+
await sink.put(record3, span ? { span } : void 0);
|
|
37815
|
+
if (!flag2) persisted(record3.id);
|
|
37696
37816
|
return;
|
|
37697
37817
|
} catch (err2) {
|
|
37698
37818
|
if (err2 instanceof RouteMissingError) {
|
|
@@ -37705,12 +37825,12 @@ function createRunHistoryWriter(opts) {
|
|
|
37705
37825
|
return;
|
|
37706
37826
|
}
|
|
37707
37827
|
if (err2 instanceof PermanentStoreError) {
|
|
37708
|
-
opts.warn(`[run-history] ${
|
|
37828
|
+
opts.warn(`[run-history] ${record3.id} not persisted (permanent, not retried): ${describe2(err2)}`);
|
|
37709
37829
|
await giveUp(describe2(err2));
|
|
37710
37830
|
return;
|
|
37711
37831
|
}
|
|
37712
37832
|
if (attempt >= attempts) {
|
|
37713
|
-
opts.warn(`[run-history] ${
|
|
37833
|
+
opts.warn(`[run-history] ${record3.id} not persisted after ${attempts} attempts: ${describe2(err2)}`);
|
|
37714
37834
|
await giveUp(`not persisted after ${attempts} attempts: ${describe2(err2)}`);
|
|
37715
37835
|
return;
|
|
37716
37836
|
}
|
|
@@ -37719,24 +37839,24 @@ function createRunHistoryWriter(opts) {
|
|
|
37719
37839
|
}
|
|
37720
37840
|
};
|
|
37721
37841
|
return {
|
|
37722
|
-
write(
|
|
37842
|
+
write(record3, writeOpts) {
|
|
37723
37843
|
let flag2;
|
|
37724
37844
|
if (writeOpts?.provisional === true) {
|
|
37725
|
-
if (finals.has(
|
|
37726
|
-
flag2 = { id:
|
|
37845
|
+
if (finals.has(record3.id)) return;
|
|
37846
|
+
flag2 = { id: record3.id, superseded: false };
|
|
37727
37847
|
provisionalFlags.add(flag2);
|
|
37728
37848
|
} else {
|
|
37729
|
-
finals.add(
|
|
37730
|
-
for (const f of provisionalFlags) if (f.id ===
|
|
37849
|
+
finals.add(record3.id);
|
|
37850
|
+
for (const f of provisionalFlags) if (f.id === record3.id) f.superseded = true;
|
|
37731
37851
|
}
|
|
37732
37852
|
const sink = writeOpts?.via ?? opts.store;
|
|
37733
|
-
const p = attemptAll(
|
|
37853
|
+
const p = attemptAll(record3, flag2, sink, writeOpts?.span).catch((err2) => {
|
|
37734
37854
|
failures++;
|
|
37735
|
-
opts.warn(`[run-history] ${
|
|
37855
|
+
opts.warn(`[run-history] ${record3.id} writer failed unexpectedly: ${describe2(err2)}`);
|
|
37736
37856
|
try {
|
|
37737
|
-
sink.abandoned(
|
|
37857
|
+
sink.abandoned(record3, `writer failed unexpectedly: ${describe2(err2)}`);
|
|
37738
37858
|
} catch (hookErr) {
|
|
37739
|
-
opts.warn(`[run-history] abandoned hook failed for ${
|
|
37859
|
+
opts.warn(`[run-history] abandoned hook failed for ${record3.id}: ${describe2(hookErr)}`);
|
|
37740
37860
|
}
|
|
37741
37861
|
}).finally(() => {
|
|
37742
37862
|
if (flag2) provisionalFlags.delete(flag2);
|
|
@@ -38099,12 +38219,12 @@ async function closeRestartRow(adopted, restart, why) {
|
|
|
38099
38219
|
async function closeResumedRow(adopted, resume, why, status3 = "interrupted", opts) {
|
|
38100
38220
|
try {
|
|
38101
38221
|
const finishedAt = (opts?.clock ?? systemClock)();
|
|
38102
|
-
const
|
|
38222
|
+
const record3 = {
|
|
38103
38223
|
...reclaimedRunRecord({ row: resume.row, events: resume.events, status: status3, finishedAt }),
|
|
38104
38224
|
...opts?.restarting === true ? { restarting: true, restartUntil: finishedAt + RESTART_CLAIM_GRACE_MS } : {}
|
|
38105
38225
|
};
|
|
38106
|
-
await putOnce(adopted.sink,
|
|
38107
|
-
return
|
|
38226
|
+
await putOnce(adopted.sink, record3);
|
|
38227
|
+
return record3;
|
|
38108
38228
|
} catch (err2) {
|
|
38109
38229
|
console.warn(
|
|
38110
38230
|
`[resume] ${resume.row.runId} could not be closed (${why}): ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
@@ -38669,6 +38789,79 @@ var init_commandChat = __esm({
|
|
|
38669
38789
|
}
|
|
38670
38790
|
});
|
|
38671
38791
|
|
|
38792
|
+
// ../../src/directives.ts
|
|
38793
|
+
function parseBudgetMinutes(value) {
|
|
38794
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
38795
|
+
const minutes = Number(value);
|
|
38796
|
+
return minutes >= MIN_BOUNDARY_MINUTES ? minutes : void 0;
|
|
38797
|
+
}
|
|
38798
|
+
function lastThreadDirectives(history) {
|
|
38799
|
+
const out = {};
|
|
38800
|
+
for (const h of history) {
|
|
38801
|
+
if (h.role !== "user") continue;
|
|
38802
|
+
for (const m of h.text.matchAll(DIRECTIVE_RE)) {
|
|
38803
|
+
if (m[1] === "model") out.model = m[2];
|
|
38804
|
+
else if (m[1] === "effort" && isEffort(m[2])) out.effort = m[2];
|
|
38805
|
+
else if (m[1] === "verbosity" && isVerbosity(m[2])) out.verbosity = m[2];
|
|
38806
|
+
}
|
|
38807
|
+
}
|
|
38808
|
+
return out;
|
|
38809
|
+
}
|
|
38810
|
+
function parseRenewals(value) {
|
|
38811
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
38812
|
+
const count = Number(value);
|
|
38813
|
+
return count <= GRANT_RENEWALS_MAX ? count : void 0;
|
|
38814
|
+
}
|
|
38815
|
+
function parseDirectives(input) {
|
|
38816
|
+
const out = { text: input };
|
|
38817
|
+
let text = input;
|
|
38818
|
+
const head = HEAD_AGENT_RE.exec(input);
|
|
38819
|
+
if (head !== null && AGENTS[head[1]] !== void 0) {
|
|
38820
|
+
out.agent = head[1];
|
|
38821
|
+
text = text.replace(head[0], " ");
|
|
38822
|
+
}
|
|
38823
|
+
for (const m of input.matchAll(DIRECTIVE_RE)) {
|
|
38824
|
+
const [token2, key, value] = m;
|
|
38825
|
+
if (key === "model") {
|
|
38826
|
+
out.model = value;
|
|
38827
|
+
} else if (key === "effort") {
|
|
38828
|
+
if (!isEffort(value)) continue;
|
|
38829
|
+
out.effort = value;
|
|
38830
|
+
} else if (key === "budget") {
|
|
38831
|
+
const minutes = parseBudgetMinutes(value);
|
|
38832
|
+
if (minutes === void 0) continue;
|
|
38833
|
+
out.budget = minutes;
|
|
38834
|
+
} else if (key === "severity") {
|
|
38835
|
+
if (!isAddressSeverity(value)) continue;
|
|
38836
|
+
out.severity = value;
|
|
38837
|
+
} else if (key === "renewals") {
|
|
38838
|
+
const count = parseRenewals(value);
|
|
38839
|
+
if (count === void 0) continue;
|
|
38840
|
+
out.renewals = count;
|
|
38841
|
+
} else {
|
|
38842
|
+
if (!isVerbosity(value)) continue;
|
|
38843
|
+
out.verbosity = value;
|
|
38844
|
+
}
|
|
38845
|
+
text = text.replace(token2, " ");
|
|
38846
|
+
}
|
|
38847
|
+
out.text = text.replace(/\s+/g, " ").trim();
|
|
38848
|
+
return out;
|
|
38849
|
+
}
|
|
38850
|
+
var DIRECTIVE_RE, HEAD_AGENT_RE;
|
|
38851
|
+
var init_directives = __esm({
|
|
38852
|
+
"../../src/directives.ts"() {
|
|
38853
|
+
"use strict";
|
|
38854
|
+
init_registry();
|
|
38855
|
+
init_validate();
|
|
38856
|
+
init_budgets();
|
|
38857
|
+
init_effort();
|
|
38858
|
+
init_shipPipeline();
|
|
38859
|
+
init_verbosity();
|
|
38860
|
+
DIRECTIVE_RE = /(?:^|\s)(model|effort|budget|severity|renewals|verbosity)[:=](\S+)/g;
|
|
38861
|
+
HEAD_AGENT_RE = /^\s*agent[:=](\S+)/;
|
|
38862
|
+
}
|
|
38863
|
+
});
|
|
38864
|
+
|
|
38672
38865
|
// ../../src/core/runRegistry/runControl.ts
|
|
38673
38866
|
var RunControl;
|
|
38674
38867
|
var init_runControl = __esm({
|
|
@@ -39668,6 +39861,7 @@ var init_commandRun = __esm({
|
|
|
39668
39861
|
async function answerChatCommand(deps, ctx) {
|
|
39669
39862
|
const { msg, io, ending, trace } = ctx;
|
|
39670
39863
|
const root = trace.root;
|
|
39864
|
+
const verbosity2 = deps.config.verbosityFor(msg.channelId, msg.userId, parseDirectives(msg.text).verbosity);
|
|
39671
39865
|
if (deps.commands) {
|
|
39672
39866
|
const chatCmd = parseChatCommand(msg.text, deps.commands);
|
|
39673
39867
|
if (chatCmd) {
|
|
@@ -39687,7 +39881,7 @@ async function answerChatCommand(deps, ctx) {
|
|
|
39687
39881
|
confirmations: deps.confirmations
|
|
39688
39882
|
})
|
|
39689
39883
|
);
|
|
39690
|
-
} : () => root.span("post.reply", () => replyCommandOutput(io, chatCmd, res.text));
|
|
39884
|
+
} : () => root.span("post.reply", () => replyCommandOutput(io, chatCmd, res.text, { verbosity: verbosity2, ok: res.ok }));
|
|
39691
39885
|
await ending.sealAfterReply(async () => {
|
|
39692
39886
|
}, replyFn);
|
|
39693
39887
|
if (res.followUp) postSettledOutcome(res.followUp, io, root);
|
|
@@ -39703,6 +39897,7 @@ var init_fastPath = __esm({
|
|
|
39703
39897
|
init_commandChat();
|
|
39704
39898
|
init_reply();
|
|
39705
39899
|
init_refusal();
|
|
39900
|
+
init_directives();
|
|
39706
39901
|
init_commandRun();
|
|
39707
39902
|
init_commandRun();
|
|
39708
39903
|
}
|
|
@@ -39996,79 +40191,6 @@ var init_references = __esm({
|
|
|
39996
40191
|
}
|
|
39997
40192
|
});
|
|
39998
40193
|
|
|
39999
|
-
// ../../src/directives.ts
|
|
40000
|
-
function parseBudgetMinutes(value) {
|
|
40001
|
-
if (!/^\d+$/.test(value)) return void 0;
|
|
40002
|
-
const minutes = Number(value);
|
|
40003
|
-
return minutes >= MIN_BOUNDARY_MINUTES ? minutes : void 0;
|
|
40004
|
-
}
|
|
40005
|
-
function lastThreadDirectives(history) {
|
|
40006
|
-
const out = {};
|
|
40007
|
-
for (const h of history) {
|
|
40008
|
-
if (h.role !== "user") continue;
|
|
40009
|
-
for (const m of h.text.matchAll(DIRECTIVE_RE)) {
|
|
40010
|
-
if (m[1] === "model") out.model = m[2];
|
|
40011
|
-
else if (m[1] === "effort" && isEffort(m[2])) out.effort = m[2];
|
|
40012
|
-
else if (m[1] === "verbosity" && isVerbosity(m[2])) out.verbosity = m[2];
|
|
40013
|
-
}
|
|
40014
|
-
}
|
|
40015
|
-
return out;
|
|
40016
|
-
}
|
|
40017
|
-
function parseRenewals(value) {
|
|
40018
|
-
if (!/^\d+$/.test(value)) return void 0;
|
|
40019
|
-
const count = Number(value);
|
|
40020
|
-
return count <= GRANT_RENEWALS_MAX ? count : void 0;
|
|
40021
|
-
}
|
|
40022
|
-
function parseDirectives(input) {
|
|
40023
|
-
const out = { text: input };
|
|
40024
|
-
let text = input;
|
|
40025
|
-
const head = HEAD_AGENT_RE.exec(input);
|
|
40026
|
-
if (head !== null && AGENTS[head[1]] !== void 0) {
|
|
40027
|
-
out.agent = head[1];
|
|
40028
|
-
text = text.replace(head[0], " ");
|
|
40029
|
-
}
|
|
40030
|
-
for (const m of input.matchAll(DIRECTIVE_RE)) {
|
|
40031
|
-
const [token2, key, value] = m;
|
|
40032
|
-
if (key === "model") {
|
|
40033
|
-
out.model = value;
|
|
40034
|
-
} else if (key === "effort") {
|
|
40035
|
-
if (!isEffort(value)) continue;
|
|
40036
|
-
out.effort = value;
|
|
40037
|
-
} else if (key === "budget") {
|
|
40038
|
-
const minutes = parseBudgetMinutes(value);
|
|
40039
|
-
if (minutes === void 0) continue;
|
|
40040
|
-
out.budget = minutes;
|
|
40041
|
-
} else if (key === "severity") {
|
|
40042
|
-
if (!isAddressSeverity(value)) continue;
|
|
40043
|
-
out.severity = value;
|
|
40044
|
-
} else if (key === "renewals") {
|
|
40045
|
-
const count = parseRenewals(value);
|
|
40046
|
-
if (count === void 0) continue;
|
|
40047
|
-
out.renewals = count;
|
|
40048
|
-
} else {
|
|
40049
|
-
if (!isVerbosity(value)) continue;
|
|
40050
|
-
out.verbosity = value;
|
|
40051
|
-
}
|
|
40052
|
-
text = text.replace(token2, " ");
|
|
40053
|
-
}
|
|
40054
|
-
out.text = text.replace(/\s+/g, " ").trim();
|
|
40055
|
-
return out;
|
|
40056
|
-
}
|
|
40057
|
-
var DIRECTIVE_RE, HEAD_AGENT_RE;
|
|
40058
|
-
var init_directives = __esm({
|
|
40059
|
-
"../../src/directives.ts"() {
|
|
40060
|
-
"use strict";
|
|
40061
|
-
init_registry();
|
|
40062
|
-
init_validate();
|
|
40063
|
-
init_budgets();
|
|
40064
|
-
init_effort();
|
|
40065
|
-
init_shipPipeline();
|
|
40066
|
-
init_verbosity();
|
|
40067
|
-
DIRECTIVE_RE = /(?:^|\s)(model|effort|budget|severity|renewals|verbosity)[:=](\S+)/g;
|
|
40068
|
-
HEAD_AGENT_RE = /^\s*agent[:=](\S+)/;
|
|
40069
|
-
}
|
|
40070
|
-
});
|
|
40071
|
-
|
|
40072
40194
|
// ../../src/execution/githubRepoProbe.ts
|
|
40073
40195
|
function githubRepoProbe(opts) {
|
|
40074
40196
|
const fetchImpl = opts.fetch ?? fetch;
|
|
@@ -40521,11 +40643,13 @@ async function ownerOf2(runs, unitsOf, threadKey) {
|
|
|
40521
40643
|
const units = await unitsOf(instanceId).catch(() => []);
|
|
40522
40644
|
const unit = units.find((u) => u.threadKey === threadKey && u.ending === void 0);
|
|
40523
40645
|
if (unit !== void 0) return { kind: "unit", instanceId, unit };
|
|
40524
|
-
const ended = units.
|
|
40646
|
+
const ended = units.filter(
|
|
40525
40647
|
(u) => u.threadKey === threadKey && u.ending !== void 0 && u.ending.kind !== "merged" && u.ending.kind !== "already_landed"
|
|
40526
40648
|
);
|
|
40527
40649
|
const ship = runs.find((r) => r.instanceId === instanceId && r.finished);
|
|
40528
|
-
if (ended
|
|
40650
|
+
if (ended.length === 1 && ship !== void 0) return { kind: "pipeline", instanceId, run: ship, unit: ended[0] };
|
|
40651
|
+
if (ended.length > 1 && ship !== void 0)
|
|
40652
|
+
return { kind: "pipeline_ambiguous", instanceId, run: ship, units: ended };
|
|
40529
40653
|
}
|
|
40530
40654
|
const agent = stickyAgentOf(runs);
|
|
40531
40655
|
if (agent !== void 0) return { kind: "session", agent };
|
|
@@ -40736,8 +40860,9 @@ function ownedProjection(p) {
|
|
|
40736
40860
|
function ownerNote(owner) {
|
|
40737
40861
|
const who2 = owner.kind === "live" ? `a live run${owner.runId !== void 0 ? ` (\`${owner.runId}\`)` : ""}` : owner.kind === "unit" ? `the unfinished plan unit ${owner.unit}` : `the ended pipeline for unmerged plan unit ${owner.unit}`;
|
|
40738
40862
|
const steer = owner.kind === "live" && owner.runId !== void 0 ? ` bind \`steer run ${owner.runId} <words>\` to deliver it,` : "";
|
|
40739
|
-
|
|
40740
|
-
|
|
40863
|
+
if (owner.kind === "pipeline")
|
|
40864
|
+
return `This thread is owned by ${who2}: the request below is a continuation of that durable task. Read tools may ground the decision, but every action folds into the owner so the current pull request is re-read and the pipeline resumes; an informational command or question is not fulfillment.`;
|
|
40865
|
+
return `This thread is owned by ${who2}: the request below is a follow-up for that owner. To act on it,${steer} bind a read command, or ask a question. Any other decision \u2014 a refusal, a preset, a write \u2014 folds the whole message into the owner unchanged and posts no answer.`;
|
|
40741
40866
|
}
|
|
40742
40867
|
function operatorProjection(input) {
|
|
40743
40868
|
return {
|
|
@@ -41122,14 +41247,13 @@ function runnableProposal(proposal, ctx) {
|
|
|
41122
41247
|
return (ctx.repositories?.length ?? 0) > 0 || /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+|(?:^|\s)[\w.-]+\/[\w.-]+(?:\s|[:#]|$)/i.test(request2);
|
|
41123
41248
|
}
|
|
41124
41249
|
function ownedDecisionRuns(event, owner, commands) {
|
|
41125
|
-
if (event.outcome !== "binds") return false;
|
|
41250
|
+
if (owner.kind === "pipeline" || event.outcome !== "binds") return false;
|
|
41126
41251
|
return (event.binds ?? []).every((bind) => {
|
|
41127
41252
|
const parsed = commands ? parseChatCommand(bind.line, commands) : null;
|
|
41128
41253
|
if (parsed?.kind !== "invoke") return false;
|
|
41129
41254
|
const def = commands.list().find((c) => c.id === parsed.id);
|
|
41130
41255
|
if (!def) return false;
|
|
41131
|
-
if (def.id === "steer.run")
|
|
41132
|
-
return owner.kind !== "pipeline" && !(owner.kind === "live" && owner.runId === void 0);
|
|
41256
|
+
if (def.id === "steer.run") return !(owner.kind === "live" && owner.runId === void 0);
|
|
41133
41257
|
return boundBlastRadius(def, parsed.input) === "read";
|
|
41134
41258
|
});
|
|
41135
41259
|
}
|
|
@@ -41447,11 +41571,10 @@ function presetRequestOf(line4) {
|
|
|
41447
41571
|
async function executeOperatorDecision(deps, ctx) {
|
|
41448
41572
|
const { event, io, msg } = ctx;
|
|
41449
41573
|
const surface = io.offer ? "chat" : "typed";
|
|
41450
|
-
const
|
|
41451
|
-
|
|
41452
|
-
"verbose"
|
|
41453
|
-
);
|
|
41574
|
+
const verbosity2 = deps.config.verbosityFor(msg.channelId, msg.userId, parseDirectives(msg.text).verbosity);
|
|
41575
|
+
const verbose = shows(verbosity2, "verbose");
|
|
41454
41576
|
const answered = { kind: "answered" };
|
|
41577
|
+
if (ctx.owner?.kind === "pipeline") return { kind: "fold" };
|
|
41455
41578
|
if (event.outcome === "question") {
|
|
41456
41579
|
await io.reply(event.question ?? "");
|
|
41457
41580
|
await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
|
|
@@ -41556,7 +41679,7 @@ async function executeOperatorDecision(deps, ctx) {
|
|
|
41556
41679
|
if (verbose) await io.reply(receipt);
|
|
41557
41680
|
const res = await runChatCommand(deps, msg, io, invocation, ctx.ending, ctx.trace, { operator: executedEvent });
|
|
41558
41681
|
carried = true;
|
|
41559
|
-
if (res.text.length > 0) await io.
|
|
41682
|
+
if (res.text.length > 0) await replyCommandOutput(io, invocation, res.text, { verbosity: verbosity2, ok: res.ok });
|
|
41560
41683
|
}
|
|
41561
41684
|
if (!carried) await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
|
|
41562
41685
|
return answered;
|
|
@@ -41811,21 +41934,21 @@ function parseSkillMarkdown(raw) {
|
|
|
41811
41934
|
if (!fm || typeof fm !== "object" || Array.isArray(fm)) {
|
|
41812
41935
|
throw new Error("skill frontmatter is not a YAML mapping");
|
|
41813
41936
|
}
|
|
41814
|
-
const
|
|
41815
|
-
const name =
|
|
41937
|
+
const record3 = fm;
|
|
41938
|
+
const name = record3.name;
|
|
41816
41939
|
if (typeof name !== "string" || name.trim() === "") {
|
|
41817
41940
|
throw new Error("skill frontmatter is missing a non-empty string `name`");
|
|
41818
41941
|
}
|
|
41819
|
-
const description =
|
|
41942
|
+
const description = record3.description;
|
|
41820
41943
|
if (typeof description !== "string" || description.trim() === "") {
|
|
41821
41944
|
throw new Error(`skill "${name}" frontmatter is missing a non-empty string \`description\``);
|
|
41822
41945
|
}
|
|
41823
|
-
const agents =
|
|
41946
|
+
const agents = record3.agents;
|
|
41824
41947
|
if (!Array.isArray(agents) || agents.length === 0 || !agents.every((a) => typeof a === "string" && a.trim() !== "")) {
|
|
41825
41948
|
throw new Error(`skill "${name}" frontmatter needs a non-empty \`agents\` array of strings (e.g. [review])`);
|
|
41826
41949
|
}
|
|
41827
|
-
const source2 = typeof
|
|
41828
|
-
const upstream = parseUpstream(
|
|
41950
|
+
const source2 = typeof record3.source === "string" ? record3.source.trim() : void 0;
|
|
41951
|
+
const upstream = parseUpstream(record3.upstream, name);
|
|
41829
41952
|
return {
|
|
41830
41953
|
name: name.trim(),
|
|
41831
41954
|
description: description.trim(),
|
|
@@ -42235,7 +42358,7 @@ async function openAckCard(deps, ctx) {
|
|
|
42235
42358
|
});
|
|
42236
42359
|
if (route2) shell.note("debug", routeReasonLabel(route2.reason, route2.collapsed));
|
|
42237
42360
|
const card = coalesceStatus(
|
|
42238
|
-
await root.span("dispatch.ack_card", () => io.status(shell.ack())),
|
|
42361
|
+
await root.span("dispatch.ack_card", () => io.status(shell.ack(), { verbosity: resolved.verbosity })),
|
|
42239
42362
|
deps.statusUpdateMinMs ?? STATUS_UPDATE_MIN_MS
|
|
42240
42363
|
);
|
|
42241
42364
|
trace.bindCard({ setupLabel: (label) => shell.setSetupLabel(label) });
|
|
@@ -43962,7 +44085,7 @@ function watchedChild(io, on) {
|
|
|
43962
44085
|
on.replied(text);
|
|
43963
44086
|
await io.reply(text);
|
|
43964
44087
|
},
|
|
43965
|
-
status: (initial) => io.status(initial),
|
|
44088
|
+
status: (initial, display) => io.status(initial, display),
|
|
43966
44089
|
history: () => io.history(),
|
|
43967
44090
|
runStarted: (started) => {
|
|
43968
44091
|
io.runStarted?.(started);
|
|
@@ -45038,9 +45161,13 @@ function mapStringLeaves(value, fn) {
|
|
|
45038
45161
|
}
|
|
45039
45162
|
return value;
|
|
45040
45163
|
}
|
|
45164
|
+
function requesterText(text) {
|
|
45165
|
+
return text.trim().replace(/\s+/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/[\\`*_{}[\]()#!|~]/g, "\\$&").replace(/@/g, "@");
|
|
45166
|
+
}
|
|
45041
45167
|
function requestedByLine(by) {
|
|
45042
|
-
const steered = by.steeredBy !== void 0 && by.steeredBy.length > 0 ? `; steered by ${by.steeredBy.join(", ")}` : "";
|
|
45043
|
-
|
|
45168
|
+
const steered = by.steeredBy !== void 0 && by.steeredBy.length > 0 ? `; steered by ${by.steeredBy.map(requesterText).join(", ")}` : "";
|
|
45169
|
+
const thread = by.threadUrl ? ` \xB7 [Thread](${by.threadUrl})` : "";
|
|
45170
|
+
return `Requested by **${requesterText(by.name) || "Unknown requester"}**${thread}${steered}`;
|
|
45044
45171
|
}
|
|
45045
45172
|
function encodeGithubPathSegments(path) {
|
|
45046
45173
|
return path.split("/").map(encodeURIComponent).join("/");
|
|
@@ -45063,6 +45190,7 @@ function renderPrDescriptionMarkdown(desc, ctx) {
|
|
|
45063
45190
|
if (!SHA_RE2.test(ctx.headSha))
|
|
45064
45191
|
throw new Error(`renderPrDescriptionMarkdown: headSha must be a full 40-char lowercase sha, got "${ctx.headSha}"`);
|
|
45065
45192
|
const out = [];
|
|
45193
|
+
if (ctx.requestedBy) out.push(requestedByLine(ctx.requestedBy), "");
|
|
45066
45194
|
out.push(desc.tldr, "");
|
|
45067
45195
|
out.push(`${MAP_LABELS.why} ${desc.why}`, "");
|
|
45068
45196
|
out.push(MAP_LABELS.whereToLook, "");
|
|
@@ -45089,10 +45217,7 @@ function renderPrDescriptionMarkdown(desc, ctx) {
|
|
|
45089
45217
|
...desc.validation.criteria.map((c) => `| ${cell2(c.criterion)} | ${cell2(c.proof)} |`)
|
|
45090
45218
|
])
|
|
45091
45219
|
);
|
|
45092
|
-
const agentLines = [
|
|
45093
|
-
...desc.agentNotes ? [desc.agentNotes] : [],
|
|
45094
|
-
...ctx.requestedBy ? [requestedByLine(ctx.requestedBy)] : []
|
|
45095
|
-
];
|
|
45220
|
+
const agentLines = desc.agentNotes ? [desc.agentNotes] : [];
|
|
45096
45221
|
if (agentLines.length > 0) out.push(...details(FOLD_SUMMARIES.agents, agentLines));
|
|
45097
45222
|
out.push(GENERATED_FOOTER, "");
|
|
45098
45223
|
return out.join("\n");
|
|
@@ -45100,6 +45225,8 @@ function renderPrDescriptionMarkdown(desc, ctx) {
|
|
|
45100
45225
|
function parsePrDescriptionMarkdown(body) {
|
|
45101
45226
|
const problems = [];
|
|
45102
45227
|
const lines = body.replace(/\r\n?/g, "\n").split("\n").filter((l) => l.trim() !== GENERATED_FOOTER);
|
|
45228
|
+
if (/^Requested by \*\*(?:\\.|[^*\\])+\*\*(?: · \[Thread\]\([^\s)]+\))?(?:; steered by .*)?$/.test(lines[0] ?? ""))
|
|
45229
|
+
lines.shift();
|
|
45103
45230
|
const fenced = fenceMask(lines);
|
|
45104
45231
|
const description = { pointers: [] };
|
|
45105
45232
|
if (lines.some((l, i) => !fenced[i] && /^##\s+Tour\s*$/i.test(l))) {
|
|
@@ -46043,6 +46170,7 @@ async function claimRun(deps, ctx) {
|
|
|
46043
46170
|
parentInstanceId: coordinator.parentInstanceId,
|
|
46044
46171
|
...unit !== void 0 ? { unit } : {},
|
|
46045
46172
|
...coordinator.base !== void 0 ? { base: coordinator.base } : {},
|
|
46173
|
+
...coordinator.publication !== void 0 ? { publication: coordinator.publication } : {},
|
|
46046
46174
|
at: clock4()
|
|
46047
46175
|
});
|
|
46048
46176
|
}
|
|
@@ -46300,12 +46428,12 @@ var init_contract4 = __esm({
|
|
|
46300
46428
|
};
|
|
46301
46429
|
RELAUNCH_CEILING = 2;
|
|
46302
46430
|
HarnessContainerReplacedError = class extends HarnessInterruptedError {
|
|
46303
|
-
constructor(message4, said2, was, now,
|
|
46431
|
+
constructor(message4, said2, was, now, record3, condition = "word") {
|
|
46304
46432
|
super(message4, "container replaced under the run", "container_replaced");
|
|
46305
46433
|
this.said = said2;
|
|
46306
46434
|
this.was = was;
|
|
46307
46435
|
this.now = now;
|
|
46308
|
-
this.record =
|
|
46436
|
+
this.record = record3;
|
|
46309
46437
|
this.condition = condition;
|
|
46310
46438
|
this.name = "HarnessContainerReplacedError";
|
|
46311
46439
|
if (condition !== "identity" !== (said2 !== void 0))
|
|
@@ -46392,11 +46520,11 @@ async function prepareRelaunch(deps, ctx) {
|
|
|
46392
46520
|
} else {
|
|
46393
46521
|
ctx.saveFacts(written);
|
|
46394
46522
|
}
|
|
46395
|
-
const { deadline, ...
|
|
46523
|
+
const { deadline, ...record3 } = replaced.record;
|
|
46396
46524
|
return {
|
|
46397
46525
|
kind: "relaunch",
|
|
46398
46526
|
resume: {
|
|
46399
|
-
...
|
|
46527
|
+
...record3,
|
|
46400
46528
|
remainingMs: Math.max(0, deadline - ctx.clock()),
|
|
46401
46529
|
facts: written,
|
|
46402
46530
|
relaunch: {
|
|
@@ -47325,7 +47453,7 @@ function established(facts2) {
|
|
|
47325
47453
|
else if (w?.kind === "salvaged")
|
|
47326
47454
|
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.`;
|
|
47327
47455
|
else if (w?.kind === "left")
|
|
47328
|
-
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.`;
|
|
47456
|
+
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.`;
|
|
47329
47457
|
const description = facts2?.description === "submitted" ? "The PR description was submitted." : facts2?.description === "not_submitted" ? "No PR description was submitted." : "";
|
|
47330
47458
|
return [tree, description].filter((s) => s !== "").join(" ");
|
|
47331
47459
|
}
|
|
@@ -47559,7 +47687,7 @@ var init_runBearers = __esm({
|
|
|
47559
47687
|
* after `record` returned would carry the old hash ahead of the new one.
|
|
47560
47688
|
* Refuses by name the runs `issue` mints nothing for, and hands `record`
|
|
47561
47689
|
* nothing then. */
|
|
47562
|
-
rotate(runId3,
|
|
47690
|
+
rotate(runId3, record3) {
|
|
47563
47691
|
const entry = this.entries.get(runId3);
|
|
47564
47692
|
if (!entry) return { ok: false, reason: "unknown_run" };
|
|
47565
47693
|
if (entry.revoked) return { ok: false, reason: "revoked" };
|
|
@@ -47567,7 +47695,7 @@ var init_runBearers = __esm({
|
|
|
47567
47695
|
const secret2 = randomBytes4(SECRET_BYTES);
|
|
47568
47696
|
const hash = hashOf(secret2);
|
|
47569
47697
|
entry.hashes.push(hash);
|
|
47570
|
-
|
|
47698
|
+
record3(hash.toString("hex"));
|
|
47571
47699
|
entry.hashes = [hash];
|
|
47572
47700
|
return { ok: true, token: token(runId3, secret2), expiresAt: entry.grant.expiresAt };
|
|
47573
47701
|
}
|
|
@@ -49370,6 +49498,82 @@ var init_process = __esm({
|
|
|
49370
49498
|
}
|
|
49371
49499
|
});
|
|
49372
49500
|
|
|
49501
|
+
// ../../src/core/providerToolSchemas.ts
|
|
49502
|
+
function responsesRejectsPattern(pattern) {
|
|
49503
|
+
return /\(\?(?:[=!]|<[=!])/.test(pattern);
|
|
49504
|
+
}
|
|
49505
|
+
function shapeResponsesSchema(value, tool, degradations) {
|
|
49506
|
+
const schema = record(value);
|
|
49507
|
+
if (!schema) {
|
|
49508
|
+
if (!Array.isArray(value)) return { value, changed: false };
|
|
49509
|
+
let changed2 = false;
|
|
49510
|
+
const items = value.map((item) => {
|
|
49511
|
+
const shaped = shapeResponsesSchema(item, tool, degradations);
|
|
49512
|
+
changed2 ||= shaped.changed;
|
|
49513
|
+
return shaped.value;
|
|
49514
|
+
});
|
|
49515
|
+
return { value: changed2 ? items : value, changed: changed2 };
|
|
49516
|
+
}
|
|
49517
|
+
let changed = false;
|
|
49518
|
+
const out = {};
|
|
49519
|
+
for (const [keyword, item] of Object.entries(schema)) {
|
|
49520
|
+
if (keyword === "pattern" && typeof item === "string" && responsesRejectsPattern(item)) {
|
|
49521
|
+
degradations.push({
|
|
49522
|
+
tool,
|
|
49523
|
+
keyword,
|
|
49524
|
+
why: "the Responses wire does not accept regular-expression lookaround"
|
|
49525
|
+
});
|
|
49526
|
+
changed = true;
|
|
49527
|
+
continue;
|
|
49528
|
+
}
|
|
49529
|
+
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record(item)) {
|
|
49530
|
+
let mapChanged = false;
|
|
49531
|
+
const mapped = {};
|
|
49532
|
+
for (const [name, child] of Object.entries(record(item))) {
|
|
49533
|
+
const shaped = shapeResponsesSchema(child, tool, degradations);
|
|
49534
|
+
mapChanged ||= shaped.changed;
|
|
49535
|
+
mapped[name] = shaped.value;
|
|
49536
|
+
}
|
|
49537
|
+
out[keyword] = mapChanged ? mapped : item;
|
|
49538
|
+
changed ||= mapChanged;
|
|
49539
|
+
continue;
|
|
49540
|
+
}
|
|
49541
|
+
if (["additionalProperties", "allOf", "anyOf", "if", "items", "not", "oneOf", "then", "else"].includes(keyword) && typeof item === "object" && item !== null) {
|
|
49542
|
+
const shaped = shapeResponsesSchema(item, tool, degradations);
|
|
49543
|
+
out[keyword] = shaped.value;
|
|
49544
|
+
changed ||= shaped.changed;
|
|
49545
|
+
continue;
|
|
49546
|
+
}
|
|
49547
|
+
out[keyword] = item;
|
|
49548
|
+
}
|
|
49549
|
+
return { value: changed ? out : value, changed };
|
|
49550
|
+
}
|
|
49551
|
+
function shapeToolSchemasForWire(shape, body) {
|
|
49552
|
+
if (shape !== "openai-responses" || !Array.isArray(body.tools)) return { body, degradations: [] };
|
|
49553
|
+
const degradations = [];
|
|
49554
|
+
let changed = false;
|
|
49555
|
+
const tools = body.tools.map((item) => {
|
|
49556
|
+
const tool = record(item);
|
|
49557
|
+
if (!tool) return item;
|
|
49558
|
+
const name = typeof tool.name === "string" ? tool.name : "unnamed";
|
|
49559
|
+
const shaped = shapeResponsesSchema(tool.parameters, name, degradations);
|
|
49560
|
+
if (!shaped.changed) return item;
|
|
49561
|
+
changed = true;
|
|
49562
|
+
return { ...tool, parameters: shaped.value };
|
|
49563
|
+
});
|
|
49564
|
+
const unique = degradations.filter(
|
|
49565
|
+
(candidate, index, all) => all.findIndex((other) => other.tool === candidate.tool && other.keyword === candidate.keyword) === index
|
|
49566
|
+
);
|
|
49567
|
+
return { body: changed ? { ...body, tools } : body, degradations: unique };
|
|
49568
|
+
}
|
|
49569
|
+
var record;
|
|
49570
|
+
var init_providerToolSchemas = __esm({
|
|
49571
|
+
"../../src/core/providerToolSchemas.ts"() {
|
|
49572
|
+
"use strict";
|
|
49573
|
+
record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
49574
|
+
}
|
|
49575
|
+
});
|
|
49576
|
+
|
|
49373
49577
|
// ../../src/core/harness/piAi.ts
|
|
49374
49578
|
import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
|
|
49375
49579
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
@@ -49537,6 +49741,7 @@ var init_piAi = __esm({
|
|
|
49537
49741
|
"../../src/core/harness/piAi.ts"() {
|
|
49538
49742
|
"use strict";
|
|
49539
49743
|
init_provider();
|
|
49744
|
+
init_providerToolSchemas();
|
|
49540
49745
|
init_secrets2();
|
|
49541
49746
|
init_clock();
|
|
49542
49747
|
ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
@@ -49624,7 +49829,10 @@ var init_piAi = __esm({
|
|
|
49624
49829
|
...this.fetchImpl ? { fetch: this.fetchImpl } : {}
|
|
49625
49830
|
};
|
|
49626
49831
|
try {
|
|
49627
|
-
const
|
|
49832
|
+
const context = toPiContext(req, model, this.clock);
|
|
49833
|
+
const wire = this.api === "openai-completions" ? "openai-chat" : this.api;
|
|
49834
|
+
const shaped = shapeToolSchemasForWire(wire, context);
|
|
49835
|
+
const message4 = await this.api$().stream(model, shaped.body, options).result();
|
|
49628
49836
|
return fromPiMessage(message4, this.name, req.tools);
|
|
49629
49837
|
} catch (err2) {
|
|
49630
49838
|
const failure2 = providerFailureOf(err2);
|
|
@@ -49778,10 +49986,28 @@ function judgeBashTimeout(timeout, ctx) {
|
|
|
49778
49986
|
return refused4(commandPastLoopEndRefusal(Math.round(timeout), Math.max(0, Math.floor(leftMs / 1e3))));
|
|
49779
49987
|
}
|
|
49780
49988
|
function judgePush(tail, ctx) {
|
|
49781
|
-
const [remote, refspec] = pushArguments(tail);
|
|
49989
|
+
const [remote, refspec, ...additionalRefspecs] = pushArguments(tail);
|
|
49782
49990
|
if (remote !== void 0 && remote !== "origin") {
|
|
49783
49991
|
return refused4(`repo:use \u2014 push to remote \`${remote}\`, not the run's repository (origin)`);
|
|
49784
49992
|
}
|
|
49993
|
+
if (ctx.publication !== void 0) {
|
|
49994
|
+
const publication = ctx.publication.authority;
|
|
49995
|
+
if ("blocked" in publication) return refused4(`repo:use \u2014 existing-PR publication blocked: ${publication.blocked}`);
|
|
49996
|
+
if (refspec === void 0)
|
|
49997
|
+
return refused4("repo:use \u2014 existing-PR publication requires the explicit owned destination");
|
|
49998
|
+
if (additionalRefspecs.length > 0)
|
|
49999
|
+
return refused4("repo:use \u2014 existing-PR publication allows exactly one owned destination");
|
|
50000
|
+
const destination2 = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
|
|
50001
|
+
const branch3 = destination2 === "HEAD" ? ctx.branch : destination2.replace(/^refs\/heads\//, "");
|
|
50002
|
+
if (branch3 !== publication.ref)
|
|
50003
|
+
return refused4(`repo:use \u2014 push to \`${branch3}\`, not the owned publication ref ${publication.ref}`);
|
|
50004
|
+
const lease = `--force-with-lease=refs/heads/${publication.ref}:${publication.expectedHeadSha}`;
|
|
50005
|
+
if (!tail.split(/\s+/).includes(lease))
|
|
50006
|
+
return refused4(
|
|
50007
|
+
`repo:use \u2014 existing-PR publication requires \`${lease}\` so concurrent movement fails atomically`
|
|
50008
|
+
);
|
|
50009
|
+
return allowed;
|
|
50010
|
+
}
|
|
49785
50011
|
if (refspec === void 0) return allowed;
|
|
49786
50012
|
const destination = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
|
|
49787
50013
|
if (ctx.branch !== void 0) {
|
|
@@ -49901,7 +50127,7 @@ function relayedToolDefinitions(harness) {
|
|
|
49901
50127
|
}
|
|
49902
50128
|
function authorizeToolCall(harness, ask) {
|
|
49903
50129
|
harness.gateSaw(ask.toolCallId);
|
|
49904
|
-
const
|
|
50130
|
+
const blocked2 = harness.toolsBlocked();
|
|
49905
50131
|
const refuse3 = (reason2) => {
|
|
49906
50132
|
harness.emit({
|
|
49907
50133
|
type: "run_note",
|
|
@@ -49910,7 +50136,7 @@ function authorizeToolCall(harness, ask) {
|
|
|
49910
50136
|
});
|
|
49911
50137
|
return { allow: false, reason: reason2 };
|
|
49912
50138
|
};
|
|
49913
|
-
if (
|
|
50139
|
+
if (blocked2 !== void 0) return refuse3(blocked2);
|
|
49914
50140
|
if (harness.tools.some((t) => t.name === ask.tool)) return { allow: true };
|
|
49915
50141
|
const verdict = judgeToolCall(ask.tool, ask.input, harness.rules);
|
|
49916
50142
|
if (verdict.verdict === "allowed") return { allow: true };
|
|
@@ -51263,8 +51489,8 @@ ${stagedLine}` : prompt;
|
|
|
51263
51489
|
return;
|
|
51264
51490
|
}
|
|
51265
51491
|
const reissued = inputs.filter((item) => item.userId === PLANE_ACTOR_ID && isReissueSteerText(item.text));
|
|
51266
|
-
const
|
|
51267
|
-
if (
|
|
51492
|
+
const waiting3 = reissued.length === 0 ? inputs : inputs.filter((item) => !reissued.includes(item));
|
|
51493
|
+
if (waiting3.length > 0) run2.inbox?.requeue(waiting3);
|
|
51268
51494
|
for (const item of reissued) {
|
|
51269
51495
|
emit({
|
|
51270
51496
|
type: "input",
|
|
@@ -51575,13 +51801,13 @@ var init_harness = __esm({
|
|
|
51575
51801
|
}
|
|
51576
51802
|
};
|
|
51577
51803
|
PiContainerReplacedError = class extends HarnessContainerReplacedError {
|
|
51578
|
-
constructor(said2, was, now,
|
|
51804
|
+
constructor(said2, was, now, record3, condition = "word") {
|
|
51579
51805
|
super(
|
|
51580
51806
|
condition === "transport" ? `the container running pi stopped answering (${was ?? "unknown"}; ${replacedBecause(condition, said2)})` : `the container running pi was replaced (${was ?? "unknown"} \u2192 ${now ?? "unknown"}; ${replacedBecause(condition, said2)})`,
|
|
51581
51807
|
said2,
|
|
51582
51808
|
was,
|
|
51583
51809
|
now,
|
|
51584
|
-
|
|
51810
|
+
record3,
|
|
51585
51811
|
condition
|
|
51586
51812
|
);
|
|
51587
51813
|
this.name = "PiContainerReplacedError";
|
|
@@ -51790,16 +52016,62 @@ var init_resume = __esm({
|
|
|
51790
52016
|
}
|
|
51791
52017
|
});
|
|
51792
52018
|
|
|
52019
|
+
// ../../src/core/existingPrPublication.ts
|
|
52020
|
+
function verifyExistingPrPublication(durable, local, remote) {
|
|
52021
|
+
if (durable === void 0) return blocked("the durable existing-PR binding is missing");
|
|
52022
|
+
if (!fullSha(durable.expectedHeadSha)) return blocked("the durable expected head is not a full commit SHA");
|
|
52023
|
+
if (durable.headRef !== durable.publicationRef)
|
|
52024
|
+
return blocked("the durable head ref and owned publication ref do not agree");
|
|
52025
|
+
if (local.repo !== durable.repo) return blocked("the resolved repository does not match the durable binding");
|
|
52026
|
+
if (local.pr !== durable.pr) return blocked("the resolved pull request does not match the durable binding");
|
|
52027
|
+
if (local.ref !== durable.headRef) return blocked("the resolved head ref does not match the durable binding");
|
|
52028
|
+
if (local.baseRef !== durable.baseRef) return blocked("the resolved base ref does not match the durable binding");
|
|
52029
|
+
if (!fullSha(local.requestHeadSha)) return blocked("the request head is missing or not a full commit SHA");
|
|
52030
|
+
if (local.requestHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52031
|
+
return blocked("the request head does not match the durable expected commit");
|
|
52032
|
+
if (local.workspaceRef !== durable.publicationRef)
|
|
52033
|
+
return blocked("the checked-out publication ref does not match the durable binding");
|
|
52034
|
+
if (!fullSha(local.workspaceHeadSha)) return blocked("the checked-out head is missing or not a full commit SHA");
|
|
52035
|
+
if (local.workspaceHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52036
|
+
return blocked("the checked-out head does not match the durable expected commit");
|
|
52037
|
+
if (!sameOwner(local.owner, durable.owner)) return blocked("the publication owner changed");
|
|
52038
|
+
if (remote === void 0) return blocked("the fresh pull-request read is unavailable");
|
|
52039
|
+
if (remote.state !== "open") return blocked("the bound pull request is no longer open");
|
|
52040
|
+
if (remote.sameRepoHead !== true) return blocked("the pull request head is not in the bound repository");
|
|
52041
|
+
if (remote.headBranchExists !== true) return blocked("the pull request head branch is missing or unknown");
|
|
52042
|
+
if (remote.headRef !== durable.headRef) return blocked("the pull request head ref moved");
|
|
52043
|
+
if (remote.baseRef !== durable.baseRef) return blocked("the pull request base ref moved");
|
|
52044
|
+
if (!fullSha(remote.headSha)) return blocked("the fresh pull-request head is missing or not a full commit SHA");
|
|
52045
|
+
if (remote.headSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52046
|
+
return blocked("the pull request head moved from the durable expected commit");
|
|
52047
|
+
return {
|
|
52048
|
+
ok: true,
|
|
52049
|
+
publication: { ref: durable.publicationRef, expectedHeadSha: durable.expectedHeadSha.toLowerCase() }
|
|
52050
|
+
};
|
|
52051
|
+
}
|
|
52052
|
+
var fullSha, sameOwner, blocked;
|
|
52053
|
+
var init_existingPrPublication = __esm({
|
|
52054
|
+
"../../src/core/existingPrPublication.ts"() {
|
|
52055
|
+
"use strict";
|
|
52056
|
+
fullSha = (value) => value !== void 0 && /^[0-9a-f]{40}$/i.test(value);
|
|
52057
|
+
sameOwner = (left, right) => left?.instanceId === right.instanceId && left.unit === right.unit;
|
|
52058
|
+
blocked = (reason2) => ({ ok: false, reason: reason2 });
|
|
52059
|
+
}
|
|
52060
|
+
});
|
|
52061
|
+
|
|
51793
52062
|
// ../../src/execution/githubComments.ts
|
|
51794
52063
|
async function postReviewComment(target, body) {
|
|
52064
|
+
const codePoints = [...body].length;
|
|
52065
|
+
if (codePoints > MAX_REVIEW_POST_CODE_POINTS) {
|
|
52066
|
+
throw new Error(
|
|
52067
|
+
`PR review body is ${codePoints} Unicode code points, over the ${MAX_REVIEW_POST_CODE_POINTS} limit; refusing to clip the structured verdict`
|
|
52068
|
+
);
|
|
52069
|
+
}
|
|
51795
52070
|
const token2 = await resolveGithubToken();
|
|
51796
52071
|
if (!token2) {
|
|
51797
52072
|
throw new Error("no GitHub credential available to post the PR review comment");
|
|
51798
52073
|
}
|
|
51799
|
-
const
|
|
51800
|
-
|
|
51801
|
-
_(review truncated to fit GitHub's comment size limit)_` : body;
|
|
51802
|
-
const payload2 = { event: "COMMENT", body: clipped };
|
|
52074
|
+
const payload2 = { event: "COMMENT", body };
|
|
51803
52075
|
if (target.commitId) payload2.commit_id = target.commitId;
|
|
51804
52076
|
const res = await fetch(`https://api.github.com/repos/${target.repo}/pulls/${target.number}/reviews`, {
|
|
51805
52077
|
method: "POST",
|
|
@@ -51817,13 +52089,12 @@ _(review truncated to fit GitHub's comment size limit)_` : body;
|
|
|
51817
52089
|
throw new Error(`PR comment post failed: HTTP ${res.status} ${redactAndCap(text, 300)}`);
|
|
51818
52090
|
}
|
|
51819
52091
|
}
|
|
51820
|
-
var MAX_COMMENT_CHARS;
|
|
51821
52092
|
var init_githubComments = __esm({
|
|
51822
52093
|
"../../src/execution/githubComments.ts"() {
|
|
51823
52094
|
"use strict";
|
|
51824
52095
|
init_githubApp();
|
|
51825
52096
|
init_redact();
|
|
51826
|
-
|
|
52097
|
+
init_reviewVerdict();
|
|
51827
52098
|
}
|
|
51828
52099
|
});
|
|
51829
52100
|
|
|
@@ -51875,13 +52146,13 @@ function parsedPrDescriptionArtifact(facts2, ctx) {
|
|
|
51875
52146
|
async function findSubmittedPrDescription(store, key, limit = SUBMITTED_LOOKUP_LIMIT) {
|
|
51876
52147
|
const rows = await store.list({ limit, visibleTo: { kind: "repos-in", repos: [key.repo] } });
|
|
51877
52148
|
for (const row2 of rows) {
|
|
51878
|
-
const
|
|
51879
|
-
if (!
|
|
51880
|
-
for (let i =
|
|
51881
|
-
const ev =
|
|
52149
|
+
const record3 = await store.get(row2.id);
|
|
52150
|
+
if (!record3) continue;
|
|
52151
|
+
for (let i = record3.events.length - 1; i >= 0; i--) {
|
|
52152
|
+
const ev = record3.events[i];
|
|
51882
52153
|
if (ev.type === "review_artifact" && ev.artifact === "pr_description" && ev.origin === "submitted" && ev.repo === key.repo && ev.pr === key.pr && ev.headSha === key.headSha) {
|
|
51883
52154
|
const { type: _type, seq: _seq, at: _at, ...artifact } = ev;
|
|
51884
|
-
return { artifact: { ...artifact, fromRunId: artifact.fromRunId ??
|
|
52155
|
+
return { artifact: { ...artifact, fromRunId: artifact.fromRunId ?? record3.id }, runId: record3.id };
|
|
51885
52156
|
}
|
|
51886
52157
|
}
|
|
51887
52158
|
}
|
|
@@ -52031,7 +52302,26 @@ async function salvageBudgetPush(executor, opts, span) {
|
|
|
52031
52302
|
}
|
|
52032
52303
|
const unpushed = parseCountOutput(await run2("git rev-list --count HEAD --not --remotes")) ?? 0;
|
|
52033
52304
|
if (!dirty && !endingCheckpoint && unpushed === 0) return { pushed: false, summary: words.nothing };
|
|
52034
|
-
|
|
52305
|
+
if (opts.publication !== void 0 && "blocked" in opts.publication)
|
|
52306
|
+
return {
|
|
52307
|
+
pushed: false,
|
|
52308
|
+
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`
|
|
52309
|
+
};
|
|
52310
|
+
const lease = opts.publication !== void 0 ? ` --force-with-lease=${shellQuote(`refs/heads/${opts.publication.ref}:${opts.publication.expectedHeadSha}`)}` : "";
|
|
52311
|
+
try {
|
|
52312
|
+
await run2(`git push${lease} origin ${shellQuote(`HEAD:refs/heads/${opts.branch}`)}`);
|
|
52313
|
+
} catch (err2) {
|
|
52314
|
+
if (opts.publication !== void 0) {
|
|
52315
|
+
const detail = err2 instanceof Error ? err2.message : String(err2);
|
|
52316
|
+
const publicationBlocked = `the atomic leased push was rejected: ${detail}`;
|
|
52317
|
+
return {
|
|
52318
|
+
pushed: false,
|
|
52319
|
+
publicationBlocked,
|
|
52320
|
+
summary: `${words.failedLead}: existing-PR publication is blocked (${publicationBlocked}) \u2014 the commit remains unpublished in the bound workspace; no alternate ref was created`
|
|
52321
|
+
};
|
|
52322
|
+
}
|
|
52323
|
+
throw err2;
|
|
52324
|
+
}
|
|
52035
52325
|
const head = parseRevParseOutput(await probe("git rev-parse HEAD"));
|
|
52036
52326
|
return {
|
|
52037
52327
|
pushed: true,
|
|
@@ -52144,7 +52434,7 @@ async function runCodingPrPostStep(input) {
|
|
|
52144
52434
|
});
|
|
52145
52435
|
}
|
|
52146
52436
|
const ownPr = target.ownPr;
|
|
52147
|
-
const requestedBy = input.
|
|
52437
|
+
const requestedBy = input.requestedBy;
|
|
52148
52438
|
const onOwnPrBranch = ownPr?.headBranch !== void 0 && branch2 === ownPr.headBranch;
|
|
52149
52439
|
const pushedPastOwnPr = ownPr !== void 0 && onOwnPrBranch && pushed && headSha !== void 0 && !sameCommit(headSha, ownPr.headSha) ? headSha : void 0;
|
|
52150
52440
|
if (prDescription && ownPr !== void 0 && ownPr.state !== "open" && pushedPastOwnPr !== void 0 && branch2 !== void 0) {
|
|
@@ -52355,16 +52645,17 @@ async function runCodingPrPostStep(input) {
|
|
|
52355
52645
|
);
|
|
52356
52646
|
}
|
|
52357
52647
|
}
|
|
52358
|
-
|
|
52359
|
-
|
|
52648
|
+
const requestedLogin = input.identity.requestedLogin;
|
|
52649
|
+
if (requestedLogin !== void 0) {
|
|
52650
|
+
const assignable = await input.identity.isAssignable(repo, requestedLogin).catch(() => void 0);
|
|
52360
52651
|
if (assignable === true)
|
|
52361
|
-
await input.identity.addAssignee(repo, opened.number,
|
|
52652
|
+
await input.identity.addAssignee(repo, opened.number, requestedLogin).catch(
|
|
52362
52653
|
(err2) => console.error(
|
|
52363
|
-
`[pr-post] ${logKey} assignee add failed for ${
|
|
52654
|
+
`[pr-post] ${logKey} assignee add failed for ${requestedLogin} on ${repo}#${opened.number}: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
52364
52655
|
)
|
|
52365
52656
|
);
|
|
52366
52657
|
else if (assignable === false)
|
|
52367
|
-
console.log(`[identity] ${logKey} ${
|
|
52658
|
+
console.log(`[identity] ${logKey} ${requestedLogin} is not assignable on ${repo}; skipped`);
|
|
52368
52659
|
}
|
|
52369
52660
|
}
|
|
52370
52661
|
console.log(
|
|
@@ -52757,6 +53048,12 @@ async function runLoop(deps, ctx) {
|
|
|
52757
53048
|
return void 0;
|
|
52758
53049
|
}
|
|
52759
53050
|
})();
|
|
53051
|
+
let existingPrPublication;
|
|
53052
|
+
let existingPrPublicationFence;
|
|
53053
|
+
const blockExistingPrPublication = (reason2) => {
|
|
53054
|
+
existingPrPublication = { blocked: reason2 };
|
|
53055
|
+
if (existingPrPublicationFence !== void 0) existingPrPublicationFence.authority = existingPrPublication;
|
|
53056
|
+
};
|
|
52760
53057
|
const identitySeam = deps.identityRewrite;
|
|
52761
53058
|
const startStateRead = identitySeam !== void 0 && isCodingPrRun && repoCtx.repo !== void 0 ? (async () => {
|
|
52762
53059
|
const repo = repoCtx.repo;
|
|
@@ -52793,14 +53090,14 @@ async function runLoop(deps, ctx) {
|
|
|
52793
53090
|
);
|
|
52794
53091
|
return pace ? ` \xB7 ${pace}` : "";
|
|
52795
53092
|
};
|
|
53093
|
+
const showsActivity = shows(resolved.verbosity, "verbose");
|
|
52796
53094
|
const chatty = shows(resolved.verbosity, "debug");
|
|
52797
53095
|
const showBookkeeping = shows(resolved.verbosity, "verbose");
|
|
52798
53096
|
const currentFrame = () => shell.live({
|
|
52799
|
-
|
|
52800
|
-
suffix: chatty ? `${quietSuffix(clock4() - lastActivityAt, inFlightTool)}${cardPace()}` : cardPace(),
|
|
53097
|
+
suffix: showsActivity ? chatty ? `${quietSuffix(clock4() - lastActivityAt, inFlightTool)}${cardPace()}` : cardPace() : "",
|
|
52801
53098
|
notice: shutdownNotice(),
|
|
52802
53099
|
detail: [checklist],
|
|
52803
|
-
activity: chatty ? lastActivity :
|
|
53100
|
+
activity: showsActivity ? chatty ? lastActivity : compactActivity(lastActivity) : void 0
|
|
52804
53101
|
});
|
|
52805
53102
|
const checklistAsLeft = () => checklist;
|
|
52806
53103
|
const checklistCheckedOff = () => checklist?.replace(/^(\s*)[○✱](?=\s)/gm, "$1\u2713");
|
|
@@ -53035,8 +53332,18 @@ async function runLoop(deps, ctx) {
|
|
|
53035
53332
|
});
|
|
53036
53333
|
const salvaged = "skipped" in target ? { pushed: false, summary: target.skipped } : await root.span(
|
|
53037
53334
|
cue === "budget" ? "run.budget_salvage" : "run.work_salvage",
|
|
53038
|
-
(span) => salvageBudgetPush(
|
|
53335
|
+
(span) => salvageBudgetPush(
|
|
53336
|
+
executor,
|
|
53337
|
+
{
|
|
53338
|
+
branch: target.branch,
|
|
53339
|
+
cue,
|
|
53340
|
+
...existingPrPublication !== void 0 ? { publication: existingPrPublication } : {}
|
|
53341
|
+
},
|
|
53342
|
+
span
|
|
53343
|
+
)
|
|
53039
53344
|
);
|
|
53345
|
+
if ("publicationBlocked" in salvaged && salvaged.publicationBlocked !== void 0)
|
|
53346
|
+
blockExistingPrPublication(salvaged.publicationBlocked);
|
|
53040
53347
|
const cleanCompletion = cue === "completion" && !salvaged.pushed && (/ended with nothing to preserve/i.test(salvaged.summary) || "skipped" in target && (observedUncommitted ?? 0) === 0 && (observedUnpushed ?? 0) === 0);
|
|
53041
53348
|
if (cleanCompletion) return;
|
|
53042
53349
|
workSalvageAttempted = true;
|
|
@@ -53051,7 +53358,10 @@ async function runLoop(deps, ctx) {
|
|
|
53051
53358
|
salvagedTo = { branch: target.branch, head: salvaged.head };
|
|
53052
53359
|
} else if (salvaged.pushed && !("skipped" in target)) salvagedTo = { branch: target.branch };
|
|
53053
53360
|
if (salvaged.pushed) await observeWorkspaceNow();
|
|
53054
|
-
else if ("
|
|
53361
|
+
else if (existingPrPublication !== void 0 && "blocked" in existingPrPublication) {
|
|
53362
|
+
await observeWorkspaceNow();
|
|
53363
|
+
shell.note("quiet", salvaged.summary);
|
|
53364
|
+
} else if ("skipped" in target || /failed|not attempted/i.test(salvaged.summary))
|
|
53055
53365
|
shell.note("quiet", salvaged.summary);
|
|
53056
53366
|
};
|
|
53057
53367
|
let relaunchEndedRun = false;
|
|
@@ -53077,7 +53387,9 @@ async function runLoop(deps, ctx) {
|
|
|
53077
53387
|
return true;
|
|
53078
53388
|
};
|
|
53079
53389
|
const tailSkipped = () => run2.control.requested === "hard" || relaunchEndedRun || latchReviewStoppedBeforeStart();
|
|
53390
|
+
const keepBlockedPublicationWorkspace = () => existingPrPublication !== void 0 && "blocked" in existingPrPublication && run2.control.requested !== "hard" && !commandInFlight && !gateBypassed && ((observedUncommitted ?? 0) > 0 || (observedUnpushed ?? 0) > 0);
|
|
53080
53391
|
const releaseWorkspace = (span) => {
|
|
53392
|
+
if (keepBlockedPublicationWorkspace()) return Promise.resolve();
|
|
53081
53393
|
const events2 = recordEvents();
|
|
53082
53394
|
const pushed = pushedBranchesOf(events2);
|
|
53083
53395
|
return round2.release({
|
|
@@ -53175,9 +53487,9 @@ async function runLoop(deps, ctx) {
|
|
|
53175
53487
|
throw new Error(
|
|
53176
53488
|
`the ${agent.name} preset's run left ${facts2.harness} harness facts on its row, but this process has no harness roster to end that process with`
|
|
53177
53489
|
);
|
|
53178
|
-
const
|
|
53490
|
+
const judge5 = harnessOf(deps.harness);
|
|
53179
53491
|
const container = deps.harness.containerFor?.(executor, profile.machine) ?? harnessContainerFor(executor, profile.machine);
|
|
53180
|
-
const found = await
|
|
53492
|
+
const found = await judge5.find(facts2, container).catch((err2) => {
|
|
53181
53493
|
if (!isContainerGone(err2)) throw err2;
|
|
53182
53494
|
return "gone";
|
|
53183
53495
|
});
|
|
@@ -53202,11 +53514,11 @@ async function runLoop(deps, ctx) {
|
|
|
53202
53514
|
onEvent({
|
|
53203
53515
|
type: "run_note",
|
|
53204
53516
|
kind: "resumed",
|
|
53205
|
-
summary: `the run's row carries ${facts2.harness} harness facts (pid ${facts2.pid}) that the ${
|
|
53517
|
+
summary: `the run's row carries ${facts2.harness} harness facts (pid ${facts2.pid}) that the ${judge5.name} harness does not own: that process was neither judged nor ended here`
|
|
53206
53518
|
});
|
|
53207
53519
|
break;
|
|
53208
53520
|
default:
|
|
53209
|
-
await
|
|
53521
|
+
await judge5.end(facts2, container);
|
|
53210
53522
|
}
|
|
53211
53523
|
} else noteUnknownWord("the run finished on the answer it already had");
|
|
53212
53524
|
windDownEnding = windDownEndingUnder(finish2.answer, loopEnding);
|
|
@@ -53230,7 +53542,38 @@ async function runLoop(deps, ctx) {
|
|
|
53230
53542
|
if (!providerCfg)
|
|
53231
53543
|
throw new Error(`the ${harness.name} harness found no provider named ${providerName} in the config`);
|
|
53232
53544
|
const prBase = repoCtx.baseRef ?? await coordinatorBase;
|
|
53233
|
-
|
|
53545
|
+
if (coordinator?.publication !== void 0) {
|
|
53546
|
+
const publication = coordinator.publication;
|
|
53547
|
+
const fresh = await (deps.fetchPrFacts ?? fetchPullRequestFacts)({
|
|
53548
|
+
repo: publication.repo,
|
|
53549
|
+
number: publication.pr
|
|
53550
|
+
}).catch(() => void 0);
|
|
53551
|
+
const verified = verifyExistingPrPublication(
|
|
53552
|
+
publication,
|
|
53553
|
+
{
|
|
53554
|
+
repo: repoCtx.repo,
|
|
53555
|
+
pr: repoCtx.pr,
|
|
53556
|
+
ref: repoCtx.ref,
|
|
53557
|
+
baseRef: prBase,
|
|
53558
|
+
requestHeadSha: repoCtx.headSha,
|
|
53559
|
+
workspaceRef: binding?.ref,
|
|
53560
|
+
workspaceHeadSha: binding?.sha,
|
|
53561
|
+
owner: {
|
|
53562
|
+
instanceId: coordinator.parentInstanceId,
|
|
53563
|
+
unit: unitOfIdempotencyKey(coordinator.idempotencyKey) ?? ""
|
|
53564
|
+
}
|
|
53565
|
+
},
|
|
53566
|
+
fresh
|
|
53567
|
+
);
|
|
53568
|
+
existingPrPublication = verified.ok ? verified.publication : { blocked: verified.reason };
|
|
53569
|
+
existingPrPublicationFence = { authority: existingPrPublication };
|
|
53570
|
+
if (!verified.ok)
|
|
53571
|
+
shell.note(
|
|
53572
|
+
"quiet",
|
|
53573
|
+
`Existing-PR publication is blocked: ${verified.reason}. Work stays on the bound checkout; no alternate branch or pull request will be published.`
|
|
53574
|
+
);
|
|
53575
|
+
}
|
|
53576
|
+
const ownBranch = coordinator?.publication !== void 0 ? coordinator.publication.publicationRef : prBase !== void 0 ? binding?.ref ?? repoCtx.ref : void 0;
|
|
53234
53577
|
const protectedBranches = [
|
|
53235
53578
|
...new Set(
|
|
53236
53579
|
(prBase !== void 0 ? [prBase] : [binding?.ref, repoCtx.ref]).filter(
|
|
@@ -53290,8 +53633,18 @@ async function runLoop(deps, ctx) {
|
|
|
53290
53633
|
const branch2 = ownBranch;
|
|
53291
53634
|
const salvaged = await root.span(
|
|
53292
53635
|
"run.compaction_salvage",
|
|
53293
|
-
(span) => salvageBudgetPush(
|
|
53636
|
+
(span) => salvageBudgetPush(
|
|
53637
|
+
executor,
|
|
53638
|
+
{
|
|
53639
|
+
branch: branch2,
|
|
53640
|
+
cue: "compaction",
|
|
53641
|
+
...existingPrPublication !== void 0 ? { publication: existingPrPublication } : {}
|
|
53642
|
+
},
|
|
53643
|
+
span
|
|
53644
|
+
)
|
|
53294
53645
|
);
|
|
53646
|
+
if (salvaged.publicationBlocked !== void 0)
|
|
53647
|
+
blockExistingPrPublication(salvaged.publicationBlocked);
|
|
53295
53648
|
onEvent({
|
|
53296
53649
|
type: "run_note",
|
|
53297
53650
|
kind: "compaction_salvage",
|
|
@@ -53304,7 +53657,8 @@ async function runLoop(deps, ctx) {
|
|
|
53304
53657
|
rules: {
|
|
53305
53658
|
checkout: binding?.workspace ?? "/workspace",
|
|
53306
53659
|
...ownBranch !== void 0 ? { branch: ownBranch } : {},
|
|
53307
|
-
protectedBranches
|
|
53660
|
+
protectedBranches,
|
|
53661
|
+
...existingPrPublicationFence !== void 0 ? { publication: existingPrPublicationFence } : {}
|
|
53308
53662
|
},
|
|
53309
53663
|
...round2.selection.backend ? { backend: round2.selection.backend } : {},
|
|
53310
53664
|
span: root,
|
|
@@ -53520,6 +53874,12 @@ async function runLoop(deps, ctx) {
|
|
|
53520
53874
|
if (descriptionTurnRan) await preserveCodingChildWork("completion");
|
|
53521
53875
|
await endHarness();
|
|
53522
53876
|
const leftBehind = isCodingPrRun && !tailSkipped() && !workSalvageAttempted ? workLeftBehindOf({ uncommittedChanges: observedUncommitted, unpushedCommits: observedUnpushed }) : void 0;
|
|
53877
|
+
const blockedCheckpoint = keepBlockedPublicationWorkspace() && existingPrPublication !== void 0 && "blocked" in existingPrPublication && observedUncommitted !== void 0 && observedUnpushed !== void 0 && (observedUncommitted > 0 || observedUnpushed > 0) ? {
|
|
53878
|
+
reason: existingPrPublication.blocked,
|
|
53879
|
+
uncommitted: observedUncommitted,
|
|
53880
|
+
unpushed: observedUnpushed,
|
|
53881
|
+
...observedHead !== void 0 ? { head: observedHead } : {}
|
|
53882
|
+
} : void 0;
|
|
53523
53883
|
const endingFacts = () => {
|
|
53524
53884
|
if (profile.machine === "none") return { workspace: { kind: "none" } };
|
|
53525
53885
|
if (!isCodingPrRun || tailSkipped()) return { workspace: { kind: "unread" } };
|
|
@@ -53538,11 +53898,16 @@ async function runLoop(deps, ctx) {
|
|
|
53538
53898
|
kind: "left",
|
|
53539
53899
|
uncommitted: observedUncommitted,
|
|
53540
53900
|
unpushed: observedUnpushed,
|
|
53541
|
-
fate: commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
53901
|
+
fate: blockedCheckpoint !== void 0 ? "retained_unverified" : commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
53542
53902
|
};
|
|
53543
53903
|
return { workspace, description: prDescription !== void 0 ? "submitted" : "not_submitted" };
|
|
53544
53904
|
};
|
|
53545
|
-
if (
|
|
53905
|
+
if (blockedCheckpoint !== void 0) {
|
|
53906
|
+
const checkpoint = blockedCheckpoint.head ? ` at ${blockedCheckpoint.head.slice(0, 7)}` : "";
|
|
53907
|
+
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`;
|
|
53908
|
+
events.publish({ type: "run_note", kind: "publication_blocked", summary: oneLine(summary), at: clock4() });
|
|
53909
|
+
shell.note("quiet", `\u26A0\uFE0F ${summary}`);
|
|
53910
|
+
} else if (leftBehind) {
|
|
53546
53911
|
events.publish({
|
|
53547
53912
|
type: "run_note",
|
|
53548
53913
|
kind: "work_left_behind",
|
|
@@ -53558,8 +53923,10 @@ async function runLoop(deps, ctx) {
|
|
|
53558
53923
|
at: clock4()
|
|
53559
53924
|
});
|
|
53560
53925
|
}
|
|
53561
|
-
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted) {
|
|
53562
|
-
const
|
|
53926
|
+
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted && !(existingPrPublication !== void 0 && "blocked" in existingPrPublication)) {
|
|
53927
|
+
const instance = coordinator && deps.coordinatorInstances ? await deps.coordinatorInstances.get(coordinator.parentInstanceId).catch(() => void 0) : void 0;
|
|
53928
|
+
const requester = instance?.userId === msg.userId ? instance : msg;
|
|
53929
|
+
const requestedLogin = identitySeam !== void 0 ? await identitySeam.requestedLogin(requester.userId).catch(() => void 0) : void 0;
|
|
53563
53930
|
prNote = await root.span(
|
|
53564
53931
|
"run.pr_post_step",
|
|
53565
53932
|
() => runCodingPrPostStep({
|
|
@@ -53572,6 +53939,10 @@ async function runLoop(deps, ctx) {
|
|
|
53572
53939
|
remoteRepo: observedRemoteRepo
|
|
53573
53940
|
},
|
|
53574
53941
|
description: prDescription,
|
|
53942
|
+
requestedBy: {
|
|
53943
|
+
name: requester.userName?.trim() || requestedLogin || requester.userId,
|
|
53944
|
+
threadUrl: threadPageLink(requester.threadKey)
|
|
53945
|
+
},
|
|
53575
53946
|
target: prTarget,
|
|
53576
53947
|
openPullRequest: deps.openPullRequest ?? openPullRequest,
|
|
53577
53948
|
findOpenPr: deps.findOpenPrByHead ?? findOpenPrByHead,
|
|
@@ -53584,7 +53955,7 @@ async function runLoop(deps, ctx) {
|
|
|
53584
53955
|
pullRequestHead: identitySeam.pullRequestHead,
|
|
53585
53956
|
isAssignable: identitySeam.isAssignable,
|
|
53586
53957
|
addAssignee: identitySeam.addAssignee,
|
|
53587
|
-
...requestedLogin !== void 0 ? {
|
|
53958
|
+
...requestedLogin !== void 0 ? { requestedLogin } : {}
|
|
53588
53959
|
}
|
|
53589
53960
|
} : {},
|
|
53590
53961
|
descriptionTurnRan,
|
|
@@ -53823,6 +54194,7 @@ var init_runLoop = __esm({
|
|
|
53823
54194
|
"use strict";
|
|
53824
54195
|
init_actor();
|
|
53825
54196
|
init_predicate();
|
|
54197
|
+
init_contract2();
|
|
53826
54198
|
init_decisionRecordReservation();
|
|
53827
54199
|
init_profile();
|
|
53828
54200
|
init_provider();
|
|
@@ -53838,6 +54210,7 @@ var init_runLoop = __esm({
|
|
|
53838
54210
|
init_resume();
|
|
53839
54211
|
init_githubPulls();
|
|
53840
54212
|
init_repoContext();
|
|
54213
|
+
init_existingPrPublication();
|
|
53841
54214
|
init_prDescription();
|
|
53842
54215
|
init_handoff();
|
|
53843
54216
|
init_reviewVerdict();
|
|
@@ -53992,7 +54365,7 @@ async function shipPreflight(input) {
|
|
|
53992
54365
|
`\u{1F6AB} ${where}'s head branch lives on a fork, not on \`${repo}\` \u2014 ship cannot drive it.`
|
|
53993
54366
|
);
|
|
53994
54367
|
}
|
|
53995
|
-
const branch2 = facts2.headRef
|
|
54368
|
+
const branch2 = facts2.headRef;
|
|
53996
54369
|
if (!branch2) {
|
|
53997
54370
|
return refuse2(
|
|
53998
54371
|
"ship_preflight_head_unknown",
|
|
@@ -54001,10 +54374,34 @@ async function shipPreflight(input) {
|
|
|
54001
54374
|
`\u{1F6AB} Could not determine ${where}'s head branch, so ship cannot bind the thread's worktree to it \u2014 refusing fail-closed.`
|
|
54002
54375
|
);
|
|
54003
54376
|
}
|
|
54004
|
-
const
|
|
54377
|
+
const headSha = facts2.headSha;
|
|
54378
|
+
if (headSha === void 0 || !/^[0-9a-f]{40}$/i.test(headSha)) {
|
|
54379
|
+
return refuse2(
|
|
54380
|
+
"ship_preflight_head_unknown",
|
|
54381
|
+
"full head commit unknown",
|
|
54382
|
+
"not started (head commit unknown)",
|
|
54383
|
+
`\u{1F6AB} Could not determine ${where}'s full head commit, so ship cannot fence publication to it \u2014 refusing fail-closed.`
|
|
54384
|
+
);
|
|
54385
|
+
}
|
|
54386
|
+
if (facts2.baseRef === void 0) {
|
|
54387
|
+
return refuse2(
|
|
54388
|
+
"ship_preflight_head_unknown",
|
|
54389
|
+
"base branch unknown",
|
|
54390
|
+
"not started (base branch unknown)",
|
|
54391
|
+
`\u{1F6AB} Could not determine ${where}'s base branch, so ship cannot bind publication to it \u2014 refusing fail-closed.`
|
|
54392
|
+
);
|
|
54393
|
+
}
|
|
54394
|
+
const ownBase = facts2.baseRef;
|
|
54005
54395
|
const ownBaseExists = ownBase !== void 0 && input.refExists ? await input.refExists(repo, ownBase).catch(() => void 0) : void 0;
|
|
54006
|
-
|
|
54007
|
-
|
|
54396
|
+
if (ownBaseExists === false) {
|
|
54397
|
+
return refuse2(
|
|
54398
|
+
"ship_preflight_head_unknown",
|
|
54399
|
+
"pull request base branch missing",
|
|
54400
|
+
"not started (base branch missing)",
|
|
54401
|
+
`\u{1F6AB} ${where}'s base branch \`${ownBase}\` is not present in \`${repo}\`, so ship cannot establish an exact publication binding \u2014 refusing fail-closed.`
|
|
54402
|
+
);
|
|
54403
|
+
}
|
|
54404
|
+
const base = resolveBaseRef([ownBase], info?.defaultBranch);
|
|
54008
54405
|
const autoMerge = facts2.autoMergeEnabled !== void 0 ? { autoMergeEnabled: facts2.autoMergeEnabled } : {};
|
|
54009
54406
|
if (task) {
|
|
54010
54407
|
return {
|
|
@@ -54013,8 +54410,7 @@ async function shipPreflight(input) {
|
|
|
54013
54410
|
repo,
|
|
54014
54411
|
branch: branch2,
|
|
54015
54412
|
base,
|
|
54016
|
-
adopt: { pr: repoCtx.pr, ...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {} },
|
|
54017
|
-
...baseFallback !== void 0 ? { baseFallback } : {},
|
|
54413
|
+
adopt: { pr: repoCtx.pr, headSha, ...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {} },
|
|
54018
54414
|
...autoMerge
|
|
54019
54415
|
}
|
|
54020
54416
|
};
|
|
@@ -54027,10 +54423,9 @@ async function shipPreflight(input) {
|
|
|
54027
54423
|
base,
|
|
54028
54424
|
resume: {
|
|
54029
54425
|
pr: repoCtx.pr,
|
|
54030
|
-
headSha
|
|
54426
|
+
headSha,
|
|
54031
54427
|
...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {}
|
|
54032
54428
|
},
|
|
54033
|
-
...baseFallback !== void 0 ? { baseFallback } : {},
|
|
54034
54429
|
...autoMerge
|
|
54035
54430
|
}
|
|
54036
54431
|
};
|
|
@@ -54221,18 +54616,30 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
|
|
|
54221
54616
|
p.graph.units.filter((u) => selected.includes(u.id)).map(async (u) => {
|
|
54222
54617
|
const previous = carried?.get(u.id);
|
|
54223
54618
|
const taskKey = p.recordTasks?.[u.id];
|
|
54224
|
-
const
|
|
54619
|
+
const record3 = taskKey !== void 0 ? await reserve(p.identity.repo, taskKey, previous?.record) : previous?.record;
|
|
54620
|
+
const branch2 = p.entryBranch ?? u.branch;
|
|
54621
|
+
const existing = p.adopt ?? p.resume;
|
|
54622
|
+
const publication = existing !== void 0 && p.entryBranch !== void 0 ? {
|
|
54623
|
+
repo: p.identity.repo,
|
|
54624
|
+
pr: existing.pr,
|
|
54625
|
+
headRef: p.entryBranch,
|
|
54626
|
+
baseRef: p.base,
|
|
54627
|
+
expectedHeadSha: existing.headSha,
|
|
54628
|
+
publicationRef: p.entryBranch,
|
|
54629
|
+
owner: { instanceId, unit: u.id }
|
|
54630
|
+
} : void 0;
|
|
54225
54631
|
return {
|
|
54226
54632
|
instanceId,
|
|
54227
54633
|
unit: u.id,
|
|
54228
54634
|
slug: u.slug,
|
|
54229
54635
|
title: u.title,
|
|
54230
|
-
branch:
|
|
54636
|
+
branch: branch2,
|
|
54231
54637
|
dependsOn: u.dependsOn,
|
|
54232
54638
|
rounds: [],
|
|
54233
54639
|
...p.resume !== void 0 ? { resume: p.resume } : {},
|
|
54640
|
+
...publication !== void 0 ? { publication } : {},
|
|
54234
54641
|
...previous?.lastPush !== void 0 ? { lastPush: previous.lastPush } : {},
|
|
54235
|
-
...
|
|
54642
|
+
...record3 !== void 0 ? { record: record3 } : {}
|
|
54236
54643
|
};
|
|
54237
54644
|
})
|
|
54238
54645
|
);
|
|
@@ -54648,7 +55055,11 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
54648
55055
|
return { hostedLive: false };
|
|
54649
55056
|
}
|
|
54650
55057
|
const entry = pre.entry;
|
|
54651
|
-
const
|
|
55058
|
+
const configuredCaps = resolveShipCaps(deps.config.config.ship);
|
|
55059
|
+
const caps = {
|
|
55060
|
+
maxRounds: Math.min(configuredCaps.maxRounds, ctx.reissueCaps?.maxRounds ?? configuredCaps.maxRounds),
|
|
55061
|
+
maxMinutes: Math.min(profile.minutes, ctx.reissueCaps?.maxMinutes ?? profile.minutes)
|
|
55062
|
+
};
|
|
54652
55063
|
const held2 = fit(caps);
|
|
54653
55064
|
if (!held2.ok) {
|
|
54654
55065
|
const reason2 = `budget ${caps.maxMinutes} min cannot hold the ship loop (${caps.maxRounds} review rounds need ${held2.need} min)`;
|
|
@@ -54792,6 +55203,7 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
54792
55203
|
...msg.authenticatedAs !== void 0 ? { authenticatedAs: msg.authenticatedAs } : {},
|
|
54793
55204
|
...msg.postedBy !== void 0 ? { postedBy: msg.postedBy } : {},
|
|
54794
55205
|
...entry.resume !== void 0 ? { pr: entry.resume.pr } : {},
|
|
55206
|
+
verbosity: ctx.verbosity,
|
|
54795
55207
|
profile
|
|
54796
55208
|
},
|
|
54797
55209
|
card: card.handle ?? null,
|
|
@@ -55135,7 +55547,13 @@ function carriedCoordinatorTag(row2, events) {
|
|
|
55135
55547
|
if (typeof parentInstanceId !== "string" || typeof idempotencyKey !== "string") return void 0;
|
|
55136
55548
|
const tag = events.find((e) => e.type === "coordinator_tag");
|
|
55137
55549
|
const base = tag?.type === "coordinator_tag" ? tag.base : void 0;
|
|
55138
|
-
|
|
55550
|
+
const publication = tag?.type === "coordinator_tag" ? tag.publication : void 0;
|
|
55551
|
+
return {
|
|
55552
|
+
parentInstanceId,
|
|
55553
|
+
idempotencyKey,
|
|
55554
|
+
...base !== void 0 ? { base } : {},
|
|
55555
|
+
...publication !== void 0 ? { publication } : {}
|
|
55556
|
+
};
|
|
55139
55557
|
}
|
|
55140
55558
|
function lostWorkspaceNote(why, restarts) {
|
|
55141
55559
|
const lost = `resumed after a restart: the run's workspace could not be re-attached (${why})`;
|
|
@@ -55359,41 +55777,41 @@ function prName(pr) {
|
|
|
55359
55777
|
const m = GITHUB_PULL_URL.exec(pr.url);
|
|
55360
55778
|
return m ? `${m[1]}#${pr.number} (${pr.url})` : `pull request ${pr.number} at ${pr.url}`;
|
|
55361
55779
|
}
|
|
55362
|
-
function runArtifacts(
|
|
55780
|
+
function runArtifacts(record3) {
|
|
55363
55781
|
const lines = [];
|
|
55364
|
-
if (
|
|
55365
|
-
const head =
|
|
55366
|
-
lines.push(`- pull request opened or edited: ${prName(
|
|
55782
|
+
if (record3.pr) {
|
|
55783
|
+
const head = record3.pr.head ? `, head branch ${record3.pr.head}` : "";
|
|
55784
|
+
lines.push(`- pull request opened or edited: ${prName(record3.pr)}${head}`);
|
|
55367
55785
|
}
|
|
55368
|
-
if (
|
|
55369
|
-
lines.push(`- verdict: ${
|
|
55370
|
-
const findings =
|
|
55786
|
+
if (record3.verdict) {
|
|
55787
|
+
lines.push(`- verdict: ${record3.verdict.verdict} \u2014 ${record3.verdict.summary}`);
|
|
55788
|
+
const findings = record3.verdict.findings ?? [];
|
|
55371
55789
|
lines.push(findings.length ? `- findings:
|
|
55372
55790
|
${indent(findings.map(formatFinding))}` : "- findings: none recorded");
|
|
55373
55791
|
}
|
|
55374
|
-
if (
|
|
55792
|
+
if (record3.dispositions) {
|
|
55375
55793
|
lines.push(
|
|
55376
|
-
|
|
55377
|
-
${indent(
|
|
55794
|
+
record3.dispositions.length ? `- dispositions:
|
|
55795
|
+
${indent(record3.dispositions.map(formatDisposition))}` : "- dispositions: none recorded"
|
|
55378
55796
|
);
|
|
55379
55797
|
}
|
|
55380
|
-
if (
|
|
55381
|
-
const entries = handoffLines(
|
|
55798
|
+
if (record3.handoff) {
|
|
55799
|
+
const entries = handoffLines(record3.handoff);
|
|
55382
55800
|
lines.push(
|
|
55383
55801
|
entries.length ? `- handoff:
|
|
55384
55802
|
${indent(entries)}` : "- handoff: nothing to hand off (three empty lists)"
|
|
55385
55803
|
);
|
|
55386
55804
|
}
|
|
55387
|
-
const tldr =
|
|
55805
|
+
const tldr = record3.pr ? tldrOf(record3.events) : void 0;
|
|
55388
55806
|
if (tldr !== void 0) lines.push(`- description TL;DR: ${tldr}`);
|
|
55389
|
-
if (
|
|
55390
|
-
const post =
|
|
55807
|
+
if (record3.reviewPost) {
|
|
55808
|
+
const post = record3.reviewPost;
|
|
55391
55809
|
lines.push(
|
|
55392
55810
|
post.posted ? `- review post: posted to ${post.target.repo}#${post.target.number} at ${post.head}${post.verdict ? ` (${post.verdict})` : ""}` : `- review post: not posted \u2014 ${post.reason}`
|
|
55393
55811
|
);
|
|
55394
55812
|
}
|
|
55395
55813
|
if (lines.length === 0) return void 0;
|
|
55396
|
-
return [`### run ${
|
|
55814
|
+
return [`### run ${record3.id} (${record3.agent ?? "agent unknown"})`, ...lines].join("\n");
|
|
55397
55815
|
}
|
|
55398
55816
|
function threadArtifactsBlock(records, opts) {
|
|
55399
55817
|
const maxRuns = opts.maxRuns ?? THREAD_ARTIFACTS_MAX_RUNS;
|
|
@@ -55654,6 +56072,23 @@ async function answerUnitOwnedThread(deps, ctx) {
|
|
|
55654
56072
|
function activeRunCount() {
|
|
55655
56073
|
return activeRuns;
|
|
55656
56074
|
}
|
|
56075
|
+
function continuationBudgetOf(instance, unit) {
|
|
56076
|
+
if (unit.ending?.kind === "wall_clock_cap" || unit.ending?.kind === "review_pending")
|
|
56077
|
+
return { kind: "exhausted", budget: "wall-clock" };
|
|
56078
|
+
if (unit.ending?.kind === "round_cap") return { kind: "exhausted", budget: "review-round" };
|
|
56079
|
+
const caps = instance.caps;
|
|
56080
|
+
if (caps === void 0 || unit.startedAt === void 0 || unit.ending === void 0 || caps.maxMinutes <= 0 || caps.maxRounds <= 0 || unit.ending.at < unit.startedAt)
|
|
56081
|
+
return { kind: "unavailable" };
|
|
56082
|
+
const remainingMinutes = caps.maxMinutes - (unit.ending.at - unit.startedAt) / MINUTE_MS;
|
|
56083
|
+
if (remainingMinutes <= 0) return { kind: "exhausted", budget: "wall-clock" };
|
|
56084
|
+
const reviewRounds = unit.rounds.reduce(
|
|
56085
|
+
(highest, round2) => round2.agent === "review" ? Math.max(highest, round2.index) : highest,
|
|
56086
|
+
0
|
|
56087
|
+
);
|
|
56088
|
+
const remainingRounds = caps.maxRounds - reviewRounds;
|
|
56089
|
+
if (remainingRounds <= 0) return { kind: "exhausted", budget: "review-round" };
|
|
56090
|
+
return { kind: "remaining", caps: { maxRounds: remainingRounds, maxMinutes: remainingMinutes } };
|
|
56091
|
+
}
|
|
55657
56092
|
async function dispatch(deps, msg, io, opts = {}) {
|
|
55658
56093
|
const ended = { status: "completed" };
|
|
55659
56094
|
const resume = opts.resume;
|
|
@@ -55748,22 +56183,46 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55748
56183
|
try {
|
|
55749
56184
|
const configuredOperator = !resume && !restart && !opts.parent && !opts.coordinator ? operatorModeOf(deps.config.config) : "off";
|
|
55750
56185
|
const typedDecision = parseDirectives(msg.text).agent !== void 0 || deps.commands !== void 0 && parseChatCommand(msg.text, deps.commands) !== null;
|
|
55751
|
-
|
|
56186
|
+
let operatorMode = configuredOperator === "on" && typedDecision ? "off" : configuredOperator;
|
|
55752
56187
|
let operatorEvent;
|
|
55753
56188
|
let operatorPreset;
|
|
55754
56189
|
let operatorRequest;
|
|
55755
56190
|
let operatorModel;
|
|
55756
56191
|
let operatorRepo;
|
|
55757
56192
|
let reissuePlanId;
|
|
56193
|
+
let reissueCaps;
|
|
55758
56194
|
let operatorThread;
|
|
55759
56195
|
let pageOwner;
|
|
55760
56196
|
if (operatorMode !== "off") {
|
|
55761
56197
|
const runsService2 = deps.runs ?? createRunsService({ registry, store: deps.runStore });
|
|
55762
56198
|
operatorThread = opts.thread ?? await readThread(runsService2, msg.threadKey);
|
|
56199
|
+
if (operatorThread !== void 0 && deps.coordinatorInstances !== void 0) {
|
|
56200
|
+
const unitReads = /* @__PURE__ */ new Map();
|
|
56201
|
+
const unitsOf = (id) => {
|
|
56202
|
+
let read = unitReads.get(id);
|
|
56203
|
+
if (read === void 0) {
|
|
56204
|
+
read = deps.coordinatorInstances.listUnits(id);
|
|
56205
|
+
unitReads.set(id, read);
|
|
56206
|
+
}
|
|
56207
|
+
return read;
|
|
56208
|
+
};
|
|
56209
|
+
pageOwner = await ownerOf2(operatorThread, unitsOf, msg.threadKey);
|
|
56210
|
+
if (operatorMode === "on" && parseDirectives(msg.text).agent === void 0) {
|
|
56211
|
+
const continuationOwner = pageOwner.kind === "pipeline" || pageOwner.kind === "pipeline_ambiguous" ? pageOwner : await ownerOf2(
|
|
56212
|
+
operatorThread.filter((run3) => run3.finished),
|
|
56213
|
+
unitsOf,
|
|
56214
|
+
msg.threadKey
|
|
56215
|
+
);
|
|
56216
|
+
if (continuationOwner.kind === "pipeline" || continuationOwner.kind === "pipeline_ambiguous") {
|
|
56217
|
+
pageOwner = continuationOwner;
|
|
56218
|
+
operatorMode = "off";
|
|
56219
|
+
}
|
|
56220
|
+
}
|
|
56221
|
+
}
|
|
56222
|
+
}
|
|
56223
|
+
if (operatorMode !== "off") {
|
|
55763
56224
|
const slot = admission.get(msg.threadKey);
|
|
55764
56225
|
const liveElsewhere = deps.threadsElsewhere.get(msg.threadKey) !== void 0;
|
|
55765
|
-
if (slot === void 0 && !liveElsewhere && operatorThread !== void 0 && deps.coordinatorInstances !== void 0)
|
|
55766
|
-
pageOwner = await ownerOf2(operatorThread, (id) => deps.coordinatorInstances.listUnits(id), msg.threadKey);
|
|
55767
56226
|
const pendingQuestion = operatorMode === "on" ? pendingQuestionOf(operatorThread) : void 0;
|
|
55768
56227
|
const joinedAnswer = pendingQuestion !== void 0 && !(pendingQuestion.proposal !== void 0 && isYesAnswer(msg.text)) ? joinedAnswerRequest(pendingQuestion, msg.text) : void 0;
|
|
55769
56228
|
const doorMsg = joinedAnswer !== void 0 ? { ...msg, text: joinedAnswer } : msg;
|
|
@@ -55775,7 +56234,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55775
56234
|
// other, and the fold runs on to the seed refusal below, which
|
|
55776
56235
|
// names the unit threads to reply in.
|
|
55777
56236
|
pageOwner.run.instanceId !== void 0 ? { kind: "live" } : { kind: "live", runId: pageOwner.run.id }
|
|
55778
|
-
) : pageOwner?.kind === "unit" ? { kind: "unit", unit: pageOwner.unit.unit } : pageOwner?.kind === "pipeline" ? { kind: "pipeline", unit: pageOwner.unit.unit } : void 0;
|
|
56237
|
+
) : pageOwner?.kind === "unit" ? { kind: "unit", unit: pageOwner.unit.unit } : pageOwner?.kind === "pipeline" ? { kind: "pipeline", unit: pageOwner.unit.unit } : pageOwner?.kind === "pipeline_ambiguous" ? { kind: "pipeline", unit: pageOwner.units.map((unit) => unit.unit).join(", ") } : void 0;
|
|
55779
56238
|
operatorEvent = await root.span(
|
|
55780
56239
|
"dispatch.operator",
|
|
55781
56240
|
() => operatorStage(deps, {
|
|
@@ -55879,20 +56338,120 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55879
56338
|
await recordPendingOperator();
|
|
55880
56339
|
return ended;
|
|
55881
56340
|
}
|
|
56341
|
+
if (owner.kind === "pipeline_ambiguous" && directives.agent === void 0) {
|
|
56342
|
+
await io.reply(
|
|
56343
|
+
`This thread matches multiple ended plan units (${owner.units.map((unit) => unit.unit).join(", ")}), so continuation is ambiguous. Nothing started.`
|
|
56344
|
+
);
|
|
56345
|
+
await recordPendingOperator();
|
|
56346
|
+
return ended;
|
|
56347
|
+
}
|
|
55882
56348
|
if (owner.kind === "pipeline" && directives.agent === void 0) {
|
|
56349
|
+
const instance = await deps.coordinatorInstances.get(owner.instanceId).catch(() => null);
|
|
56350
|
+
if (instance === null) {
|
|
56351
|
+
await io.reply(
|
|
56352
|
+
`The ended pipeline's record \`${owner.instanceId}\` is unavailable, so continuation did not start.`
|
|
56353
|
+
);
|
|
56354
|
+
await recordPendingOperator();
|
|
56355
|
+
return ended;
|
|
56356
|
+
}
|
|
56357
|
+
const actor = chatActorOf(deps.config, msg);
|
|
56358
|
+
if (authorizeSteerOwner({
|
|
56359
|
+
caller: { ids: actorIdsOf(actor), grants: effectiveGrants(actor) },
|
|
56360
|
+
target: { runId: owner.run.id, requesterId: instance.userId }
|
|
56361
|
+
}).kind === "refused") {
|
|
56362
|
+
await io.reply(STEER_OWNER_REFUSED);
|
|
56363
|
+
await recordPendingOperator();
|
|
56364
|
+
return ended;
|
|
56365
|
+
}
|
|
56366
|
+
if (instance.threadKey !== msg.threadKey || instance.plan === void 0 || instance.plan.path !== void 0) {
|
|
56367
|
+
await io.reply(
|
|
56368
|
+
`The ended pipeline's durable generated-task identity does not match this thread, so continuation did not start.`
|
|
56369
|
+
);
|
|
56370
|
+
await recordPendingOperator();
|
|
56371
|
+
return ended;
|
|
56372
|
+
}
|
|
56373
|
+
if (instance.repo !== owner.run.repo || instance.branch !== owner.unit.branch) {
|
|
56374
|
+
await io.reply(
|
|
56375
|
+
`The ended pipeline's repository or branch identity no longer matches unit ${owner.unit.unit}, so continuation did not start.`
|
|
56376
|
+
);
|
|
56377
|
+
await recordPendingOperator();
|
|
56378
|
+
return ended;
|
|
56379
|
+
}
|
|
56380
|
+
const continuationBudget = continuationBudgetOf(instance, owner.unit);
|
|
56381
|
+
if (continuationBudget.kind === "unavailable") {
|
|
56382
|
+
await io.reply(
|
|
56383
|
+
`Unit ${owner.unit.unit} does not retain a complete durable wall-clock and review-round budget, so continuation did not start. Nothing else ran.`
|
|
56384
|
+
);
|
|
56385
|
+
await recordPendingOperator();
|
|
56386
|
+
return ended;
|
|
56387
|
+
}
|
|
56388
|
+
if (continuationBudget.kind === "exhausted") {
|
|
56389
|
+
await io.reply(
|
|
56390
|
+
`Unit ${owner.unit.unit} exhausted the ended pipeline's ${continuationBudget.budget} budget, so continuation did not start. Nothing else ran.`
|
|
56391
|
+
);
|
|
56392
|
+
await recordPendingOperator();
|
|
56393
|
+
return ended;
|
|
56394
|
+
}
|
|
55883
56395
|
const original = await shipRequestOf(runsService, owner.run.id);
|
|
55884
56396
|
if (original === void 0) {
|
|
55885
56397
|
await io.reply(
|
|
55886
|
-
|
|
56398
|
+
`The ended pipeline's original task is unavailable from run \`${owner.run.id}\`, so continuation did not start.`
|
|
55887
56399
|
);
|
|
55888
56400
|
await recordPendingOperator();
|
|
55889
56401
|
return ended;
|
|
55890
56402
|
}
|
|
55891
|
-
const
|
|
55892
|
-
if (
|
|
55893
|
-
|
|
56403
|
+
const recordedPr = owner.unit.pr;
|
|
56404
|
+
if (recordedPr === void 0) {
|
|
56405
|
+
await io.reply(
|
|
56406
|
+
`Unit ${owner.unit.unit} does not retain a durable pull request identity, so continuation did not start. Nothing else ran.`
|
|
56407
|
+
);
|
|
56408
|
+
await recordPendingOperator();
|
|
56409
|
+
return ended;
|
|
56410
|
+
}
|
|
56411
|
+
const expectedHead = owner.unit.lastPush;
|
|
56412
|
+
if (expectedHead === void 0) {
|
|
56413
|
+
await io.reply(
|
|
56414
|
+
`Unit ${owner.unit.unit} does not retain the pull request's durable expected head, so continuation did not start. Nothing else ran.`
|
|
56415
|
+
);
|
|
56416
|
+
await recordPendingOperator();
|
|
56417
|
+
return ended;
|
|
56418
|
+
}
|
|
56419
|
+
const target = { repo: instance.repo, number: recordedPr.number };
|
|
56420
|
+
const facts2 = await (deps.fetchPrFacts ?? fetchPullRequestFacts)(target).catch(() => void 0);
|
|
56421
|
+
if (facts2 === void 0) {
|
|
56422
|
+
await io.reply(
|
|
56423
|
+
`GitHub did not return current facts for ${instance.repo}#${recordedPr.number}, so continuation did not start.`
|
|
56424
|
+
);
|
|
56425
|
+
await recordPendingOperator();
|
|
56426
|
+
return ended;
|
|
56427
|
+
}
|
|
56428
|
+
if (facts2.state !== "open") {
|
|
56429
|
+
await io.reply(
|
|
56430
|
+
`${instance.repo}#${recordedPr.number} is closed, so this ended pipeline has no open pull request to continue. Nothing started.`
|
|
56431
|
+
);
|
|
56432
|
+
await recordPendingOperator();
|
|
56433
|
+
return ended;
|
|
56434
|
+
}
|
|
56435
|
+
const verifiedHead = facts2.verifiedHead;
|
|
56436
|
+
if (!facts2.sameRepoHead || facts2.headBranchExists !== true || facts2.headRef !== owner.unit.branch || verifiedHead === void 0 || verifiedHead.repo.toLowerCase() !== instance.repo.toLowerCase() || verifiedHead.ref !== owner.unit.branch || facts2.headSha !== verifiedHead.sha) {
|
|
56437
|
+
await io.reply(
|
|
56438
|
+
`${instance.repo}#${recordedPr.number} no longer has the pipeline's verifiable \`${owner.unit.branch}\` head, so continuation did not start.`
|
|
56439
|
+
);
|
|
56440
|
+
await recordPendingOperator();
|
|
56441
|
+
return ended;
|
|
56442
|
+
}
|
|
56443
|
+
if (verifiedHead.sha !== expectedHead) {
|
|
56444
|
+
await io.reply(
|
|
56445
|
+
`${instance.repo}#${recordedPr.number} moved from the pipeline's expected head \`${expectedHead}\` to \`${verifiedHead.sha}\`, so continuation did not start. Nothing else ran.`
|
|
56446
|
+
);
|
|
56447
|
+
await recordPendingOperator();
|
|
56448
|
+
return ended;
|
|
56449
|
+
}
|
|
56450
|
+
reissuePlanId = instance.plan.id;
|
|
56451
|
+
reissueCaps = continuationBudget.caps;
|
|
56452
|
+
const durableRequest = parseDirectives(original);
|
|
56453
|
+
Object.assign(directives, durableRequest, { agent: void 0, budget: reissueCaps.maxMinutes });
|
|
55894
56454
|
operatorPreset = "ship";
|
|
55895
|
-
operatorRequest = original;
|
|
55896
56455
|
settled = resolveCurrent();
|
|
55897
56456
|
({ sticky, resolved, agentSource } = settled);
|
|
55898
56457
|
agentSource = "sticky";
|
|
@@ -56118,6 +56677,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
56118
56677
|
card,
|
|
56119
56678
|
directives,
|
|
56120
56679
|
...reissuePlanId !== void 0 ? { reissuePlanId } : {},
|
|
56680
|
+
...reissueCaps !== void 0 ? { reissueCaps: { ...reissueCaps, maxMinutes: Math.min(reissueCaps.maxMinutes, profile.minutes) } } : {},
|
|
56121
56681
|
sticky,
|
|
56122
56682
|
history,
|
|
56123
56683
|
repoCtx,
|
|
@@ -57046,6 +57606,7 @@ var init_dispatcher = __esm({
|
|
|
57046
57606
|
init_route();
|
|
57047
57607
|
init_operator();
|
|
57048
57608
|
init_authorize2();
|
|
57609
|
+
init_authorize();
|
|
57049
57610
|
init_messages();
|
|
57050
57611
|
init_staging();
|
|
57051
57612
|
init_runRecord();
|
|
@@ -57064,6 +57625,7 @@ var init_dispatcher = __esm({
|
|
|
57064
57625
|
init_settle();
|
|
57065
57626
|
init_reattach2();
|
|
57066
57627
|
init_factory();
|
|
57628
|
+
init_githubPulls();
|
|
57067
57629
|
init_sandboxErrors();
|
|
57068
57630
|
init_lineage();
|
|
57069
57631
|
init_seed();
|
|
@@ -57424,8 +57986,8 @@ function createLedgerWriteThrough(opts) {
|
|
|
57424
57986
|
settleLanding(this.runId, entry, outcome);
|
|
57425
57987
|
return value;
|
|
57426
57988
|
},
|
|
57427
|
-
abandoned: (
|
|
57428
|
-
if (
|
|
57989
|
+
abandoned: (record3, why) => {
|
|
57990
|
+
if (record3.id !== this.runId) return;
|
|
57429
57991
|
const entry = landing.get(this.runId);
|
|
57430
57992
|
if (entry !== void 0) settleLanding(this.runId, entry, { kind: "failed", why });
|
|
57431
57993
|
}
|
|
@@ -57435,18 +57997,18 @@ function createLedgerWriteThrough(opts) {
|
|
|
57435
57997
|
async land(plain7) {
|
|
57436
57998
|
await this.close();
|
|
57437
57999
|
const session = this.sessionOnRecord();
|
|
57438
|
-
const
|
|
58000
|
+
const record3 = session ? { ...plain7, session } : plain7;
|
|
57439
58001
|
let result;
|
|
57440
58002
|
try {
|
|
57441
|
-
result = await ledger.finish(this.runId, gen,
|
|
58003
|
+
result = await ledger.finish(this.runId, gen, record3);
|
|
57442
58004
|
} catch (err2) {
|
|
57443
58005
|
if (!(err2 instanceof RouteMissingError)) throw err2;
|
|
57444
58006
|
routeMissing();
|
|
57445
|
-
return { value: await fallback.put(
|
|
58007
|
+
return { value: await fallback.put(record3), outcome: { kind: "refused" } };
|
|
57446
58008
|
}
|
|
57447
58009
|
if (result.ok) return { value: result, outcome: { kind: "landed" } };
|
|
57448
58010
|
warn(`[ledger] ${this.threadKey} finish refused (${result.reason}) \u2014 record written to the store directly`);
|
|
57449
|
-
return { value: await fallback.put(
|
|
58011
|
+
return { value: await fallback.put(record3), outcome: { kind: "refused" } };
|
|
57450
58012
|
}
|
|
57451
58013
|
tracked() {
|
|
57452
58014
|
return !this.detached;
|
|
@@ -57516,7 +58078,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
57516
58078
|
}));
|
|
57517
58079
|
if (report2.compaction)
|
|
57518
58080
|
turns.push({ idx: this.rowIndex(report2.firstIdx + report2.turns.length), compaction: report2.compaction });
|
|
57519
|
-
const
|
|
58081
|
+
const record3 = {
|
|
57520
58082
|
step: ++this.stepNo,
|
|
57521
58083
|
seq: this.lastSeq,
|
|
57522
58084
|
turnIndex: report2.firstIdx + turns.length,
|
|
@@ -57527,10 +58089,10 @@ function createLedgerWriteThrough(opts) {
|
|
|
57527
58089
|
turn: report2.turn,
|
|
57528
58090
|
iteration: report2.iteration
|
|
57529
58091
|
};
|
|
57530
|
-
this.factRound =
|
|
57531
|
-
const kept = new Set(
|
|
58092
|
+
this.factRound = record3.step;
|
|
58093
|
+
const kept = new Set(record3.inFlight.map((c) => c.callId));
|
|
57532
58094
|
for (const id of this.factCallStarts.keys()) if (!kept.has(id)) this.factCallStarts.delete(id);
|
|
57533
|
-
const call2 =
|
|
58095
|
+
const call2 = record3.inFlight[0];
|
|
57534
58096
|
this.factInFlight = call2 ? {
|
|
57535
58097
|
callId: call2.callId,
|
|
57536
58098
|
tool: call2.tool,
|
|
@@ -57542,13 +58104,13 @@ function createLedgerWriteThrough(opts) {
|
|
|
57542
58104
|
} : void 0;
|
|
57543
58105
|
for (let attempt = 1; ; attempt++) {
|
|
57544
58106
|
try {
|
|
57545
|
-
const result = await ledger.step(this.runId, gen,
|
|
57546
|
-
if (!result.ok) this.detach(`step ${
|
|
57547
|
-
else this.turnsWritten =
|
|
58107
|
+
const result = await ledger.step(this.runId, gen, record3, turns, this.sessionRow?.key);
|
|
58108
|
+
if (!result.ok) this.detach(`step ${record3.step} refused (${result.reason})`);
|
|
58109
|
+
else this.turnsWritten = record3.turnIndex;
|
|
57548
58110
|
return;
|
|
57549
58111
|
} catch (err2) {
|
|
57550
58112
|
if (err2 instanceof RouteMissingError || err2 instanceof PermanentStoreError || attempt >= 2) {
|
|
57551
|
-
this.detach(`step ${
|
|
58113
|
+
this.detach(`step ${record3.step} failed: ${describe9(err2)}`);
|
|
57552
58114
|
return;
|
|
57553
58115
|
}
|
|
57554
58116
|
await sleep2(RETRY_MS[0]);
|
|
@@ -59376,8 +59938,8 @@ async function launchOpenCode(deps, spec, bearer) {
|
|
|
59376
59938
|
let consumed = 0;
|
|
59377
59939
|
for (const line4 of text.split("\n").slice(0, -1)) {
|
|
59378
59940
|
consumed += Buffer.byteLength(line4, "utf8") + 1;
|
|
59379
|
-
const
|
|
59380
|
-
if (
|
|
59941
|
+
const record3 = parseFeedRecord(line4);
|
|
59942
|
+
if (record3?.feed === "tailer" && record3.note === "connected") {
|
|
59381
59943
|
feedOffset = consumed;
|
|
59382
59944
|
break;
|
|
59383
59945
|
}
|
|
@@ -59457,9 +60019,9 @@ var init_process2 = __esm({
|
|
|
59457
60019
|
});
|
|
59458
60020
|
|
|
59459
60021
|
// ../../src/core/harness/opencode/bridge.ts
|
|
59460
|
-
function executionStartedFor(
|
|
59461
|
-
if (
|
|
59462
|
-
const data =
|
|
60022
|
+
function executionStartedFor(record3, sessionID) {
|
|
60023
|
+
if (record3.feed !== "event" || record3.event.type !== "session.execution.started") return false;
|
|
60024
|
+
const data = record3.event.data;
|
|
59463
60025
|
return data?.sessionID === sessionID;
|
|
59464
60026
|
}
|
|
59465
60027
|
function openCodeToolNameWord(name) {
|
|
@@ -60131,18 +60693,18 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
60131
60693
|
const after = transport.consumedOffset;
|
|
60132
60694
|
const reattachCatchUp = conn.reattach !== void 0 && after <= conn.reattach.catchUpTo;
|
|
60133
60695
|
lastRecord = next.value;
|
|
60134
|
-
const
|
|
60135
|
-
if (!
|
|
60136
|
-
if (!reattachCatchUp && executionStartedFor(
|
|
60696
|
+
const record3 = parseFeedRecord(next.value);
|
|
60697
|
+
if (!record3) continue;
|
|
60698
|
+
if (!reattachCatchUp && executionStartedFor(record3, conn.sessionID)) {
|
|
60137
60699
|
executionOwned = true;
|
|
60138
60700
|
awaiting = void 0;
|
|
60139
60701
|
}
|
|
60140
60702
|
bridge.observing = reattachCatchUp ? "catching-up" : executionOwned ? "own" : "earlier";
|
|
60141
|
-
const obs = bridge.observe(
|
|
60703
|
+
const obs = bridge.observe(record3);
|
|
60142
60704
|
if (!reattachCatchUp && obs.boundary === true) conn.boundaries?.reached();
|
|
60143
60705
|
if (!reattachCatchUp && obs.inboxDelivered !== void 0) conn.inboxFate?.deliver(obs.inboxDelivered);
|
|
60144
60706
|
if (!reattachCatchUp && obs.inboxDropped === true) conn.inboxFate?.interruptAll();
|
|
60145
|
-
if (
|
|
60707
|
+
if (record3.feed === "messages" && conn.saveOffset !== void 0) {
|
|
60146
60708
|
const save = conn.saveOffset;
|
|
60147
60709
|
void bridge.flush().then(() => save(after));
|
|
60148
60710
|
}
|
|
@@ -60229,14 +60791,14 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
60229
60791
|
);
|
|
60230
60792
|
}
|
|
60231
60793
|
if (replacedBy !== void 0) {
|
|
60232
|
-
const
|
|
60794
|
+
const record3 = bridge.record(recordBase, deadline);
|
|
60233
60795
|
bridge.closeOpenSpans((open2) => openCodeReplacedCallNote(open2.tool));
|
|
60234
60796
|
const replaced = replacedBy.condition === "word" ? new OpenCodeContainerReplacedError(
|
|
60235
60797
|
replacedBy.said.message,
|
|
60236
60798
|
conn.containerWord,
|
|
60237
60799
|
await conn.container.identity().catch(() => void 0),
|
|
60238
|
-
|
|
60239
|
-
) : new OpenCodeContainerReplacedError(void 0, replacedBy.was, replacedBy.now,
|
|
60800
|
+
record3
|
|
60801
|
+
) : new OpenCodeContainerReplacedError(void 0, replacedBy.was, replacedBy.now, record3, "identity");
|
|
60240
60802
|
note("sandbox_restarted", replaced.message);
|
|
60241
60803
|
throw replaced;
|
|
60242
60804
|
}
|
|
@@ -60376,13 +60938,13 @@ var init_bridge3 = __esm({
|
|
|
60376
60938
|
diagnostics;
|
|
60377
60939
|
};
|
|
60378
60940
|
OpenCodeContainerReplacedError = class extends HarnessContainerReplacedError {
|
|
60379
|
-
constructor(said2, was, now,
|
|
60941
|
+
constructor(said2, was, now, record3, condition = "word") {
|
|
60380
60942
|
super(
|
|
60381
60943
|
`the container running OpenCode was replaced (${was ?? "unknown"} \u2192 ${now ?? "unknown"}; ${replacedBecause(condition, said2)})`,
|
|
60382
60944
|
said2,
|
|
60383
60945
|
was,
|
|
60384
60946
|
now,
|
|
60385
|
-
|
|
60947
|
+
record3,
|
|
60386
60948
|
condition
|
|
60387
60949
|
);
|
|
60388
60950
|
this.name = "OpenCodeContainerReplacedError";
|
|
@@ -60831,25 +61393,25 @@ var init_bridge3 = __esm({
|
|
|
60831
61393
|
emit(event) {
|
|
60832
61394
|
this.deps.emit(event.at === void 0 ? { ...event, at: this.deps.clock() } : event);
|
|
60833
61395
|
}
|
|
60834
|
-
observe(
|
|
60835
|
-
switch (
|
|
61396
|
+
observe(record3) {
|
|
61397
|
+
switch (record3.feed) {
|
|
60836
61398
|
case "event":
|
|
60837
|
-
return this.onEvent(
|
|
61399
|
+
return this.onEvent(record3.event);
|
|
60838
61400
|
case "permissions":
|
|
60839
61401
|
if (this.observing === "earlier") return { replies: [], settled: false };
|
|
60840
|
-
return this.onPermissionsRefill(
|
|
61402
|
+
return this.onPermissionsRefill(record3.data);
|
|
60841
61403
|
case "messages": {
|
|
60842
|
-
const out = this.onMessagesRefill(
|
|
60843
|
-
if (this.settleOwedTo !== void 0 &&
|
|
61404
|
+
const out = this.onMessagesRefill(record3.data);
|
|
61405
|
+
if (this.settleOwedTo !== void 0 && record3.reason === this.settleOwedTo) {
|
|
60844
61406
|
this.settleOwedTo = void 0;
|
|
60845
61407
|
out.settled = true;
|
|
60846
61408
|
}
|
|
60847
61409
|
return out;
|
|
60848
61410
|
}
|
|
60849
61411
|
case "tailer":
|
|
60850
|
-
if (
|
|
60851
|
-
this.deps.onProgress?.(`opencode feed: ${redactAndCap(
|
|
60852
|
-
if (this.settleOwedTo !== void 0 && (
|
|
61412
|
+
if (record3.note === "stream closed" || record3.note.includes("failed"))
|
|
61413
|
+
this.deps.onProgress?.(`opencode feed: ${redactAndCap(record3.note, 120)}`);
|
|
61414
|
+
if (this.settleOwedTo !== void 0 && (record3.note === "message refill failed" || record3.note === "refill failed") && record3.reason === this.settleOwedTo) {
|
|
60853
61415
|
this.settleOwedTo = void 0;
|
|
60854
61416
|
return { replies: [], settled: true };
|
|
60855
61417
|
}
|
|
@@ -61809,7 +62371,7 @@ ${stagedLine}` : prompt;
|
|
|
61809
62371
|
return "lost";
|
|
61810
62372
|
return "running";
|
|
61811
62373
|
};
|
|
61812
|
-
const resolve13 = async (steerSeq, knownAtSteer, seen,
|
|
62374
|
+
const resolve13 = async (steerSeq, knownAtSteer, seen, waiting3) => {
|
|
61813
62375
|
let left = false;
|
|
61814
62376
|
for (; ; ) {
|
|
61815
62377
|
const verdict = await learnRow(steerSeq, knownAtSteer);
|
|
@@ -61824,7 +62386,7 @@ ${stagedLine}` : prompt;
|
|
|
61824
62386
|
"the follow-up's steer",
|
|
61825
62387
|
"the loop left the feed with no row of the steer and the store still showing its execution under way, so the store cannot say whether the server took it"
|
|
61826
62388
|
);
|
|
61827
|
-
|
|
62389
|
+
waiting3();
|
|
61828
62390
|
if (await conn.boundaries.wait(seen) === "left") left = true;
|
|
61829
62391
|
seen = conn.boundaries.count;
|
|
61830
62392
|
}
|
|
@@ -62136,6 +62698,14 @@ var init_requester = __esm({
|
|
|
62136
62698
|
|
|
62137
62699
|
// ../../src/channels/http.ts
|
|
62138
62700
|
import { timingSafeEqual as timingSafeEqual4 } from "node:crypto";
|
|
62701
|
+
function ingressComponentError(name, value) {
|
|
62702
|
+
if (value.length === 0) return `\`${name}\` must be a non-empty string`;
|
|
62703
|
+
if (value.length > INGRESS_COMPONENT_MAX_LENGTH) {
|
|
62704
|
+
return `\`${name}\` must be at most ${INGRESS_COMPONENT_MAX_LENGTH} characters`;
|
|
62705
|
+
}
|
|
62706
|
+
if (/[:#]/.test(value)) return `\`${name}\` must not contain ':' or '#'`;
|
|
62707
|
+
return void 0;
|
|
62708
|
+
}
|
|
62139
62709
|
function safeEqual2(a, b) {
|
|
62140
62710
|
const ab = Buffer.from(a, "utf8");
|
|
62141
62711
|
const bb = Buffer.from(b, "utf8");
|
|
@@ -62179,11 +62749,15 @@ function parseBody3(raw) {
|
|
|
62179
62749
|
if (typeof obj2.text !== "string" || obj2.text.trim() === "") {
|
|
62180
62750
|
return { error: "`text` is required and must be a non-empty string" };
|
|
62181
62751
|
}
|
|
62182
|
-
if (obj2.channel !== void 0
|
|
62183
|
-
return { error: "`channel` must be a string" };
|
|
62752
|
+
if (obj2.channel !== void 0) {
|
|
62753
|
+
if (typeof obj2.channel !== "string") return { error: "`channel` must be a string" };
|
|
62754
|
+
const error = ingressComponentError("channel", obj2.channel);
|
|
62755
|
+
if (error) return { error };
|
|
62184
62756
|
}
|
|
62185
|
-
if (obj2.thread !== void 0
|
|
62186
|
-
return { error: "`thread` must be a string" };
|
|
62757
|
+
if (obj2.thread !== void 0) {
|
|
62758
|
+
if (typeof obj2.thread !== "string") return { error: "`thread` must be a string" };
|
|
62759
|
+
const error = ingressComponentError("thread", obj2.thread);
|
|
62760
|
+
if (error) return { error };
|
|
62187
62761
|
}
|
|
62188
62762
|
if (obj2.async !== void 0 && typeof obj2.async !== "boolean") {
|
|
62189
62763
|
return { error: "`async` must be a boolean" };
|
|
@@ -62236,6 +62810,10 @@ async function handleAuthorized(identity2, body, deps, options) {
|
|
|
62236
62810
|
if ("error" in parsed) {
|
|
62237
62811
|
return { status: 400, body: { error: parsed.error } };
|
|
62238
62812
|
}
|
|
62813
|
+
if (identity2.channel !== void 0) {
|
|
62814
|
+
const error = ingressComponentError("channel", identity2.channel);
|
|
62815
|
+
if (error) return { status: 400, body: { error } };
|
|
62816
|
+
}
|
|
62239
62817
|
const receivedAt = systemClock();
|
|
62240
62818
|
const trace = startRequestRoot(deps, { channel: "http", receivedAt });
|
|
62241
62819
|
const msg = {
|
|
@@ -62316,7 +62894,7 @@ function parseIngressTokens(secrets) {
|
|
|
62316
62894
|
if (!parsed.ok) console.error(`[ingress] SWITCHBOARD_INGRESS_TOKENS is ${parsed.reason} \u2014 ingress disabled`);
|
|
62317
62895
|
return { tokens: parsed.tokens };
|
|
62318
62896
|
}
|
|
62319
|
-
var PLATFORM, DEFAULT_CHANNEL, DEFAULT_THREAD, MAX_BODY_BYTES2, HttpIO, DISPATCH_ACTION;
|
|
62897
|
+
var PLATFORM, DEFAULT_CHANNEL, DEFAULT_THREAD, INGRESS_COMPONENT_MAX_LENGTH, MAX_BODY_BYTES2, HttpIO, DISPATCH_ACTION;
|
|
62320
62898
|
var init_http = __esm({
|
|
62321
62899
|
"../../src/channels/http.ts"() {
|
|
62322
62900
|
"use strict";
|
|
@@ -62329,6 +62907,7 @@ var init_http = __esm({
|
|
|
62329
62907
|
PLATFORM = "http";
|
|
62330
62908
|
DEFAULT_CHANNEL = "default";
|
|
62331
62909
|
DEFAULT_THREAD = "default";
|
|
62910
|
+
INGRESS_COMPONENT_MAX_LENGTH = 128;
|
|
62332
62911
|
MAX_BODY_BYTES2 = 1e6;
|
|
62333
62912
|
HttpIO = class {
|
|
62334
62913
|
constructor(priorTurns = []) {
|
|
@@ -62525,7 +63104,7 @@ function openAiStopReason(raw) {
|
|
|
62525
63104
|
}
|
|
62526
63105
|
}
|
|
62527
63106
|
function meterAnthropicMessage(json4) {
|
|
62528
|
-
const msg =
|
|
63107
|
+
const msg = record2(json4);
|
|
62529
63108
|
if (!msg) return {};
|
|
62530
63109
|
const usage = usageFromAnthropic(msg.usage);
|
|
62531
63110
|
return {
|
|
@@ -62534,11 +63113,11 @@ function meterAnthropicMessage(json4) {
|
|
|
62534
63113
|
};
|
|
62535
63114
|
}
|
|
62536
63115
|
function meterOpenAiCompletion(json4) {
|
|
62537
|
-
const body =
|
|
63116
|
+
const body = record2(json4);
|
|
62538
63117
|
if (!body) return {};
|
|
62539
63118
|
const usage = usageFromOpenAI(body.usage);
|
|
62540
63119
|
const reported = reportedCostOf(body.usage);
|
|
62541
|
-
const choice = Array.isArray(body.choices) ?
|
|
63120
|
+
const choice = Array.isArray(body.choices) ? record2(body.choices[0]) : void 0;
|
|
62542
63121
|
return {
|
|
62543
63122
|
...usage ? { usage } : {},
|
|
62544
63123
|
...reported ? { reported } : {},
|
|
@@ -62548,15 +63127,15 @@ function meterOpenAiCompletion(json4) {
|
|
|
62548
63127
|
function responsesStopReason(response) {
|
|
62549
63128
|
const output = Array.isArray(response.output) ? response.output : [];
|
|
62550
63129
|
if (response.status === "completed") {
|
|
62551
|
-
return output.some((item) =>
|
|
63130
|
+
return output.some((item) => record2(item)?.type === "function_call") ? "tool_use" : "end_turn";
|
|
62552
63131
|
}
|
|
62553
63132
|
if (response.status === "incomplete") {
|
|
62554
|
-
return
|
|
63133
|
+
return record2(response.incomplete_details)?.reason === "max_output_tokens" ? "max_tokens" : "other";
|
|
62555
63134
|
}
|
|
62556
63135
|
return "other";
|
|
62557
63136
|
}
|
|
62558
63137
|
function meterResponses(json4) {
|
|
62559
|
-
const body =
|
|
63138
|
+
const body = record2(json4);
|
|
62560
63139
|
if (!body) return {};
|
|
62561
63140
|
const usage = usageFromResponses(body.usage);
|
|
62562
63141
|
const reported = reportedCostOf(body.usage);
|
|
@@ -62578,87 +63157,20 @@ function shapeTools(shape, body, marks) {
|
|
|
62578
63157
|
const allowed2 = new Set(marks.turn.tools);
|
|
62579
63158
|
const table = Array.isArray(body.tools) ? body.tools : [];
|
|
62580
63159
|
const kept = table.filter((t) => {
|
|
62581
|
-
const name = toolNameOf(shape,
|
|
63160
|
+
const name = toolNameOf(shape, record2(t));
|
|
62582
63161
|
return typeof name === "string" && allowed2.has(name);
|
|
62583
63162
|
});
|
|
62584
63163
|
const { tool_choice: _choice, ...rest } = body;
|
|
62585
63164
|
const shaped = { ...rest, tools: kept };
|
|
62586
63165
|
return { body: shaped, toolChoice: toolChoiceWord(shape, void 0, kept.length) };
|
|
62587
63166
|
}
|
|
62588
|
-
function responsesRejectsPattern(pattern) {
|
|
62589
|
-
return /\(\?(?:[=!]|<[=!])/.test(pattern);
|
|
62590
|
-
}
|
|
62591
|
-
function shapeResponsesSchema(value, tool, degradations) {
|
|
62592
|
-
const schema = record(value);
|
|
62593
|
-
if (!schema) {
|
|
62594
|
-
if (!Array.isArray(value)) return { value, changed: false };
|
|
62595
|
-
let changed2 = false;
|
|
62596
|
-
const items = value.map((item) => {
|
|
62597
|
-
const shaped = shapeResponsesSchema(item, tool, degradations);
|
|
62598
|
-
changed2 ||= shaped.changed;
|
|
62599
|
-
return shaped.value;
|
|
62600
|
-
});
|
|
62601
|
-
return { value: changed2 ? items : value, changed: changed2 };
|
|
62602
|
-
}
|
|
62603
|
-
let changed = false;
|
|
62604
|
-
const out = {};
|
|
62605
|
-
for (const [keyword, item] of Object.entries(schema)) {
|
|
62606
|
-
if (keyword === "pattern" && typeof item === "string" && responsesRejectsPattern(item)) {
|
|
62607
|
-
degradations.push({
|
|
62608
|
-
tool,
|
|
62609
|
-
keyword,
|
|
62610
|
-
why: "the Responses wire does not accept regular-expression lookaround"
|
|
62611
|
-
});
|
|
62612
|
-
changed = true;
|
|
62613
|
-
continue;
|
|
62614
|
-
}
|
|
62615
|
-
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record(item)) {
|
|
62616
|
-
let mapChanged = false;
|
|
62617
|
-
const mapped = {};
|
|
62618
|
-
for (const [name, child] of Object.entries(record(item))) {
|
|
62619
|
-
const shaped = shapeResponsesSchema(child, tool, degradations);
|
|
62620
|
-
mapChanged ||= shaped.changed;
|
|
62621
|
-
mapped[name] = shaped.value;
|
|
62622
|
-
}
|
|
62623
|
-
out[keyword] = mapChanged ? mapped : item;
|
|
62624
|
-
changed ||= mapChanged;
|
|
62625
|
-
continue;
|
|
62626
|
-
}
|
|
62627
|
-
if (["additionalProperties", "allOf", "anyOf", "if", "items", "not", "oneOf", "then", "else"].includes(keyword) && typeof item === "object" && item !== null) {
|
|
62628
|
-
const shaped = shapeResponsesSchema(item, tool, degradations);
|
|
62629
|
-
out[keyword] = shaped.value;
|
|
62630
|
-
changed ||= shaped.changed;
|
|
62631
|
-
continue;
|
|
62632
|
-
}
|
|
62633
|
-
out[keyword] = item;
|
|
62634
|
-
}
|
|
62635
|
-
return { value: changed ? out : value, changed };
|
|
62636
|
-
}
|
|
62637
|
-
function shapeToolSchemasForWire(shape, body) {
|
|
62638
|
-
if (shape !== "openai-responses" || !Array.isArray(body.tools)) return { body, degradations: [] };
|
|
62639
|
-
const degradations = [];
|
|
62640
|
-
let changed = false;
|
|
62641
|
-
const tools = body.tools.map((item) => {
|
|
62642
|
-
const tool = record(item);
|
|
62643
|
-
if (!tool) return item;
|
|
62644
|
-
const name = typeof tool.name === "string" ? tool.name : "unnamed";
|
|
62645
|
-
const shaped = shapeResponsesSchema(tool.parameters, name, degradations);
|
|
62646
|
-
if (!shaped.changed) return item;
|
|
62647
|
-
changed = true;
|
|
62648
|
-
return { ...tool, parameters: shaped.value };
|
|
62649
|
-
});
|
|
62650
|
-
const unique = degradations.filter(
|
|
62651
|
-
(candidate, index, all) => all.findIndex((other) => other.tool === candidate.tool && other.keyword === candidate.keyword) === index
|
|
62652
|
-
);
|
|
62653
|
-
return { body: changed ? { ...body, tools } : body, degradations: unique };
|
|
62654
|
-
}
|
|
62655
63167
|
function toolNameOf(shape, tool) {
|
|
62656
63168
|
if (tool === void 0) return void 0;
|
|
62657
|
-
return shape === "openai-chat" ?
|
|
63169
|
+
return shape === "openai-chat" ? record2(tool.function)?.name : tool.name;
|
|
62658
63170
|
}
|
|
62659
63171
|
function toolSchemaOf(shape, tool) {
|
|
62660
63172
|
if (shape === "anthropic-messages") return tool.input_schema;
|
|
62661
|
-
if (shape === "openai-chat") return
|
|
63173
|
+
if (shape === "openai-chat") return record2(tool.function)?.parameters;
|
|
62662
63174
|
return tool.parameters;
|
|
62663
63175
|
}
|
|
62664
63176
|
function schemaKeywords(value, into = /* @__PURE__ */ new Set()) {
|
|
@@ -62666,12 +63178,12 @@ function schemaKeywords(value, into = /* @__PURE__ */ new Set()) {
|
|
|
62666
63178
|
for (const item of value) schemaKeywords(item, into);
|
|
62667
63179
|
return into;
|
|
62668
63180
|
}
|
|
62669
|
-
const schema =
|
|
63181
|
+
const schema = record2(value);
|
|
62670
63182
|
if (!schema) return into;
|
|
62671
63183
|
for (const [keyword, item] of Object.entries(schema)) {
|
|
62672
63184
|
into.add(keyword);
|
|
62673
|
-
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") &&
|
|
62674
|
-
for (const child of Object.values(
|
|
63185
|
+
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record2(item)) {
|
|
63186
|
+
for (const child of Object.values(record2(item))) schemaKeywords(child, into);
|
|
62675
63187
|
} else {
|
|
62676
63188
|
schemaKeywords(item, into);
|
|
62677
63189
|
}
|
|
@@ -62682,7 +63194,7 @@ function providerErrorProse(body) {
|
|
|
62682
63194
|
const strings2 = (value, into = []) => {
|
|
62683
63195
|
if (typeof value === "string") into.push(value);
|
|
62684
63196
|
else if (Array.isArray(value)) for (const item of value) strings2(item, into);
|
|
62685
|
-
else if (
|
|
63197
|
+
else if (record2(value)) for (const item of Object.values(record2(value))) strings2(item, into);
|
|
62686
63198
|
return into;
|
|
62687
63199
|
};
|
|
62688
63200
|
try {
|
|
@@ -62700,7 +63212,7 @@ function providerSchemaRejectionOf2(shape, body, errorBody) {
|
|
|
62700
63212
|
prose2
|
|
62701
63213
|
))
|
|
62702
63214
|
return void 0;
|
|
62703
|
-
const candidates2 = (Array.isArray(body.tools) ? body.tools : []).map((item) =>
|
|
63215
|
+
const candidates2 = (Array.isArray(body.tools) ? body.tools : []).map((item) => record2(item)).filter((item) => item !== void 0).map((tool) => ({ name: toolNameOf(shape, tool), schema: toolSchemaOf(shape, tool) })).filter(
|
|
62704
63216
|
(tool) => typeof tool.name === "string" && quoted(prose2, tool.name)
|
|
62705
63217
|
);
|
|
62706
63218
|
if (candidates2.length !== 1) return void 0;
|
|
@@ -62723,7 +63235,7 @@ function toolsOffered(shape, body) {
|
|
|
62723
63235
|
const table = Array.isArray(body.tools) ? body.tools : [];
|
|
62724
63236
|
const names = [];
|
|
62725
63237
|
for (const t of table) {
|
|
62726
|
-
const name = toolNameOf(shape,
|
|
63238
|
+
const name = toolNameOf(shape, record2(t));
|
|
62727
63239
|
if (typeof name === "string") names.push(name);
|
|
62728
63240
|
}
|
|
62729
63241
|
names.sort();
|
|
@@ -62736,7 +63248,7 @@ function toolsOffered(shape, body) {
|
|
|
62736
63248
|
function toolChoiceWord(shape, choice, tools) {
|
|
62737
63249
|
if (choice === void 0 || choice === null) return tools > 0 ? "auto" : "none";
|
|
62738
63250
|
if (shape === "anthropic-messages") {
|
|
62739
|
-
const type =
|
|
63251
|
+
const type = record2(choice)?.type;
|
|
62740
63252
|
if (type === "none" || type === "any" || type === "tool") return type;
|
|
62741
63253
|
if (type === "auto") return tools > 0 ? "auto" : "none";
|
|
62742
63254
|
return tools > 0 ? "auto" : "none";
|
|
@@ -62744,7 +63256,7 @@ function toolChoiceWord(shape, choice, tools) {
|
|
|
62744
63256
|
if (choice === "none") return "none";
|
|
62745
63257
|
if (choice === "required") return "any";
|
|
62746
63258
|
if (choice === "auto") return tools > 0 ? "auto" : "none";
|
|
62747
|
-
return
|
|
63259
|
+
return record2(choice) ? "tool" : tools > 0 ? "auto" : "none";
|
|
62748
63260
|
}
|
|
62749
63261
|
function turnAttrs(grant2, meter, ttftMs, offered, price) {
|
|
62750
63262
|
const u = meter.usage;
|
|
@@ -62853,7 +63365,7 @@ async function handleAdmitted(door, req, deps) {
|
|
|
62853
63365
|
} catch {
|
|
62854
63366
|
return refusalResponse(shape, 400, "invalid_body", "the body is not JSON");
|
|
62855
63367
|
}
|
|
62856
|
-
const body =
|
|
63368
|
+
const body = record2(parsed);
|
|
62857
63369
|
if (!body) return refusalResponse(shape, 400, "invalid_body", "the body must be a JSON object");
|
|
62858
63370
|
const upstream = upstreamFor(
|
|
62859
63371
|
shape,
|
|
@@ -63124,7 +63636,7 @@ function createModelProxyHandler(deps) {
|
|
|
63124
63636
|
})();
|
|
63125
63637
|
};
|
|
63126
63638
|
}
|
|
63127
|
-
var ANTHROPIC_MESSAGES_PATH, OPENAI_CHAT_COMPLETIONS_PATH, OPENAI_RESPONSES_PATH, PROXY_PATHS, RESPONSES_MIN_OUTPUT_TOKENS, MAX_PROXY_BODY_BYTES, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_ANTHROPIC_VERSION, FORWARDED_REQUEST_HEADERS, FORWARDED_RESPONSE_HEADERS,
|
|
63639
|
+
var ANTHROPIC_MESSAGES_PATH, OPENAI_CHAT_COMPLETIONS_PATH, OPENAI_RESPONSES_PATH, PROXY_PATHS, RESPONSES_MIN_OUTPUT_TOKENS, MAX_PROXY_BODY_BYTES, DEFAULT_ANTHROPIC_BASE_URL, DEFAULT_ANTHROPIC_VERSION, FORWARDED_REQUEST_HEADERS, FORWARDED_RESPONSE_HEADERS, record2, SseMeter, MODEL_STREAM_HEARTBEAT;
|
|
63128
63640
|
var init_modelProxy = __esm({
|
|
63129
63641
|
"../../src/channels/modelProxy.ts"() {
|
|
63130
63642
|
"use strict";
|
|
@@ -63132,6 +63644,7 @@ var init_modelProxy = __esm({
|
|
|
63132
63644
|
init_providerFailureAuth();
|
|
63133
63645
|
init_usage();
|
|
63134
63646
|
init_modelPricing();
|
|
63647
|
+
init_providerToolSchemas();
|
|
63135
63648
|
init_provider();
|
|
63136
63649
|
init_http();
|
|
63137
63650
|
ANTHROPIC_MESSAGES_PATH = "/v1/messages";
|
|
@@ -63148,7 +63661,7 @@ var init_modelProxy = __esm({
|
|
|
63148
63661
|
DEFAULT_ANTHROPIC_VERSION = "2023-06-01";
|
|
63149
63662
|
FORWARDED_REQUEST_HEADERS = ["accept", "anthropic-version", "anthropic-beta"];
|
|
63150
63663
|
FORWARDED_RESPONSE_HEADERS = ["content-type", "request-id", "x-request-id"];
|
|
63151
|
-
|
|
63664
|
+
record2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
|
|
63152
63665
|
SseMeter = class {
|
|
63153
63666
|
constructor(shape) {
|
|
63154
63667
|
this.shape = shape;
|
|
@@ -63192,7 +63705,7 @@ var init_modelProxy = __esm({
|
|
|
63192
63705
|
};
|
|
63193
63706
|
}
|
|
63194
63707
|
apply(json4) {
|
|
63195
|
-
const event =
|
|
63708
|
+
const event = record2(json4);
|
|
63196
63709
|
if (!event) return;
|
|
63197
63710
|
if (this.shape === "openai-responses") {
|
|
63198
63711
|
if (event.type !== "response.completed" && event.type !== "response.incomplete" && event.type !== "response.failed")
|
|
@@ -63205,12 +63718,12 @@ var init_modelProxy = __esm({
|
|
|
63205
63718
|
}
|
|
63206
63719
|
if (this.shape === "anthropic-messages") {
|
|
63207
63720
|
if (event.type === "message_start") {
|
|
63208
|
-
const usage2 = usageFromAnthropic(
|
|
63721
|
+
const usage2 = usageFromAnthropic(record2(event.message)?.usage);
|
|
63209
63722
|
if (usage2) this.usage = { ...this.usage, ...usage2 };
|
|
63210
63723
|
} else if (event.type === "message_delta") {
|
|
63211
|
-
const stop =
|
|
63724
|
+
const stop = record2(event.delta)?.stop_reason;
|
|
63212
63725
|
if (typeof stop === "string") this.stopReason = anthropicStopReason(stop);
|
|
63213
|
-
const usage2 =
|
|
63726
|
+
const usage2 = record2(event.usage);
|
|
63214
63727
|
if (usage2) {
|
|
63215
63728
|
if (typeof usage2.output_tokens === "number") this.usage.outputTokens = usage2.output_tokens;
|
|
63216
63729
|
if (typeof usage2.input_tokens === "number") this.usage.inputTokens = usage2.input_tokens;
|
|
@@ -63223,7 +63736,7 @@ var init_modelProxy = __esm({
|
|
|
63223
63736
|
}
|
|
63224
63737
|
return;
|
|
63225
63738
|
}
|
|
63226
|
-
const choice = Array.isArray(event.choices) ?
|
|
63739
|
+
const choice = Array.isArray(event.choices) ? record2(event.choices[0]) : void 0;
|
|
63227
63740
|
if (typeof choice?.finish_reason === "string") this.stopReason = openAiStopReason(choice.finish_reason);
|
|
63228
63741
|
const usage = usageFromOpenAI(event.usage);
|
|
63229
63742
|
if (usage) this.usage = { ...this.usage, ...usage };
|
|
@@ -63245,7 +63758,7 @@ function bearerOf(headers) {
|
|
|
63245
63758
|
const m = value ? /^Bearer\s+(\S+)$/i.exec(value.trim()) : null;
|
|
63246
63759
|
return m ? m[1] : void 0;
|
|
63247
63760
|
}
|
|
63248
|
-
function
|
|
63761
|
+
function judge4(deps, presented) {
|
|
63249
63762
|
const verdict = deps.bearers.verify(presented);
|
|
63250
63763
|
if (!verdict.ok) {
|
|
63251
63764
|
const status3 = verdict.reason === "unknown_run" ? 404 : verdict.reason === "malformed" ? 401 : verdict.reason === "unknown_bearer" ? 401 : 403;
|
|
@@ -63267,12 +63780,12 @@ function held(path, reason2, runId3) {
|
|
|
63267
63780
|
async function admitHarnessRequest(deps, headers, path) {
|
|
63268
63781
|
const presented = bearerOf(headers);
|
|
63269
63782
|
if (!presented) return { ok: false, response: { status: 401, body: { error: "missing_bearer" } } };
|
|
63270
|
-
let verdict =
|
|
63783
|
+
let verdict = judge4(deps, presented);
|
|
63271
63784
|
if (verdict.ok) return verdict;
|
|
63272
63785
|
if (!RESUME_CAN_OPEN.has(verdict.reason)) return refused6(verdict);
|
|
63273
63786
|
if (!deps.takeover.settled) {
|
|
63274
63787
|
await deps.takeover.whenSettled(DOOR_HOLD_MS);
|
|
63275
|
-
verdict =
|
|
63788
|
+
verdict = judge4(deps, presented);
|
|
63276
63789
|
if (verdict.ok) return verdict;
|
|
63277
63790
|
if (!RESUME_CAN_OPEN.has(verdict.reason)) return refused6(verdict);
|
|
63278
63791
|
if (!deps.takeover.settled) return held(path, "reclaim_pending", verdict.runId);
|
|
@@ -64608,13 +65121,13 @@ async function catchUpMissedMentions(opts) {
|
|
|
64608
65121
|
const orphanCutoffMs = now - (opts.orphanWindowMs ?? ORPHAN_CARD_WINDOW_MS);
|
|
64609
65122
|
const threadCutoffMs = sweep ? Math.min(cutoffMs, orphanCutoffMs) : cutoffMs;
|
|
64610
65123
|
const { client, botUserId } = opts;
|
|
64611
|
-
const
|
|
65124
|
+
const record3 = opts.record ?? recordCatchUpOutcome;
|
|
64612
65125
|
let channels;
|
|
64613
65126
|
try {
|
|
64614
65127
|
channels = await listChannels(client);
|
|
64615
65128
|
} catch (err2) {
|
|
64616
65129
|
log(`[catch-up] cannot list channels: ${errMsg(err2)}`);
|
|
64617
|
-
|
|
65130
|
+
record3({ at: now, channels: 0, missed: 0, skippedChannels: 0, error: errMsg(err2) });
|
|
64618
65131
|
return { channels: 0, missed: 0, orphans: 0, skippedChannels: 0 };
|
|
64619
65132
|
}
|
|
64620
65133
|
const scans = await mapLimit(channels, CATCH_UP_CHANNEL_CONCURRENCY, async (channel) => {
|
|
@@ -64683,7 +65196,7 @@ async function catchUpMissedMentions(opts) {
|
|
|
64683
65196
|
log(
|
|
64684
65197
|
`[catch-up] scanned ${channels.length} channel(s): ${missed} missed message(s), ${orphans} orphaned card(s), ${skippedChannels} skipped`
|
|
64685
65198
|
);
|
|
64686
|
-
|
|
65199
|
+
record3({ at: now, channels: channels.length, missed, skippedChannels });
|
|
64687
65200
|
return { channels: channels.length, missed, orphans, skippedChannels };
|
|
64688
65201
|
}
|
|
64689
65202
|
async function listChannels(client) {
|
|
@@ -65403,7 +65916,8 @@ async function handle(deps, { client, statusClient }, ev, intake) {
|
|
|
65403
65916
|
span,
|
|
65404
65917
|
{
|
|
65405
65918
|
staging: deps.artifacts !== void 0,
|
|
65406
|
-
maxBytesPerMessage: deps.config.config.artifacts?.inbound?.maxBytesPerMessage ?? ARTIFACT_DEFAULTS.maxBytesPerMessage
|
|
65919
|
+
maxBytesPerMessage: deps.config.config.artifacts?.inbound?.maxBytesPerMessage ?? ARTIFACT_DEFAULTS.maxBytesPerMessage,
|
|
65920
|
+
verbosityFor: (channelId, userId, request2) => deps.config.verbosityFor(channelId, userId, request2)
|
|
65407
65921
|
},
|
|
65408
65922
|
relayAppsOf(deps),
|
|
65409
65923
|
intake
|
|
@@ -65485,10 +65999,25 @@ async function receiveSlackMessage(client, ev, span, policy, relayApps, intake)
|
|
|
65485
65999
|
threadRuns = gated.thread;
|
|
65486
66000
|
}
|
|
65487
66001
|
}
|
|
65488
|
-
|
|
65489
|
-
|
|
65490
|
-
|
|
65491
|
-
|
|
66002
|
+
const requester = await resolveSlackRequester(
|
|
66003
|
+
client,
|
|
66004
|
+
{
|
|
66005
|
+
channel: ev.channel,
|
|
66006
|
+
ts: ev.ts,
|
|
66007
|
+
threadTs: ev.threadTs,
|
|
66008
|
+
...ev.user !== void 0 ? { user: ev.user } : {},
|
|
66009
|
+
text: ev.rawText ?? ev.text,
|
|
66010
|
+
...ev.poster !== void 0 ? { poster: ev.poster } : {}
|
|
66011
|
+
},
|
|
66012
|
+
relayApps
|
|
66013
|
+
);
|
|
66014
|
+
span.setAttrs({ requester: requester.resolvedBy });
|
|
66015
|
+
const verbosity2 = policy.verbosityFor?.(`${PLATFORM3}:${ev.channel}`, requester.userId, parseDirectives(ev.text).verbosity) ?? policy.verbosity ?? DEFAULT_VERBOSITY;
|
|
66016
|
+
if (shows(verbosity2, "verbose"))
|
|
66017
|
+
client.reactions.add({ channel: ev.channel, timestamp: ev.ts, name: ACK_EMOJI }).catch((err2) => {
|
|
66018
|
+
if (!err2.message.includes("already_reacted")) console.error(`[ack] ${err2.message}`);
|
|
66019
|
+
});
|
|
66020
|
+
const delayNote = ev.caughtUp && shows(verbosity2, "verbose") ? new SlackIO(client, ev).reply(catchUpDelayNote(ev.ts, systemClock())).catch((err2) => {
|
|
65492
66021
|
console.error(`[catch-up] ${ev.channel}:${ev.ts} delay note failed: ${err2.message}`);
|
|
65493
66022
|
}) : Promise.resolve();
|
|
65494
66023
|
const [imagePass, documentPass] = await Promise.all([
|
|
@@ -65511,19 +66040,6 @@ async function receiveSlackMessage(client, ev, span, policy, relayApps, intake)
|
|
|
65511
66040
|
});
|
|
65512
66041
|
const unsupported = imagePass.skippedFiles.filter((f) => documentPass.skippedFiles.includes(f));
|
|
65513
66042
|
const { staged, skipped } = stagedFiles(unsupported, { ...policy, messageId: ev.ts });
|
|
65514
|
-
const requester = await resolveSlackRequester(
|
|
65515
|
-
client,
|
|
65516
|
-
{
|
|
65517
|
-
channel: ev.channel,
|
|
65518
|
-
ts: ev.ts,
|
|
65519
|
-
threadTs: ev.threadTs,
|
|
65520
|
-
...ev.user !== void 0 ? { user: ev.user } : {},
|
|
65521
|
-
text: ev.rawText ?? ev.text,
|
|
65522
|
-
...ev.poster !== void 0 ? { poster: ev.poster } : {}
|
|
65523
|
-
},
|
|
65524
|
-
relayApps
|
|
65525
|
-
);
|
|
65526
|
-
span.setAttrs({ requester: requester.resolvedBy });
|
|
65527
66043
|
const [channelName, userName, team] = await Promise.all([
|
|
65528
66044
|
resolveChannelName(client, ev.channel),
|
|
65529
66045
|
requester.slackUserId !== void 0 ? resolveUserName(client, requester.slackUserId) : Promise.resolve(requester.userName),
|
|
@@ -65843,6 +66359,8 @@ var init_slack = __esm({
|
|
|
65843
66359
|
init_intake();
|
|
65844
66360
|
init_thread();
|
|
65845
66361
|
init_operator();
|
|
66362
|
+
init_directives();
|
|
66363
|
+
init_verbosity();
|
|
65846
66364
|
init_confirmations();
|
|
65847
66365
|
init_threadTurns();
|
|
65848
66366
|
init_statusBudget();
|
|
@@ -66028,14 +66546,15 @@ ${file.text}`));
|
|
|
66028
66546
|
io
|
|
66029
66547
|
};
|
|
66030
66548
|
}
|
|
66031
|
-
async status(initial) {
|
|
66549
|
+
async status(initial, display = {}) {
|
|
66032
66550
|
const statusClient = this.opts.statusClient ?? this.client;
|
|
66033
66551
|
const budget = this.opts.statusBudget ?? processStatusBudget;
|
|
66034
|
-
const
|
|
66552
|
+
const shimmerVisible = shows(display.verbosity ?? DEFAULT_VERBOSITY, "verbose");
|
|
66553
|
+
const setShimmer = (status3 = LOADING_PHRASES[0]) => statusClient.assistant.threads.setStatus({
|
|
66035
66554
|
channel_id: this.ev.channel,
|
|
66036
66555
|
thread_ts: this.ev.threadTs,
|
|
66037
|
-
status:
|
|
66038
|
-
loading_messages: LOADING_PHRASES
|
|
66556
|
+
status: status3,
|
|
66557
|
+
...status3 === "" ? {} : { loading_messages: LOADING_PHRASES }
|
|
66039
66558
|
}).catch((err2) => console.error(`[shimmer] ${err2.message}`));
|
|
66040
66559
|
let ts;
|
|
66041
66560
|
if (this.opts.existingCard) {
|
|
@@ -66060,11 +66579,17 @@ ${file.text}`));
|
|
|
66060
66579
|
liveCards.add(liveCardKey(this.ev.channel, ts));
|
|
66061
66580
|
const card = `${this.ev.channel}:${ts}`;
|
|
66062
66581
|
const shimmerKey = `${this.ev.channel}:${this.ev.threadTs}`;
|
|
66063
|
-
|
|
66064
|
-
|
|
66065
|
-
|
|
66066
|
-
|
|
66067
|
-
|
|
66582
|
+
let shimmerTimer;
|
|
66583
|
+
if (shimmerVisible) {
|
|
66584
|
+
shimmerOwners.set(shimmerKey, card);
|
|
66585
|
+
await setShimmer();
|
|
66586
|
+
shimmerTimer = setInterval(() => {
|
|
66587
|
+
if (shimmerOwners.get(shimmerKey) === card) void setShimmer();
|
|
66588
|
+
}, 75e3);
|
|
66589
|
+
} else {
|
|
66590
|
+
shimmerOwners.delete(shimmerKey);
|
|
66591
|
+
await setShimmer("");
|
|
66592
|
+
}
|
|
66068
66593
|
budget.open(card);
|
|
66069
66594
|
const edit = (frame2) => statusClient.chat.update({ channel: this.ev.channel, ts, ...render2(frame2) });
|
|
66070
66595
|
let dropped = 0;
|
|
@@ -66093,7 +66618,7 @@ ${file.text}`));
|
|
|
66093
66618
|
});
|
|
66094
66619
|
},
|
|
66095
66620
|
done: async (frame2) => {
|
|
66096
|
-
clearInterval(shimmerTimer);
|
|
66621
|
+
if (shimmerTimer !== void 0) clearInterval(shimmerTimer);
|
|
66097
66622
|
liveCards.delete(liveCardKey(this.ev.channel, ts));
|
|
66098
66623
|
budget.close(card);
|
|
66099
66624
|
const waitMs = budget.takeTerminal(this.ev.channel);
|
|
@@ -66105,7 +66630,7 @@ ${file.text}`));
|
|
|
66105
66630
|
if (dropped > 0) console.log(`[slack] card ${card}: ${dropped} progress frames dropped by the status budget`);
|
|
66106
66631
|
if (shimmerOwners.get(shimmerKey) !== card) return;
|
|
66107
66632
|
shimmerOwners.delete(shimmerKey);
|
|
66108
|
-
await
|
|
66633
|
+
await setShimmer("").catch(() => {
|
|
66109
66634
|
});
|
|
66110
66635
|
}
|
|
66111
66636
|
};
|
|
@@ -66571,11 +67096,19 @@ ${JSON.stringify(result.value)}` }] });
|
|
|
66571
67096
|
if (typeof args.text !== "string" || args.text.trim() === "") {
|
|
66572
67097
|
return err(id, INVALID_PARAMS, "`text` is required and must be a non-empty string");
|
|
66573
67098
|
}
|
|
66574
|
-
if (args.channel !== void 0
|
|
66575
|
-
return err(id, INVALID_PARAMS, "`channel` must be a string");
|
|
67099
|
+
if (args.channel !== void 0) {
|
|
67100
|
+
if (typeof args.channel !== "string") return err(id, INVALID_PARAMS, "`channel` must be a string");
|
|
67101
|
+
const error = ingressComponentError("channel", args.channel);
|
|
67102
|
+
if (error) return err(id, INVALID_PARAMS, error);
|
|
67103
|
+
}
|
|
67104
|
+
if (args.thread !== void 0) {
|
|
67105
|
+
if (typeof args.thread !== "string") return err(id, INVALID_PARAMS, "`thread` must be a string");
|
|
67106
|
+
const error = ingressComponentError("thread", args.thread);
|
|
67107
|
+
if (error) return err(id, INVALID_PARAMS, error);
|
|
66576
67108
|
}
|
|
66577
|
-
if (
|
|
66578
|
-
|
|
67109
|
+
if (identity2.channel !== void 0) {
|
|
67110
|
+
const error = ingressComponentError("channel", identity2.channel);
|
|
67111
|
+
if (error) return err(id, INVALID_PARAMS, error);
|
|
66579
67112
|
}
|
|
66580
67113
|
const receivedAt = systemClock();
|
|
66581
67114
|
const trace = startRequestRoot(deps, { channel: "mcp", receivedAt });
|
|
@@ -68211,16 +68744,16 @@ function residentStateTone(state) {
|
|
|
68211
68744
|
}
|
|
68212
68745
|
function residentsFleetTone(records) {
|
|
68213
68746
|
if (records.length === 0) return "grey";
|
|
68214
|
-
const tones = new Set(records.map((
|
|
68747
|
+
const tones = new Set(records.map((record3) => residentStateTone(residentLive(rec(record3)).state)));
|
|
68215
68748
|
if (tones.has("red")) return "red";
|
|
68216
68749
|
if (tones.has("amber")) return "amber";
|
|
68217
68750
|
return tones.size === 1 && tones.has("green") ? "green" : "grey";
|
|
68218
68751
|
}
|
|
68219
|
-
function residentSlug(
|
|
68220
|
-
return str8(
|
|
68752
|
+
function residentSlug(record3) {
|
|
68753
|
+
return str8(record3.resource).replace(/^repo:/, "");
|
|
68221
68754
|
}
|
|
68222
|
-
function residentLive(
|
|
68223
|
-
const live = rec(
|
|
68755
|
+
function residentLive(record3) {
|
|
68756
|
+
const live = rec(record3.live);
|
|
68224
68757
|
if (typeof live.error === "string") return { ...live, state: "unreachable", reason: live.error };
|
|
68225
68758
|
return { ...live, state: str8(live.state) || "unknown", reason: str8(live.reason) };
|
|
68226
68759
|
}
|
|
@@ -68520,12 +69053,12 @@ function createResidentsViewHandler(deps) {
|
|
|
68520
69053
|
finish2(200);
|
|
68521
69054
|
return;
|
|
68522
69055
|
}
|
|
68523
|
-
const
|
|
68524
|
-
if (!
|
|
69056
|
+
const record3 = listing.residents.find((x) => residentSlug(x) === route2.slug);
|
|
69057
|
+
if (!record3) {
|
|
68525
69058
|
plainEnd(404, `${route2.slug} is not onboarded as a resident`);
|
|
68526
69059
|
return;
|
|
68527
69060
|
}
|
|
68528
|
-
page2(req, res, 200, ctx.actor, route2.slug, { page: "resident", slug: route2.slug, record:
|
|
69061
|
+
page2(req, res, 200, ctx.actor, route2.slug, { page: "resident", slug: route2.slug, record: record3 });
|
|
68529
69062
|
finish2(200);
|
|
68530
69063
|
});
|
|
68531
69064
|
return true;
|
|
@@ -70667,9 +71200,9 @@ function startReclaimSweep(opts) {
|
|
|
70667
71200
|
return { stop: () => stop(timer) };
|
|
70668
71201
|
}
|
|
70669
71202
|
async function closeReclaimed(ledger, gen, input) {
|
|
70670
|
-
const
|
|
70671
|
-
const result = await ledger.finish(input.row.runId, gen,
|
|
70672
|
-
return result.ok ? { ok: true, record:
|
|
71203
|
+
const record3 = reclaimedRunRecord(input);
|
|
71204
|
+
const result = await ledger.finish(input.row.runId, gen, record3);
|
|
71205
|
+
return result.ok ? { ok: true, record: record3 } : { ok: false, reason: result.reason ?? "refused" };
|
|
70673
71206
|
}
|
|
70674
71207
|
async function completenessVerdict(ledger, row2, lastStep) {
|
|
70675
71208
|
if (!lastStep) return { resumable: false, why: "no step record: killed before its conversation was stored" };
|
|
@@ -70870,7 +71403,12 @@ async function launchResumes(deps, resumable, opts) {
|
|
|
70870
71403
|
startedAt: row2.startedAt,
|
|
70871
71404
|
now: deps.clock ?? Date.now
|
|
70872
71405
|
});
|
|
70873
|
-
|
|
71406
|
+
const verbosity2 = row2.meta.verbosity ?? deps.config.verbosityFor(
|
|
71407
|
+
row2.meta.channelId,
|
|
71408
|
+
row2.meta.userId,
|
|
71409
|
+
parseDirectives(inputTextOf(run2.events)).verbosity
|
|
71410
|
+
);
|
|
71411
|
+
await io2.status(shell.live({ detail: [rehostCardDetail(run2)] }), { verbosity: verbosity2 });
|
|
70874
71412
|
} catch (err2) {
|
|
70875
71413
|
warn(
|
|
70876
71414
|
`[resume] ${row2.runId} ${row2.threadKey}: restart card could not be written: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
@@ -70949,6 +71487,7 @@ var PI_READ_ONLY_TOOLS;
|
|
|
70949
71487
|
var init_resumeLaunch = __esm({
|
|
70950
71488
|
"../../src/core/resumeLaunch.ts"() {
|
|
70951
71489
|
"use strict";
|
|
71490
|
+
init_directives();
|
|
70952
71491
|
init_inboxMessage();
|
|
70953
71492
|
init_resume();
|
|
70954
71493
|
init_runRegistry();
|
|
@@ -71925,7 +72464,7 @@ function watched(io, on) {
|
|
|
71925
72464
|
on.replied(text);
|
|
71926
72465
|
await io.reply(text);
|
|
71927
72466
|
},
|
|
71928
|
-
status: (initial) => io.status(initial),
|
|
72467
|
+
status: (initial, display) => io.status(initial, display),
|
|
71929
72468
|
history: () => io.history(),
|
|
71930
72469
|
runStarted: (started) => {
|
|
71931
72470
|
io.runStarted?.(started);
|
|
@@ -71999,6 +72538,33 @@ The thread since the last step:
|
|
|
71999
72538
|
${foldThreadEvents(folded)}` };
|
|
72000
72539
|
}
|
|
72001
72540
|
const carried = foldThreadAttachments(folded);
|
|
72541
|
+
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);
|
|
72542
|
+
if (existingPrCodingTarget) {
|
|
72543
|
+
if (row2.publication === void 0)
|
|
72544
|
+
return json3(409, {
|
|
72545
|
+
ok: false,
|
|
72546
|
+
error: "publication_binding_missing",
|
|
72547
|
+
message: "the existing pull request has no durable publication binding",
|
|
72548
|
+
at
|
|
72549
|
+
});
|
|
72550
|
+
try {
|
|
72551
|
+
const owner = deps.runnerOwnership?.owner(instance.repo, row2.publication.pr);
|
|
72552
|
+
if (owner === void 0 || owner.instanceId !== row2.publication.owner.instanceId || owner.unit !== row2.publication.owner.unit)
|
|
72553
|
+
return json3(409, {
|
|
72554
|
+
ok: false,
|
|
72555
|
+
error: "publication_ownership_changed",
|
|
72556
|
+
message: "the durable existing-PR publication owner is no longer the runner's sole owner",
|
|
72557
|
+
at
|
|
72558
|
+
});
|
|
72559
|
+
} catch (err2) {
|
|
72560
|
+
return json3(409, {
|
|
72561
|
+
ok: false,
|
|
72562
|
+
error: "publication_ownership_unknown",
|
|
72563
|
+
message: describe12(err2),
|
|
72564
|
+
at
|
|
72565
|
+
});
|
|
72566
|
+
}
|
|
72567
|
+
}
|
|
72002
72568
|
const msg = {
|
|
72003
72569
|
channelId: instance.channelId,
|
|
72004
72570
|
userId: instance.userId,
|
|
@@ -72040,10 +72606,16 @@ ${foldThreadEvents(folded)}` };
|
|
|
72040
72606
|
lastReply = text;
|
|
72041
72607
|
}
|
|
72042
72608
|
});
|
|
72609
|
+
const roundExpectedHead = req.brief?.kind === "findings" || req.brief?.kind === "rebase" ? req.brief.headSha : req.brief?.kind === "contract" ? req.brief.continue?.from : void 0;
|
|
72610
|
+
const publication = req.preset === "coding" && row2?.publication !== void 0 ? {
|
|
72611
|
+
...row2.publication,
|
|
72612
|
+
...roundExpectedHead !== void 0 ? { expectedHeadSha: roundExpectedHead } : {}
|
|
72613
|
+
} : void 0;
|
|
72043
72614
|
const tag = {
|
|
72044
72615
|
parentInstanceId: instance.id,
|
|
72045
72616
|
idempotencyKey: key,
|
|
72046
|
-
...instance.base !== void 0 ? { base: instance.base } : {}
|
|
72617
|
+
...instance.base !== void 0 ? { base: instance.base } : {},
|
|
72618
|
+
...publication !== void 0 ? { publication } : {}
|
|
72047
72619
|
};
|
|
72048
72620
|
let dispatching;
|
|
72049
72621
|
try {
|
|
@@ -72219,11 +72791,11 @@ async function reviewPostedAtPatiently(deps, pr, verdict, head) {
|
|
|
72219
72791
|
}
|
|
72220
72792
|
return answer;
|
|
72221
72793
|
}
|
|
72222
|
-
function reviewPostedByRecord(
|
|
72223
|
-
const post =
|
|
72224
|
-
if (post === void 0 ||
|
|
72794
|
+
function reviewPostedByRecord(record3, pr) {
|
|
72795
|
+
const post = record3.reviewPost;
|
|
72796
|
+
if (post === void 0 || record3.reviewHead === void 0 || record3.verdict === void 0) return void 0;
|
|
72225
72797
|
if (!post.posted) return { reviewPosted: false, reviewPostReason: post.reason };
|
|
72226
|
-
const same = post.target.repo === pr.repo && post.target.number === pr.number && sameCommit(post.head.toLowerCase(),
|
|
72798
|
+
const same = post.target.repo === pr.repo && post.target.number === pr.number && sameCommit(post.head.toLowerCase(), record3.reviewHead) && post.verdict === record3.verdict.verdict;
|
|
72227
72799
|
return same ? { reviewPosted: true } : void 0;
|
|
72228
72800
|
}
|
|
72229
72801
|
function headBranchStateUnknown(facts2) {
|
|
@@ -72240,6 +72812,7 @@ function pullRequestState(number, fallbackUrl, facts2) {
|
|
|
72240
72812
|
state: "merged",
|
|
72241
72813
|
prNumber: number,
|
|
72242
72814
|
url,
|
|
72815
|
+
...facts2.headSha !== void 0 ? { headSha: facts2.headSha } : {},
|
|
72243
72816
|
sha: facts2.mergeCommitSha,
|
|
72244
72817
|
mergedAt: facts2.mergedAt,
|
|
72245
72818
|
...facts2.mergedBy !== void 0 ? { mergedBy: facts2.mergedBy } : {}
|
|
@@ -72310,15 +72883,16 @@ async function readRecord(body, deps) {
|
|
|
72310
72883
|
at
|
|
72311
72884
|
});
|
|
72312
72885
|
const full = await deps.runs.getRun(body.runId, { include: "messages" });
|
|
72313
|
-
const
|
|
72314
|
-
const finalReply = finalReplyOf2(
|
|
72315
|
-
const reviewAskedAt = reviewAskedAtOf(
|
|
72316
|
-
const
|
|
72886
|
+
const record3 = full.ok ? full.value : view;
|
|
72887
|
+
const finalReply = finalReplyOf2(record3.events);
|
|
72888
|
+
const reviewAskedAt = reviewAskedAtOf(record3.events);
|
|
72889
|
+
const description = record3.events?.some((event) => event.type === "pr_description") === true;
|
|
72890
|
+
const opened = prOpenedOf(record3.events);
|
|
72317
72891
|
const pr = opened !== void 0 ? { number: opened.number, url: opened.url, created: opened.created } : void 0;
|
|
72318
72892
|
let coordinatorUnit;
|
|
72319
72893
|
if (instanceRow !== null && typeof body.unit === "string") {
|
|
72320
72894
|
coordinatorUnit = (await deps.instances.listUnits(instanceRow.id)).find((u) => u.unit === body.unit);
|
|
72321
|
-
const pushedPrRef = opened?.head !== void 0 &&
|
|
72895
|
+
const pushedPrRef = opened?.head !== void 0 && record3.pushed?.some((pushed) => pushed.ref === opened.head) ? opened.head : void 0;
|
|
72322
72896
|
if (coordinatorUnit !== void 0 && pushedPrRef !== void 0 && coordinatorUnit.branch !== pushedPrRef) {
|
|
72323
72897
|
coordinatorUnit = { ...coordinatorUnit, branch: pushedPrRef };
|
|
72324
72898
|
await deps.instances.putUnits([coordinatorUnit]);
|
|
@@ -72345,13 +72919,13 @@ async function readRecord(body, deps) {
|
|
|
72345
72919
|
});
|
|
72346
72920
|
}
|
|
72347
72921
|
let posted;
|
|
72348
|
-
if (
|
|
72922
|
+
if (record3.verdict !== void 0 && record3.reviewHead !== void 0 && typeof body.unit === "string") {
|
|
72349
72923
|
const instance = instanceRow;
|
|
72350
72924
|
if (instance && coordinatorUnit?.pr !== void 0) {
|
|
72351
72925
|
const unitPr = { repo: instance.repo, number: coordinatorUnit.pr.number };
|
|
72352
|
-
posted = reviewPostedByRecord(
|
|
72926
|
+
posted = reviewPostedByRecord(record3, unitPr);
|
|
72353
72927
|
if (posted === void 0) {
|
|
72354
|
-
const seen = await reviewPostedAtPatiently(deps, unitPr,
|
|
72928
|
+
const seen = await reviewPostedAtPatiently(deps, unitPr, record3.verdict.verdict, record3.reviewHead);
|
|
72355
72929
|
if (seen !== void 0) posted = { reviewPosted: seen };
|
|
72356
72930
|
}
|
|
72357
72931
|
}
|
|
@@ -72363,27 +72937,31 @@ async function readRecord(body, deps) {
|
|
|
72363
72937
|
run: {
|
|
72364
72938
|
...coordinatorRunView(view, id.value, finalReply),
|
|
72365
72939
|
...pr !== void 0 ? { pr } : {},
|
|
72366
|
-
...
|
|
72367
|
-
...
|
|
72940
|
+
...record3.verdict !== void 0 ? { verdict: record3.verdict } : {},
|
|
72941
|
+
...record3.reviewHead !== void 0 ? { reviewHead: record3.reviewHead } : {},
|
|
72368
72942
|
...reviewAskedAt !== void 0 ? { reviewAskedAt } : {},
|
|
72369
72943
|
...posted !== void 0 ? { reviewPosted: posted.reviewPosted } : {},
|
|
72370
72944
|
...posted?.reviewPostReason !== void 0 ? { reviewPostReason: posted.reviewPostReason } : {},
|
|
72371
|
-
...
|
|
72372
|
-
|
|
72945
|
+
...record3.dispositions !== void 0 ? { dispositions: record3.dispositions } : {},
|
|
72946
|
+
// Findings readiness requires the typed description output. Its event is
|
|
72947
|
+
// durable on the child record, so the runner can verify it without
|
|
72948
|
+
// trusting final-reply prose.
|
|
72949
|
+
...description ? { description: true } : {},
|
|
72950
|
+
...record3.handoff !== void 0 ? { handoff: true } : {},
|
|
72373
72951
|
// The renewal's facts (decision 0046): progress is read off these.
|
|
72374
|
-
...
|
|
72375
|
-
...
|
|
72952
|
+
...record3.pushed !== void 0 ? { pushed: record3.pushed } : {},
|
|
72953
|
+
...record3.lease !== void 0 ? { leaseStartedAt: record3.lease.startedAt } : {},
|
|
72376
72954
|
// What the child cost, as the runs service prices it (costs.md item 4c):
|
|
72377
72955
|
// null when unknown — no usage on the record, or a model without a price
|
|
72378
72956
|
// — so a capped grant never renews on an understated total.
|
|
72379
|
-
costUsd:
|
|
72380
|
-
...
|
|
72957
|
+
costUsd: record3.cost?.usd ?? null,
|
|
72958
|
+
...record3.handoff !== void 0 ? { handoffLists: record3.handoff } : {},
|
|
72381
72959
|
// The failure by name (run-history item 57): a `provider_transient` lets
|
|
72382
72960
|
// the machine re-run a round-0 child that pushed nothing (issue 1932).
|
|
72383
|
-
...
|
|
72961
|
+
...record3.failure !== void 0 ? { failure: record3.failure } : {},
|
|
72384
72962
|
// What ended an interrupted child (issue 1876), off its record's own
|
|
72385
72963
|
// events: the unit's ending names the actual cause in the user's nouns.
|
|
72386
|
-
...view.status === "interrupted" ? interruptionOf(
|
|
72964
|
+
...view.status === "interrupted" ? interruptionOf(record3.events) : {}
|
|
72387
72965
|
},
|
|
72388
72966
|
at
|
|
72389
72967
|
});
|
|
@@ -72462,6 +73040,13 @@ async function recoverPushedBranch(deps, instance, row2, branch2, runId3) {
|
|
|
72462
73040
|
if (subject2 !== void 0 && checkPrTitle(subject2, prTitleVocabulary_default).ok) title = subject2;
|
|
72463
73041
|
}
|
|
72464
73042
|
title ??= recoveredFallbackTitle(row2, branch2, instance.plan?.id ?? parsePlanBranch(branch2)?.planId);
|
|
73043
|
+
const header4 = requestedByLine({
|
|
73044
|
+
name: instance.userName?.trim() || instance.userId,
|
|
73045
|
+
threadUrl: threadPageLink(instance.threadKey, deps.runPageBase?.replace(/\/runs\/?$/, "") ?? "")
|
|
73046
|
+
});
|
|
73047
|
+
prBody = `${header4}
|
|
73048
|
+
|
|
73049
|
+
${prBody}`;
|
|
72465
73050
|
if (deps.rewriteIdentities !== void 0) {
|
|
72466
73051
|
const rewritten = await deps.rewriteIdentities({
|
|
72467
73052
|
repo: instance.repo,
|
|
@@ -72521,9 +73106,19 @@ async function prCheck(body, deps) {
|
|
|
72521
73106
|
const unit = await unitRowOf(deps, instance, body.unit);
|
|
72522
73107
|
if (!unit.ok) return unit.response;
|
|
72523
73108
|
const branch2 = unit.row?.branch ?? instance.branch;
|
|
72524
|
-
const remember = async (pr) => {
|
|
72525
|
-
if (unit.row
|
|
72526
|
-
|
|
73109
|
+
const remember = async (pr, facts2) => {
|
|
73110
|
+
if (!unit.row) return;
|
|
73111
|
+
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) ? {
|
|
73112
|
+
repo: instance.repo,
|
|
73113
|
+
pr: pr.number,
|
|
73114
|
+
headRef: facts2.headRef,
|
|
73115
|
+
baseRef: facts2.baseRef,
|
|
73116
|
+
expectedHeadSha: facts2.headSha,
|
|
73117
|
+
publicationRef: facts2.headRef,
|
|
73118
|
+
owner: { instanceId: instance.id, unit: unit.row.unit }
|
|
73119
|
+
} : void 0);
|
|
73120
|
+
if (unit.row.pr?.number !== pr.number || unit.row.pr.url !== pr.url || unit.row.publication === void 0 && publication !== void 0)
|
|
73121
|
+
await deps.instances.putUnits([{ ...unit.row, pr, ...publication !== void 0 ? { publication } : {} }]);
|
|
72527
73122
|
};
|
|
72528
73123
|
try {
|
|
72529
73124
|
if (follow !== void 0) {
|
|
@@ -72532,8 +73127,12 @@ async function prCheck(body, deps) {
|
|
|
72532
73127
|
const fallbackUrl = unit.row?.pr?.url ?? `https://github.com/${instance.repo}/pull/${follow}`;
|
|
72533
73128
|
const state2 = pullRequestState(follow, fallbackUrl, facts2);
|
|
72534
73129
|
if (state2 === void 0) throw new Error(headBranchStateError(instance.repo, follow));
|
|
72535
|
-
await remember({ number: follow, url: String(state2.url) });
|
|
73130
|
+
await remember({ number: follow, url: String(state2.url) }, facts2);
|
|
72536
73131
|
if (state2.state !== "open") return json3(200, { ok: true, ...state2, at });
|
|
73132
|
+
deps.runnerOwnership?.claim(instance.repo, follow, {
|
|
73133
|
+
instanceId: instance.id,
|
|
73134
|
+
unit: unit.row?.unit ?? body.unit
|
|
73135
|
+
});
|
|
72537
73136
|
const prRef = { repo: instance.repo, number: follow };
|
|
72538
73137
|
const headSha = facts2.headSha;
|
|
72539
73138
|
const approved = entry && headSha !== void 0 ? await reviewPostedAt(deps, prRef, "approve", headSha) : void 0;
|
|
@@ -72564,13 +73163,13 @@ async function prCheck(body, deps) {
|
|
|
72564
73163
|
}
|
|
72565
73164
|
const open2 = await deps.findOpenPrByHead(instance.repo, branch2);
|
|
72566
73165
|
if (open2) {
|
|
72567
|
-
await remember({ number: open2.number, url: open2.htmlUrl });
|
|
72568
73166
|
deps.runnerOwnership?.claim(instance.repo, open2.number, {
|
|
72569
73167
|
instanceId: instance.id,
|
|
72570
73168
|
unit: unit.row?.unit ?? body.unit
|
|
72571
73169
|
});
|
|
72572
73170
|
const liveFacts = await deps.fetchPrFacts({ repo: instance.repo, number: open2.number });
|
|
72573
73171
|
if (liveFacts === void 0) throw new Error(`could not read ${instance.repo}#${open2.number}`);
|
|
73172
|
+
await remember({ number: open2.number, url: open2.htmlUrl }, liveFacts);
|
|
72574
73173
|
const current = pullRequestState(open2.number, open2.htmlUrl, liveFacts);
|
|
72575
73174
|
if (current === void 0) throw new Error(headBranchStateError(instance.repo, open2.number));
|
|
72576
73175
|
if (current.state !== "open") return json3(200, { ok: true, ...current, at });
|
|
@@ -72879,12 +73478,13 @@ async function drawCard(deps, instance, units, close) {
|
|
|
72879
73478
|
source: instance.addressSeveritySource ?? "org"
|
|
72880
73479
|
} : void 0
|
|
72881
73480
|
);
|
|
73481
|
+
const display = { verbosity: instance.verbosity ?? DEFAULT_VERBOSITY };
|
|
72882
73482
|
if (!close) {
|
|
72883
|
-
await io.status(shell.live({ detail }));
|
|
73483
|
+
await io.status(shell.live({ detail }), display);
|
|
72884
73484
|
return;
|
|
72885
73485
|
}
|
|
72886
73486
|
shell.freeze(clock4());
|
|
72887
|
-
const handle2 = await io.status(shell.live({ detail }));
|
|
73487
|
+
const handle2 = await io.status(shell.live({ detail }), display);
|
|
72888
73488
|
await handle2.done(shell.close({ kind: "done", icon: close.icon, detail: detail.join("\n") }));
|
|
72889
73489
|
}
|
|
72890
73490
|
function parseGate(raw) {
|
|
@@ -73711,7 +74311,7 @@ ${planSummary(units)}`).catch(() => {
|
|
|
73711
74311
|
const summary = deps.registry.getById(host.runId);
|
|
73712
74312
|
const visibility = await deps.channelVisibilityOf(instance.channelId);
|
|
73713
74313
|
const finishedAt = snap?.finishedAt ?? at;
|
|
73714
|
-
const
|
|
74314
|
+
const record3 = assembleRunRecord({
|
|
73715
74315
|
run: { id: host.runId, ...summary?.label !== void 0 ? { label: summary.label } : {} },
|
|
73716
74316
|
snap,
|
|
73717
74317
|
agent: "ship",
|
|
@@ -73741,7 +74341,7 @@ ${planSummary(units)}`).catch(() => {
|
|
|
73741
74341
|
seal
|
|
73742
74342
|
});
|
|
73743
74343
|
const handle2 = deps.ledgerRuns().find((run2) => run2.runId === host.runId);
|
|
73744
|
-
deps.runHistoryWriter.write(
|
|
74344
|
+
deps.runHistoryWriter.write(record3, handle2 !== void 0 ? { via: handle2.sink } : void 0);
|
|
73745
74345
|
}
|
|
73746
74346
|
(deps.log ?? console.log)(`[coordinator] ${instance.id}: finished ${body.outcome} \u2014 record ${runId3}`);
|
|
73747
74347
|
return json3(200, { ok: true, runId: runId3, at });
|
|
@@ -73867,6 +74467,8 @@ var init_adminCoordinator = __esm({
|
|
|
73867
74467
|
"use strict";
|
|
73868
74468
|
init_budgets();
|
|
73869
74469
|
init_verbosity();
|
|
74470
|
+
init_prDescription();
|
|
74471
|
+
init_reply();
|
|
73870
74472
|
init_registry();
|
|
73871
74473
|
init_authorize();
|
|
73872
74474
|
init_actor();
|