@coreplane/switchboard 1.261.0 → 1.261.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/deploy/cloudflare-memory/worker.ts +1 -1
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/coordinator/driver.ts +17 -5
- package/dist/assets/src/core/reviewVerdict.ts +33 -6
- package/dist/assets/src/core/ship/coordinator.ts +330 -72
- package/dist/assets/src/core/ship/renewal.ts +1 -1
- package/dist/cli.js +842 -510
- package/package.json +1 -1
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() ?? "";
|
|
@@ -6553,15 +6579,15 @@ function capEvent(event, maxBytes) {
|
|
|
6553
6579
|
}
|
|
6554
6580
|
return { event: capped4, bytes: size };
|
|
6555
6581
|
}
|
|
6556
|
-
function fitRecordToBudget(
|
|
6557
|
-
const cappedAll =
|
|
6558
|
-
const measure = (evs) => utf8ByteLength(JSON.stringify({ ...
|
|
6559
|
-
const whole = { ...
|
|
6582
|
+
function fitRecordToBudget(record3, maxBytes = MAX_RECORD_BYTES) {
|
|
6583
|
+
const cappedAll = record3.events.map((e) => capEvent(e, MAX_EVENT_BYTES));
|
|
6584
|
+
const measure = (evs) => utf8ByteLength(JSON.stringify({ ...record3, events: evs, storedEventCount: evs.length, truncated: true }));
|
|
6585
|
+
const whole = { ...record3, events: cappedAll.map((c) => c.event), storedEventCount: cappedAll.length };
|
|
6560
6586
|
if (utf8ByteLength(JSON.stringify(whole)) <= maxBytes) return whole;
|
|
6561
6587
|
const baseline = measure([]);
|
|
6562
6588
|
const capped4 = dropSpanPairsFromTheMiddle(cappedAll, maxBytes - baseline);
|
|
6563
6589
|
const events = capped4.map((c) => c.event);
|
|
6564
|
-
if (measure(events) <= maxBytes) return { ...
|
|
6590
|
+
if (measure(events) <= maxBytes) return { ...record3, events, storedEventCount: events.length, truncated: true };
|
|
6565
6591
|
const sizes = capped4.map((c) => c.bytes + 1);
|
|
6566
6592
|
let remaining = maxBytes - measure([]);
|
|
6567
6593
|
let head = 0;
|
|
@@ -6578,7 +6604,7 @@ function fitRecordToBudget(record2, maxBytes = MAX_RECORD_BYTES) {
|
|
|
6578
6604
|
while (kept.length > 0 && measure(kept) > maxBytes) {
|
|
6579
6605
|
kept.splice(Math.floor(kept.length / 2), 1);
|
|
6580
6606
|
}
|
|
6581
|
-
return { ...
|
|
6607
|
+
return { ...record3, events: kept, storedEventCount: kept.length, truncated: true };
|
|
6582
6608
|
}
|
|
6583
6609
|
function headLength(events) {
|
|
6584
6610
|
let n2 = 0;
|
|
@@ -6696,9 +6722,9 @@ function dominantFriction(byCategory) {
|
|
|
6696
6722
|
}
|
|
6697
6723
|
return best;
|
|
6698
6724
|
}
|
|
6699
|
-
function pointOf(
|
|
6700
|
-
if (
|
|
6701
|
-
const usage =
|
|
6725
|
+
function pointOf(record3, prices) {
|
|
6726
|
+
if (record3.provisional === true) return void 0;
|
|
6727
|
+
const usage = record3.usage ?? emptyUsage();
|
|
6702
6728
|
const priced = llmUsdOfUsage(usage, prices);
|
|
6703
6729
|
let inputTokens = 0;
|
|
6704
6730
|
let outputTokens = 0;
|
|
@@ -6710,27 +6736,27 @@ function pointOf(record2, prices) {
|
|
|
6710
6736
|
cacheReadTokens += m.cacheReadTokens;
|
|
6711
6737
|
cacheWriteTokens += m.cacheWriteTokens;
|
|
6712
6738
|
}
|
|
6713
|
-
const shape =
|
|
6739
|
+
const shape = record3.diagnosis.shape;
|
|
6714
6740
|
const blobs = {
|
|
6715
6741
|
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":
|
|
6742
|
+
agent: record3.agent ?? "",
|
|
6743
|
+
preset: record3.profile?.preset ?? "",
|
|
6744
|
+
model: record3.model ?? "",
|
|
6745
|
+
status: record3.status,
|
|
6746
|
+
"failure kind": record3.failure?.kind ?? "",
|
|
6747
|
+
"dominant friction": dominantFriction(record3.diagnosis.byCategory),
|
|
6748
|
+
channel: record3.channelId,
|
|
6749
|
+
repository: record3.repo ?? "",
|
|
6750
|
+
"machine class": record3.profile?.machine ?? "",
|
|
6751
|
+
"route class": record3.route !== void 0 ? "routed" : "chosen",
|
|
6752
|
+
reply: record3.replyOk === true ? "ok" : record3.replyOk === false ? "failed" : "none",
|
|
6753
|
+
lineage: record3.parentRunId !== void 0 ? "child" : "root",
|
|
6754
|
+
requester: record3.userId,
|
|
6755
|
+
identity: record3.profile?.identity ?? "",
|
|
6756
|
+
"run id": record3.id
|
|
6731
6757
|
};
|
|
6732
6758
|
const doubles = {
|
|
6733
|
-
wall:
|
|
6759
|
+
wall: record3.finishedAt - (record3.receivedAt ?? record3.startedAt),
|
|
6734
6760
|
"getting ready": shape?.gettingReadyMs ?? 0,
|
|
6735
6761
|
thinking: shape?.thinkingMs ?? 0,
|
|
6736
6762
|
tools: shape?.toolsMs ?? 0,
|
|
@@ -6744,15 +6770,15 @@ function pointOf(record2, prices) {
|
|
|
6744
6770
|
"cache read tokens": cacheReadTokens,
|
|
6745
6771
|
"cache write tokens": cacheWriteTokens,
|
|
6746
6772
|
dollars: priced.usd,
|
|
6747
|
-
steps:
|
|
6748
|
-
"tool calls": isFinite_2(
|
|
6749
|
-
events:
|
|
6773
|
+
steps: record3.stepCount ?? 0,
|
|
6774
|
+
"tool calls": isFinite_2(record3.diagnosis.toolCalls) ? record3.diagnosis.toolCalls : 0,
|
|
6775
|
+
events: record3.eventCount,
|
|
6750
6776
|
"unpriced tokens": priced.unpricedTokens,
|
|
6751
|
-
minutes:
|
|
6752
|
-
"finished at":
|
|
6777
|
+
minutes: record3.profile?.minutes ?? 0,
|
|
6778
|
+
"finished at": record3.finishedAt
|
|
6753
6779
|
};
|
|
6754
6780
|
return {
|
|
6755
|
-
indexes: [capBlob(
|
|
6781
|
+
indexes: [capBlob(record3.agent ?? UNKNOWN_AGENT)],
|
|
6756
6782
|
blobs: POINT_COLUMNS.blobs.map((n2) => capBlob(blobs[n2])),
|
|
6757
6783
|
doubles: POINT_COLUMNS.doubles.map((n2) => doubles[n2])
|
|
6758
6784
|
};
|
|
@@ -10390,12 +10416,8 @@ function containerApplicationName(script, className) {
|
|
|
10390
10416
|
return `${script}-${className.toLowerCase()}`;
|
|
10391
10417
|
}
|
|
10392
10418
|
function bindLiveGate(gate, script, healthUrl) {
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
healthUrl,
|
|
10396
|
-
bearerEnv: gate.bearerEnv,
|
|
10397
|
-
containerApp: containerApplicationName(script, gate.containerClass)
|
|
10398
|
-
};
|
|
10419
|
+
const containerApp = containerApplicationName(script, gate.containerClass);
|
|
10420
|
+
return gate.kind === "bot" ? { kind: "bot", healthUrl, containerApp } : { kind: "sandbox", healthUrl, bearerEnv: gate.bearerEnv, containerApp };
|
|
10399
10421
|
}
|
|
10400
10422
|
function workersFor(profile) {
|
|
10401
10423
|
return WORKER_SPECS.flatMap(({ preflight, liveGate, ...spec }) => {
|
|
@@ -10410,8 +10432,8 @@ function workersFor(profile) {
|
|
|
10410
10432
|
script,
|
|
10411
10433
|
baseUrl,
|
|
10412
10434
|
healthUrl,
|
|
10413
|
-
//
|
|
10414
|
-
...preflight ? { preflight: liveGate?.kind === "
|
|
10435
|
+
// The bot preflight reads the same URL as its application + health live gate.
|
|
10436
|
+
...preflight ? { preflight: liveGate?.kind === "bot" ? { ...preflight, healthUrl } : preflight } : {},
|
|
10415
10437
|
...liveGate ? { liveGate: bindLiveGate(liveGate, script, healthUrl) } : {}
|
|
10416
10438
|
}
|
|
10417
10439
|
];
|
|
@@ -10522,7 +10544,7 @@ function formatPlan(plan3) {
|
|
|
10522
10544
|
plan3.steps.forEach((s, i) => {
|
|
10523
10545
|
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
10546
|
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 === "
|
|
10547
|
+
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
10548
|
const cap = s.capabilities.length > 0 ? ` \u2014 credential must pass ${s.capabilities.map((c) => `\`${c.command.join(" ")}\` (${c.needs})`).join(" and ")}` : "";
|
|
10527
10549
|
lines.push(
|
|
10528
10550
|
` ${i + 1}. ${s.name} (${s.script}) \u2014 ${s.dir}: ${s.command.join(" ")}${pf}${env}${cap}${live}
|
|
@@ -10593,7 +10615,7 @@ function classifyDeployOutput(exitCode, output) {
|
|
|
10593
10615
|
const firstError = lines.find((l) => isErrorish(l) && !/^npm ERR!/i.test(l)) ?? lines.find(isErrorish);
|
|
10594
10616
|
return { kind: "failed", reason: firstError ?? `exit ${exitCode}` };
|
|
10595
10617
|
}
|
|
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;
|
|
10618
|
+
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
10619
|
var init_plan = __esm({
|
|
10598
10620
|
"../../src/deploy/plan.ts"() {
|
|
10599
10621
|
"use strict";
|
|
@@ -10605,6 +10627,7 @@ var init_plan = __esm({
|
|
|
10605
10627
|
init_profile2();
|
|
10606
10628
|
UNSET_ENV = ["CLOUDFLARE_ACCOUNT_ID"];
|
|
10607
10629
|
DEPLOY_FORCE_ENV = "SWITCHBOARD_DEPLOY_FORCE";
|
|
10630
|
+
BOT_CONTAINER_CLASS = "SwitchboardServer";
|
|
10608
10631
|
SANDBOX_CONTAINER_CLASS = "SwitchboardSandbox";
|
|
10609
10632
|
SANDBOX_BEARER_ENV = "SANDBOX_TOKEN";
|
|
10610
10633
|
R2_CAPABILITY = {
|
|
@@ -10665,10 +10688,10 @@ var init_plan = __esm({
|
|
|
10665
10688
|
]
|
|
10666
10689
|
},
|
|
10667
10690
|
preflight: { forceEnv: DEPLOY_FORCE_ENV, baseUrlEnv: "SWITCHBOARD_BASE_URL" },
|
|
10668
|
-
liveGate: { kind: "
|
|
10691
|
+
liveGate: { kind: "bot", containerClass: BOT_CONTAINER_CLASS },
|
|
10669
10692
|
// The preflight reads the container application and the deploy pushes the image: both need Containers.
|
|
10670
10693
|
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
|
|
10694
|
+
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
10695
|
},
|
|
10673
10696
|
{
|
|
10674
10697
|
name: "resident",
|
|
@@ -11788,11 +11811,11 @@ var init_runStoreWorker = __esm({
|
|
|
11788
11811
|
fetchImpl;
|
|
11789
11812
|
abandoned() {
|
|
11790
11813
|
}
|
|
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(
|
|
11814
|
+
async put(record3, trace) {
|
|
11815
|
+
if (!RUN_ID_PATTERN.test(record3.id))
|
|
11816
|
+
throw new PermanentStoreError(`run store: refusing to put malformed id ${JSON.stringify(record3.id)}`);
|
|
11817
|
+
const body = { storeKey: this.opts.storeKey, record: record3 };
|
|
11818
|
+
const point = pointOf(record3, this.opts.prices);
|
|
11796
11819
|
if (point !== void 0) body.point = point;
|
|
11797
11820
|
if (this.opts.policy) {
|
|
11798
11821
|
body.policy = this.opts.policy;
|
|
@@ -11962,6 +11985,66 @@ var init_supersede = __esm({
|
|
|
11962
11985
|
}
|
|
11963
11986
|
});
|
|
11964
11987
|
|
|
11988
|
+
// ../../src/deploy/botLiveGate.ts
|
|
11989
|
+
function judge(input) {
|
|
11990
|
+
if ("error" in input.app)
|
|
11991
|
+
return waiting(`container application ${input.containerApp} unreadable: ${input.app.error}`);
|
|
11992
|
+
const app = input.app.value;
|
|
11993
|
+
let rollout;
|
|
11994
|
+
if (input.target) {
|
|
11995
|
+
if (input.target.image !== null && app.image !== input.target.image)
|
|
11996
|
+
return waiting(
|
|
11997
|
+
`container application ${input.containerApp} still targets ${app.image ?? "an unknown image"}; expected ${input.target.image}`
|
|
11998
|
+
);
|
|
11999
|
+
if ("error" in input.before)
|
|
12000
|
+
return waiting(
|
|
12001
|
+
`container application ${input.containerApp} pre-deploy version unreadable (${input.before.error}); cannot prove the application version advanced`
|
|
12002
|
+
);
|
|
12003
|
+
if (app.version <= input.before.value.version)
|
|
12004
|
+
return waiting(
|
|
12005
|
+
`container application ${input.containerApp} is still at pre-deploy version ${input.before.value.version}; expected a newer application version`
|
|
12006
|
+
);
|
|
12007
|
+
rollout = ` at version ${app.version} (up from ${input.before.value.version})`;
|
|
12008
|
+
} else {
|
|
12009
|
+
rollout = ` at version ${app.version} (Worker-only deploy; no application change printed)`;
|
|
12010
|
+
}
|
|
12011
|
+
if ("error" in input.health) return waiting(`health: GET /healthz failed: ${input.health.error}`);
|
|
12012
|
+
if (input.health.status !== 200)
|
|
12013
|
+
return waiting(
|
|
12014
|
+
`health: GET /healthz \u2192 HTTP ${input.health.status}; expected the deployed container's exact build identity`
|
|
12015
|
+
);
|
|
12016
|
+
if (!input.health.body || input.health.body.ok !== true)
|
|
12017
|
+
return waiting("health: /healthz is not ready or did not answer with JSON");
|
|
12018
|
+
const commit = servedCommit(input.health.body);
|
|
12019
|
+
if (!commit) return waiting("health: /healthz carries no build identity");
|
|
12020
|
+
if (commit !== input.expectedCommit)
|
|
12021
|
+
return waiting(`health: serving commit ${commit}, expected exact ${input.expectedCommit}`);
|
|
12022
|
+
if (input.health.body.draining === true)
|
|
12023
|
+
return waiting("health: the exact deployed commit answers but its container is draining");
|
|
12024
|
+
const target = input.target?.image ?? app.image ?? "an unchanged image";
|
|
12025
|
+
return {
|
|
12026
|
+
kind: "live",
|
|
12027
|
+
summary: `container application ${input.containerApp} targets ${target}${rollout}; /healthz serves exact ${commit}`
|
|
12028
|
+
};
|
|
12029
|
+
}
|
|
12030
|
+
function decideBotLive(input, deadlineMs = LIVE_GATE_DEADLINE_MS) {
|
|
12031
|
+
const decision = judge(input);
|
|
12032
|
+
if (decision.kind !== "waiting" || input.elapsedMs < deadlineMs) return decision;
|
|
12033
|
+
return {
|
|
12034
|
+
kind: "failed",
|
|
12035
|
+
reason: `${decision.reason} \u2014 still not live after ${Math.round(input.elapsedMs / MINUTE_MS)} min (deadline ${deadlineMs / MINUTE_MS} min)`
|
|
12036
|
+
};
|
|
12037
|
+
}
|
|
12038
|
+
var waiting;
|
|
12039
|
+
var init_botLiveGate = __esm({
|
|
12040
|
+
"../../src/deploy/botLiveGate.ts"() {
|
|
12041
|
+
"use strict";
|
|
12042
|
+
init_budgets();
|
|
12043
|
+
init_liveGate();
|
|
12044
|
+
waiting = (reason2) => ({ kind: "waiting", reason: reason2 });
|
|
12045
|
+
}
|
|
12046
|
+
});
|
|
12047
|
+
|
|
11965
12048
|
// ../../src/execution/sandboxErrors.ts
|
|
11966
12049
|
function fleetBusyRunEndedLine(run2, thread, err2) {
|
|
11967
12050
|
if (typeof err2 !== "object" || err2 === null) return null;
|
|
@@ -12048,51 +12131,51 @@ function rolloutAdvanced(app, before, target) {
|
|
|
12048
12131
|
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
12132
|
};
|
|
12050
12133
|
}
|
|
12051
|
-
function
|
|
12134
|
+
function judge2(input) {
|
|
12052
12135
|
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
|
|
12136
|
+
if (!worker.ok) return worker.fatal ? { kind: "failed", reason: worker.reason } : waiting2(worker.reason);
|
|
12137
|
+
if (input.app === null) return waiting2("rollout: not read yet");
|
|
12138
|
+
if ("error" in input.app) return waiting2(`rollout: ${input.app.error}`);
|
|
12056
12139
|
const app = input.app.value;
|
|
12057
12140
|
const appVersion = app.version;
|
|
12058
12141
|
let advancedFrom = null;
|
|
12059
12142
|
if (input.target) {
|
|
12060
12143
|
const advanced = rolloutAdvanced(app, input.before, input.target);
|
|
12061
|
-
if (!advanced.ok) return
|
|
12144
|
+
if (!advanced.ok) return waiting2(advanced.reason);
|
|
12062
12145
|
advancedFrom = advanced.from;
|
|
12063
12146
|
}
|
|
12064
|
-
if (input.instances === null) return
|
|
12065
|
-
if ("error" in input.instances) return
|
|
12147
|
+
if (input.instances === null) return waiting2("rollout: instances not read yet");
|
|
12148
|
+
if ("error" in input.instances) return waiting2(`rollout: ${input.instances.error}`);
|
|
12066
12149
|
const running = input.instances.value.filter((i) => i.state === "running");
|
|
12067
12150
|
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
12151
|
const stale = running.filter((i) => i.version !== appVersion);
|
|
12069
12152
|
if (stale.length > 0) {
|
|
12070
12153
|
const versions = [...new Set(stale.map((i) => i.version === null ? "?" : String(i.version)))].join("/");
|
|
12071
|
-
return
|
|
12154
|
+
return waiting2(
|
|
12072
12155
|
`rollout in progress \u2014 ${stale.length} of ${running.length} running instance(s) still on version ${versions}, app version ${appVersion}`
|
|
12073
12156
|
);
|
|
12074
12157
|
}
|
|
12075
|
-
if (input.probe === null) return
|
|
12076
|
-
if ("error" in input.probe) return
|
|
12158
|
+
if (input.probe === null) return waiting2("probe: not sent yet");
|
|
12159
|
+
if ("error" in input.probe) return waiting2(`probe: ${input.probe.error}`);
|
|
12077
12160
|
const body = input.probe.body;
|
|
12078
12161
|
if (body.reason === FLEET_BUSY_REASON)
|
|
12079
|
-
return
|
|
12162
|
+
return waiting2("probe: fleet busy \u2014 no free instance for the probe thread (max_instances reached)");
|
|
12080
12163
|
if (typeof body.error === "string" && CONTAINER_STARTING.test(body.error))
|
|
12081
|
-
return
|
|
12164
|
+
return waiting2("probe: container starting");
|
|
12082
12165
|
if ("error" in body) {
|
|
12083
12166
|
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
|
|
12167
|
+
return waiting2(`probe: ${failed} \u2014 the probe's container may still run the previous image`);
|
|
12085
12168
|
}
|
|
12086
12169
|
if (body.exitCode !== 0) {
|
|
12087
12170
|
const stderr = typeof body.stderr === "string" ? body.stderr.trim() : "";
|
|
12088
|
-
return
|
|
12171
|
+
return waiting2(`probe: \`${PROBE_COMMAND}\` exited ${String(body.exitCode)}${stderr ? `: ${stderr}` : ""}`);
|
|
12089
12172
|
}
|
|
12090
12173
|
const stdout = typeof body.stdout === "string" ? body.stdout.trim() : "";
|
|
12091
|
-
if (stdout !== "ok") return
|
|
12174
|
+
if (stdout !== "ok") return waiting2(`probe: \`${PROBE_COMMAND}\` printed ${JSON.stringify(stdout)}`);
|
|
12092
12175
|
const mine = input.instances.value.find((i) => i.name === input.probeThreadKey);
|
|
12093
|
-
if (!mine) return
|
|
12176
|
+
if (!mine) return waiting2(`probe instance ${input.probeThreadKey} not listed yet`);
|
|
12094
12177
|
if (mine.version !== appVersion)
|
|
12095
|
-
return
|
|
12178
|
+
return waiting2(
|
|
12096
12179
|
`probe instance ${input.probeThreadKey} is on version ${mine.version === null ? "?" : mine.version}, app version ${appVersion} \u2014 the probe landed on a previous image`
|
|
12097
12180
|
);
|
|
12098
12181
|
return {
|
|
@@ -12101,7 +12184,7 @@ function judge(input) {
|
|
|
12101
12184
|
};
|
|
12102
12185
|
}
|
|
12103
12186
|
function decideSandboxLive(input, deadlineMs = LIVE_GATE_DEADLINE_MS) {
|
|
12104
|
-
const d =
|
|
12187
|
+
const d = judge2(input);
|
|
12105
12188
|
if (d.kind !== "waiting" || input.elapsedMs < deadlineMs) return d;
|
|
12106
12189
|
return {
|
|
12107
12190
|
kind: "failed",
|
|
@@ -12175,7 +12258,7 @@ function parseExecStream(text) {
|
|
|
12175
12258
|
return { error: `/exec answered no JSON document: ${JSON.stringify(text.trim().slice(0, 120))}` };
|
|
12176
12259
|
}
|
|
12177
12260
|
}
|
|
12178
|
-
var PROBE_COMMAND, PROBE_TIMEOUT_MS, CONTAINER_STARTING,
|
|
12261
|
+
var PROBE_COMMAND, PROBE_TIMEOUT_MS, CONTAINER_STARTING, waiting2, isRecord2, ANSI_SEQUENCE2, SECTION_GUTTER;
|
|
12179
12262
|
var init_sandboxLiveGate = __esm({
|
|
12180
12263
|
"../../src/deploy/sandboxLiveGate.ts"() {
|
|
12181
12264
|
"use strict";
|
|
@@ -12184,7 +12267,7 @@ var init_sandboxLiveGate = __esm({
|
|
|
12184
12267
|
PROBE_COMMAND = "echo ok";
|
|
12185
12268
|
PROBE_TIMEOUT_MS = 6e4;
|
|
12186
12269
|
CONTAINER_STARTING = /Container is starting/i;
|
|
12187
|
-
|
|
12270
|
+
waiting2 = (reason2) => ({ kind: "waiting", reason: reason2 });
|
|
12188
12271
|
isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
12189
12272
|
ANSI_SEQUENCE2 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
12190
12273
|
SECTION_GUTTER = /^[\s│├╭╰|]+/;
|
|
@@ -12510,26 +12593,6 @@ async function fetchHealthz(url) {
|
|
|
12510
12593
|
const r = await readHealthz(url);
|
|
12511
12594
|
return "body" in r ? r.body : void 0;
|
|
12512
12595
|
}
|
|
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
12596
|
async function wranglerJson(dir, args) {
|
|
12534
12597
|
const r = await run("npx", ["wrangler", ...args], { cwd: workerDir(dir), unset: UNSET_ENV });
|
|
12535
12598
|
if (r.code !== 0)
|
|
@@ -12550,6 +12613,29 @@ async function resolveContainerAppId(dir, containerApp) {
|
|
|
12550
12613
|
containerAppIds.set(containerApp, id);
|
|
12551
12614
|
return { value: id };
|
|
12552
12615
|
}
|
|
12616
|
+
async function waitUntilBotLive(step, gate, expectedCommit, rollout, io, deps = defaultSandboxGateDeps) {
|
|
12617
|
+
const started = deps.now();
|
|
12618
|
+
for (; ; ) {
|
|
12619
|
+
const elapsed2 = deps.now() - started;
|
|
12620
|
+
const app = await deps.readAppState(step.dir, gate.containerApp);
|
|
12621
|
+
const health = await deps.readHealth(gate.healthUrl);
|
|
12622
|
+
const decision = decideBotLive({
|
|
12623
|
+
containerApp: gate.containerApp,
|
|
12624
|
+
health,
|
|
12625
|
+
app,
|
|
12626
|
+
before: rollout.before,
|
|
12627
|
+
target: rollout.target,
|
|
12628
|
+
expectedCommit,
|
|
12629
|
+
elapsedMs: elapsed2
|
|
12630
|
+
});
|
|
12631
|
+
if (decision.kind === "live") return { live: true, detail: decision.summary, waitedMs: deps.now() - started };
|
|
12632
|
+
if (decision.kind === "failed") return { live: false, reason: decision.reason };
|
|
12633
|
+
io.log(
|
|
12634
|
+
`[deploy:all] ${step.name}: deployed, not live yet \u2014 ${decision.reason} (${Math.floor(elapsed2 / 6e4)}m ${Math.floor(elapsed2 % 6e4 / 1e3)}s)`
|
|
12635
|
+
);
|
|
12636
|
+
await deps.sleep(LIVE_GATE_POLL_MS);
|
|
12637
|
+
}
|
|
12638
|
+
}
|
|
12553
12639
|
async function waitUntilSandboxLive(step, gate, expectedCommit, rollout, io, deps = defaultSandboxGateDeps) {
|
|
12554
12640
|
const bearer = deps.env[gate.bearerEnv];
|
|
12555
12641
|
if (!bearer)
|
|
@@ -12590,7 +12676,7 @@ async function waitUntilSandboxLive(step, gate, expectedCommit, rollout, io, dep
|
|
|
12590
12676
|
async function readAppBeforeUpload(step, gate, io, deps) {
|
|
12591
12677
|
const before = await deps.readAppState(step.dir, gate.containerApp);
|
|
12592
12678
|
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`
|
|
12679
|
+
"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
12680
|
);
|
|
12595
12681
|
return before;
|
|
12596
12682
|
}
|
|
@@ -12666,8 +12752,7 @@ async function deployStepLoop(step, plan3, expectedCommit, io, deps, exec, root,
|
|
|
12666
12752
|
for (; ; ) {
|
|
12667
12753
|
io.log(`
|
|
12668
12754
|
[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;
|
|
12755
|
+
const application = step.liveGate ? { before: await readAppBeforeUpload(step, step.liveGate, io, deps) } : void 0;
|
|
12671
12756
|
const r = await exec(step, io);
|
|
12672
12757
|
const outcome = classifyDeployOutput(r.code, r.output);
|
|
12673
12758
|
if (outcome.kind === "deployed") {
|
|
@@ -12680,15 +12765,20 @@ async function deployStepLoop(step, plan3, expectedCommit, io, deps, exec, root,
|
|
|
12680
12765
|
);
|
|
12681
12766
|
const liveGate = step.liveGate;
|
|
12682
12767
|
const gate = await root.span("deploy.wait_live", async (wait2) => {
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12768
|
+
if (!application) throw new Error(`${step.name}: live gate has no application state`);
|
|
12769
|
+
const target = rolloutTargetFromDeployOutput(r.output);
|
|
12770
|
+
const from = "value" in application.before ? `version ${application.before.value.version}` : "its pre-deploy version";
|
|
12771
|
+
io.log(
|
|
12772
|
+
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`
|
|
12773
|
+
);
|
|
12774
|
+
const g = liveGate.kind === "sandbox" ? await waitUntilSandboxLive(
|
|
12775
|
+
step,
|
|
12776
|
+
liveGate,
|
|
12777
|
+
expectedCommit,
|
|
12778
|
+
{ before: application.before, target },
|
|
12779
|
+
io,
|
|
12780
|
+
deps
|
|
12781
|
+
) : await waitUntilBotLive(step, liveGate, expectedCommit, { before: application.before, target }, io, deps);
|
|
12692
12782
|
wait2.setAttrs(g.live ? { outcome: "live", waitedMs: g.waitedMs } : { outcome: "not_live" });
|
|
12693
12783
|
return g;
|
|
12694
12784
|
});
|
|
@@ -13105,6 +13195,7 @@ var init_run = __esm({
|
|
|
13105
13195
|
init_planeDeploy();
|
|
13106
13196
|
init_wranglerTemplate();
|
|
13107
13197
|
init_supersede();
|
|
13198
|
+
init_botLiveGate();
|
|
13108
13199
|
init_sandboxLiveGate();
|
|
13109
13200
|
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
13110
13201
|
workerDir = (dir) => workPath(OPERATOR_ROOT, dir);
|
|
@@ -17364,7 +17455,7 @@ async function branchHeadSubject(repo, branch2) {
|
|
|
17364
17455
|
if (typeof message4 !== "string" || message4.trim() === "") return void 0;
|
|
17365
17456
|
return message4.split("\n", 1)[0].trim();
|
|
17366
17457
|
}
|
|
17367
|
-
async function
|
|
17458
|
+
async function readHeadRef(repo, branch2, headers) {
|
|
17368
17459
|
let res;
|
|
17369
17460
|
try {
|
|
17370
17461
|
res = await fetch(`https://api.github.com/repos/${repo}/git/ref/heads/${encodeGithubRef(branch2)}`, {
|
|
@@ -17372,13 +17463,17 @@ async function headRefTipSha(repo, branch2, headers) {
|
|
|
17372
17463
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
17373
17464
|
});
|
|
17374
17465
|
} catch {
|
|
17375
|
-
return
|
|
17466
|
+
return { kind: "unverified" };
|
|
17376
17467
|
}
|
|
17377
|
-
if (
|
|
17468
|
+
if (res.status === 404) return { kind: "missing" };
|
|
17469
|
+
if (!res.ok) return { kind: "unverified" };
|
|
17378
17470
|
const data = await res.json().catch(() => null);
|
|
17379
|
-
|
|
17380
|
-
|
|
17381
|
-
|
|
17471
|
+
const sha = data?.object?.sha;
|
|
17472
|
+
return data?.object?.type === "commit" && typeof sha === "string" && /^[0-9a-f]{40}$/.test(sha) ? { kind: "verified", sha } : { kind: "unverified" };
|
|
17473
|
+
}
|
|
17474
|
+
async function headRefTipSha(repo, branch2, headers) {
|
|
17475
|
+
const read = await readHeadRef(repo, branch2, headers);
|
|
17476
|
+
return read.kind === "verified" ? read.sha : void 0;
|
|
17382
17477
|
}
|
|
17383
17478
|
function preferRefTip(where, prSha, refTip, branch2) {
|
|
17384
17479
|
if (refTip === void 0) return prSha;
|
|
@@ -17428,8 +17523,11 @@ async function fetchPullRequestFacts(pr) {
|
|
|
17428
17523
|
const prSha = typeof data.head?.sha === "string" && /^[0-9a-f]{40}$/.test(data.head.sha) ? data.head.sha : void 0;
|
|
17429
17524
|
const sameRepoHead = headRepo === pr.repo.toLowerCase();
|
|
17430
17525
|
const headRef = typeof data.head?.ref === "string" && data.head.ref ? data.head.ref : void 0;
|
|
17431
|
-
const
|
|
17432
|
-
const
|
|
17526
|
+
const headRead = sameRepoHead && headRef !== void 0 ? await readHeadRef(pr.repo, headRef, headers) : void 0;
|
|
17527
|
+
const headBranchExists = headRead?.kind === "verified" ? true : headRead?.kind === "missing" ? false : void 0;
|
|
17528
|
+
const refTip = headRead?.kind === "verified" ? headRead.sha : void 0;
|
|
17529
|
+
const sha = sameRepoHead && headRef !== void 0 ? preferRefTip(`${pr.repo}#${pr.number}`, prSha, refTip, headRef) : prSha;
|
|
17530
|
+
const verifiedHead = sameRepoHead && headRef !== void 0 && prSha !== void 0 && refTip === prSha ? { repo: pr.repo, ref: headRef, sha: refTip } : void 0;
|
|
17433
17531
|
let closedBy = typeof data.closed_by?.login === "string" ? data.closed_by.login : void 0;
|
|
17434
17532
|
if (data.state === "closed" && !(typeof data.merged_at === "string" && data.merged_at) && closedBy === void 0) {
|
|
17435
17533
|
try {
|
|
@@ -17454,6 +17552,7 @@ async function fetchPullRequestFacts(pr) {
|
|
|
17454
17552
|
} : {},
|
|
17455
17553
|
...headRef !== void 0 ? { headRef } : {},
|
|
17456
17554
|
...sha ? { headSha: sha } : {},
|
|
17555
|
+
...verifiedHead !== void 0 ? { verifiedHead } : {},
|
|
17457
17556
|
...headBranchExists !== void 0 ? { headBranchExists } : {},
|
|
17458
17557
|
sameRepoHead,
|
|
17459
17558
|
...typeof data.base?.ref === "string" && data.base.ref ? { baseRef: data.base.ref } : {},
|
|
@@ -21081,8 +21180,8 @@ function pageEvents(events, opts) {
|
|
|
21081
21180
|
if (page2.length < rest.length && page2.length > 0) out.nextAfterSeq = page2[page2.length - 1].seq;
|
|
21082
21181
|
return out;
|
|
21083
21182
|
}
|
|
21084
|
-
function toListItem(
|
|
21085
|
-
const { events: _events, ...rest } =
|
|
21183
|
+
function toListItem(record3, bytes) {
|
|
21184
|
+
const { events: _events, ...rest } = record3;
|
|
21086
21185
|
return { ...rest, bytes };
|
|
21087
21186
|
}
|
|
21088
21187
|
function retentionPolicyOf(cfg) {
|
|
@@ -21233,19 +21332,19 @@ var init_runStore = __esm({
|
|
|
21233
21332
|
}
|
|
21234
21333
|
abandoned() {
|
|
21235
21334
|
}
|
|
21236
|
-
async put(
|
|
21237
|
-
if (!isValidRunId(
|
|
21335
|
+
async put(record3) {
|
|
21336
|
+
if (!isValidRunId(record3.id))
|
|
21238
21337
|
return { ok: true, retained: this.readIndex().length, stored: false, rewritten: false };
|
|
21239
21338
|
this.ensureDir();
|
|
21240
|
-
const content = JSON.stringify(
|
|
21339
|
+
const content = JSON.stringify(record3);
|
|
21241
21340
|
const bytes = utf8ByteLength(content);
|
|
21242
21341
|
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 ===
|
|
21342
|
+
const prev = index.find((r) => r.id === record3.id);
|
|
21343
|
+
const rewritten = prev !== void 0 && !sameStoredVersion(prev, { ...record3, bytes });
|
|
21344
|
+
this.writeAtomic(this.recordPath(record3.id), content);
|
|
21345
|
+
const item = toListItem(record3, bytes);
|
|
21346
|
+
const kept = this.compact([...index.filter((r) => r.id !== record3.id), item]);
|
|
21347
|
+
return { ok: true, retained: kept.length, stored: kept.some((r) => r.id === record3.id), rewritten };
|
|
21249
21348
|
}
|
|
21250
21349
|
/** Retention is decided from the index alone (no per-file stat); the one
|
|
21251
21350
|
* file that matters is then read directly — a torn or missing file lands
|
|
@@ -21272,16 +21371,16 @@ var init_runStore = __esm({
|
|
|
21272
21371
|
return selectListItems(this.retainedIntact(), opts);
|
|
21273
21372
|
}
|
|
21274
21373
|
async events(id, opts) {
|
|
21275
|
-
const
|
|
21276
|
-
return
|
|
21374
|
+
const record3 = await this.get(id);
|
|
21375
|
+
return record3 ? pageEvents(record3.events, opts) : null;
|
|
21277
21376
|
}
|
|
21278
21377
|
async usage(query) {
|
|
21279
21378
|
const items = this.retainedIntact();
|
|
21280
21379
|
const records = [];
|
|
21281
21380
|
for (const item of items) {
|
|
21282
21381
|
if (item.finishedAt < query.sinceMs || item.finishedAt >= query.untilMs) continue;
|
|
21283
|
-
const
|
|
21284
|
-
if (
|
|
21382
|
+
const record3 = await this.get(item.id);
|
|
21383
|
+
if (record3) records.push(record3);
|
|
21285
21384
|
}
|
|
21286
21385
|
const parent = (id) => {
|
|
21287
21386
|
const p = items.find((i) => i.id === id);
|
|
@@ -21999,24 +22098,24 @@ function predicateWith(rules, actor, action, resourceType, kind) {
|
|
|
21999
22098
|
function predicateFor(actor, action, resourceType, kind) {
|
|
22000
22099
|
return predicateWith(POLICY, actor, action, resourceType, kind);
|
|
22001
22100
|
}
|
|
22002
|
-
function matchesPredicate(predicate,
|
|
22101
|
+
function matchesPredicate(predicate, record3) {
|
|
22003
22102
|
switch (predicate.kind) {
|
|
22004
22103
|
case "none":
|
|
22005
22104
|
return false;
|
|
22006
22105
|
case "all":
|
|
22007
22106
|
return true;
|
|
22008
22107
|
case "channels-in":
|
|
22009
|
-
return
|
|
22108
|
+
return record3.channelId !== void 0 && holds(predicate.channelIds, record3.channelId);
|
|
22010
22109
|
case "user-is":
|
|
22011
|
-
return
|
|
22110
|
+
return record3.userId !== void 0 && record3.userId === predicate.userId;
|
|
22012
22111
|
case "repos-in":
|
|
22013
|
-
return
|
|
22112
|
+
return record3.repo !== void 0 && holds(predicate.repos, record3.repo);
|
|
22014
22113
|
case "visibility-in":
|
|
22015
|
-
return predicate.visibilities.has(
|
|
22114
|
+
return predicate.visibilities.has(record3.channelVisibility ?? "unknown");
|
|
22016
22115
|
case "or":
|
|
22017
|
-
return predicate.of.some((p) => matchesPredicate(p,
|
|
22116
|
+
return predicate.of.some((p) => matchesPredicate(p, record3));
|
|
22018
22117
|
case "and":
|
|
22019
|
-
return predicate.of.length > 0 && predicate.of.every((p) => matchesPredicate(p,
|
|
22118
|
+
return predicate.of.length > 0 && predicate.of.every((p) => matchesPredicate(p, record3));
|
|
22020
22119
|
}
|
|
22021
22120
|
return false;
|
|
22022
22121
|
}
|
|
@@ -25602,9 +25701,9 @@ function endingCauseWords(cause) {
|
|
|
25602
25701
|
function isReissueSteerText(text) {
|
|
25603
25702
|
return /^the model provider .+ is answering again — re-issue the held turn and continue$/.test(text.trim());
|
|
25604
25703
|
}
|
|
25605
|
-
function waitingWords(
|
|
25606
|
-
if (
|
|
25607
|
-
return
|
|
25704
|
+
function waitingWords(waiting3) {
|
|
25705
|
+
if (waiting3.length === 0) return "its turn";
|
|
25706
|
+
return waiting3.map(
|
|
25608
25707
|
(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
25708
|
).join(", then ");
|
|
25610
25709
|
}
|
|
@@ -28876,23 +28975,23 @@ var init_turnEffort = __esm({
|
|
|
28876
28975
|
function tokenize2(text) {
|
|
28877
28976
|
return text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
28878
28977
|
}
|
|
28879
|
-
function keywordMatch(
|
|
28978
|
+
function keywordMatch(record3, query) {
|
|
28880
28979
|
const queryTokens = typeof query === "string" ? tokenize2(query) : query;
|
|
28881
28980
|
if (queryTokens.length === 0) return 0;
|
|
28882
|
-
const recordTokens = /* @__PURE__ */ new Set([...tokenize2(
|
|
28981
|
+
const recordTokens = /* @__PURE__ */ new Set([...tokenize2(record3.keywords.join(" ")), ...tokenize2(record3.text)]);
|
|
28883
28982
|
let hits = 0;
|
|
28884
28983
|
for (const token2 of queryTokens) {
|
|
28885
28984
|
if (recordTokens.has(token2)) hits++;
|
|
28886
28985
|
}
|
|
28887
28986
|
return hits / queryTokens.length;
|
|
28888
28987
|
}
|
|
28889
|
-
function recencyScore(
|
|
28890
|
-
const ts =
|
|
28988
|
+
function recencyScore(record3, now, tau = RECENCY_TAU_MS) {
|
|
28989
|
+
const ts = record3.lastUsedAt ?? record3.createdAt;
|
|
28891
28990
|
const age = Math.max(0, now - ts);
|
|
28892
28991
|
return Math.exp(-age / tau);
|
|
28893
28992
|
}
|
|
28894
|
-
function scoreRecord(
|
|
28895
|
-
return weights.keyword * keywordMatch(
|
|
28993
|
+
function scoreRecord(record3, query, now, weights = DEFAULT_WEIGHTS, tau = RECENCY_TAU_MS) {
|
|
28994
|
+
return weights.keyword * keywordMatch(record3, query) + weights.recency * recencyScore(record3, now, tau);
|
|
28896
28995
|
}
|
|
28897
28996
|
function estimateTokens(text) {
|
|
28898
28997
|
return Math.ceil(text.length / 4);
|
|
@@ -28900,11 +28999,11 @@ function estimateTokens(text) {
|
|
|
28900
28999
|
function applyBudget(records, budget) {
|
|
28901
29000
|
const out = [];
|
|
28902
29001
|
let tokens = 0;
|
|
28903
|
-
for (const
|
|
29002
|
+
for (const record3 of records) {
|
|
28904
29003
|
if (out.length >= budget.maxRecords) break;
|
|
28905
|
-
const cost = estimateTokens(renderMemoryBullet(
|
|
29004
|
+
const cost = estimateTokens(renderMemoryBullet(record3));
|
|
28906
29005
|
if (out.length > 0 && tokens + cost > budget.maxTokens) break;
|
|
28907
|
-
out.push(
|
|
29006
|
+
out.push(record3);
|
|
28908
29007
|
tokens += cost;
|
|
28909
29008
|
}
|
|
28910
29009
|
return out;
|
|
@@ -28915,8 +29014,8 @@ function memoryBlockPrefix(resource) {
|
|
|
28915
29014
|
function sanitizeMemoryField(s) {
|
|
28916
29015
|
return s.replace(/[\x00-\x1f\x7f-\x9f\u2028\u2029]/g, " ").replace(/\s+/g, " ").trim().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
28917
29016
|
}
|
|
28918
|
-
function renderMemoryBullet(
|
|
28919
|
-
return `- ${sanitizeMemoryField(
|
|
29017
|
+
function renderMemoryBullet(record3) {
|
|
29018
|
+
return `- ${sanitizeMemoryField(record3.text)} (source: ${sanitizeMemoryField(record3.sourceThreadKey)})`;
|
|
28920
29019
|
}
|
|
28921
29020
|
function renderMemoryBlock(resource, records) {
|
|
28922
29021
|
const bullets = records.map(renderMemoryBullet);
|
|
@@ -29502,7 +29601,7 @@ function parseConfirmation(v) {
|
|
|
29502
29601
|
function isRefusal(v) {
|
|
29503
29602
|
return typeof v === "string" && CONFIRMATION_REFUSALS.includes(v);
|
|
29504
29603
|
}
|
|
29505
|
-
function
|
|
29604
|
+
function judge3(rows, id, actorIds, now, kind) {
|
|
29506
29605
|
const row2 = rows.get(id);
|
|
29507
29606
|
if (!row2) return { ok: false, refused: "used" };
|
|
29508
29607
|
if (kind === "consume" && row2.expiresAt <= now) {
|
|
@@ -29572,13 +29671,13 @@ var init_confirmations = __esm({
|
|
|
29572
29671
|
}
|
|
29573
29672
|
async consume(id, actorIds) {
|
|
29574
29673
|
const rows = this.read();
|
|
29575
|
-
const out =
|
|
29674
|
+
const out = judge3(rows, id, actorIds, this.clock(), "consume");
|
|
29576
29675
|
this.write(rows);
|
|
29577
29676
|
return out;
|
|
29578
29677
|
}
|
|
29579
29678
|
async cancel(id, actorIds) {
|
|
29580
29679
|
const rows = this.read();
|
|
29581
|
-
const out =
|
|
29680
|
+
const out = judge3(rows, id, actorIds, this.clock(), "cancel");
|
|
29582
29681
|
this.write(rows);
|
|
29583
29682
|
return out;
|
|
29584
29683
|
}
|
|
@@ -29586,7 +29685,7 @@ var init_confirmations = __esm({
|
|
|
29586
29685
|
const rows = this.read();
|
|
29587
29686
|
const id = threadRowId(rows, threadKey);
|
|
29588
29687
|
if (id === void 0) return { ok: false, refused: "used" };
|
|
29589
|
-
const out =
|
|
29688
|
+
const out = judge3(rows, id, actorIds, this.clock(), "cancel");
|
|
29590
29689
|
this.write(rows);
|
|
29591
29690
|
return out;
|
|
29592
29691
|
}
|
|
@@ -30067,16 +30166,16 @@ async function reflect(deps) {
|
|
|
30067
30166
|
dropped.push(`${placement.from} (${placement.reason})`);
|
|
30068
30167
|
continue;
|
|
30069
30168
|
}
|
|
30070
|
-
let
|
|
30169
|
+
let record3 = plain7;
|
|
30071
30170
|
if (placement.narrowed) {
|
|
30072
|
-
const { supersedes: _supersedes, restates: _restates, ...withoutPointers } =
|
|
30073
|
-
|
|
30171
|
+
const { supersedes: _supersedes, restates: _restates, ...withoutPointers } = record3;
|
|
30172
|
+
record3 = withoutPointers;
|
|
30074
30173
|
const line4 = `${placement.narrowed.from} \u2192 ${placement.target.kind} (${placement.narrowed.reason})`;
|
|
30075
30174
|
narrowed.set(line4, (narrowed.get(line4) ?? 0) + 1);
|
|
30076
30175
|
}
|
|
30077
30176
|
let batch = byScope.get(placement.target.key);
|
|
30078
30177
|
if (!batch) byScope.set(placement.target.key, batch = []);
|
|
30079
|
-
batch.push(
|
|
30178
|
+
batch.push(record3);
|
|
30080
30179
|
}
|
|
30081
30180
|
if (narrowed.size > 0)
|
|
30082
30181
|
warn(`write narrowed by policy: ${[...narrowed].map(([line4, n2]) => `${n2}\xD7 ${line4}`).join(", ")}`);
|
|
@@ -31907,8 +32006,8 @@ ${line4}`
|
|
|
31907
32006
|
const list = await call(() => api.residents());
|
|
31908
32007
|
if (list.status !== 200) throw residentFailure(list);
|
|
31909
32008
|
const residents = list.data.residents ?? [];
|
|
31910
|
-
const
|
|
31911
|
-
if (!
|
|
32009
|
+
const record3 = residents.find((rec2) => rec2.resource === repoResourceId(args.slug));
|
|
32010
|
+
if (!record3) {
|
|
31912
32011
|
const slugs = residents.map(
|
|
31913
32012
|
(rec2) => String(rec2.resource ?? "").replace(/^repo:/, "").toLowerCase()
|
|
31914
32013
|
).filter(Boolean);
|
|
@@ -31928,7 +32027,7 @@ ${line4}`
|
|
|
31928
32027
|
${line4}` : ""}`
|
|
31929
32028
|
);
|
|
31930
32029
|
}
|
|
31931
|
-
body.commands = { ...obj(
|
|
32030
|
+
body.commands = { ...obj(record3.commands), ...commands };
|
|
31932
32031
|
}
|
|
31933
32032
|
if (options.ref !== void 0) body.defaultRef = options.ref;
|
|
31934
32033
|
const r = await call(() => api.reconfigure(body));
|
|
@@ -31953,11 +32052,11 @@ ${line4}` : ""}`
|
|
|
31953
32052
|
|
|
31954
32053
|
// ../../src/core/reviewAbridge.ts
|
|
31955
32054
|
import { resolve as resolve10 } from "node:path";
|
|
31956
|
-
function meatArtifactOf(
|
|
31957
|
-
return
|
|
32055
|
+
function meatArtifactOf(record3) {
|
|
32056
|
+
return record3.events.find((e) => isReadingDiff(e) && e.poweredBy === "meat");
|
|
31958
32057
|
}
|
|
31959
|
-
function gitArtifactOf(
|
|
31960
|
-
return
|
|
32058
|
+
function gitArtifactOf(record3) {
|
|
32059
|
+
return record3.events.find((e) => isReadingDiff(e) && e.poweredBy === "git");
|
|
31961
32060
|
}
|
|
31962
32061
|
function summarize(a) {
|
|
31963
32062
|
return {
|
|
@@ -31971,16 +32070,16 @@ function summarize(a) {
|
|
|
31971
32070
|
};
|
|
31972
32071
|
}
|
|
31973
32072
|
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(
|
|
32073
|
+
const record3 = await store.get(runId3);
|
|
32074
|
+
if (!record3) throw new Error("the run's record is gone \u2014 nothing to append to");
|
|
32075
|
+
const kept = record3.events.filter((e) => !(isReadingDiff(e) && e.poweredBy === "meat"));
|
|
32076
|
+
const replaced = kept.length !== record3.events.length;
|
|
32077
|
+
const seq = Math.max(0, ...storedEventSeqs(record3.events), ...record3.events.map((e) => e.seq ?? 0)) + 1;
|
|
31979
32078
|
const event = { type: "review_artifact", artifact: "reading_diff", ...artifact, at, seq };
|
|
31980
32079
|
const next = fitRecordToBudget({
|
|
31981
|
-
...
|
|
32080
|
+
...record3,
|
|
31982
32081
|
events: [...kept, event],
|
|
31983
|
-
eventCount:
|
|
32082
|
+
eventCount: record3.eventCount + (replaced ? 0 : 1),
|
|
31984
32083
|
storedEventCount: kept.length + 1
|
|
31985
32084
|
});
|
|
31986
32085
|
if (!next.events.includes(event))
|
|
@@ -32069,19 +32168,19 @@ var init_reviewAbridge = __esm({
|
|
|
32069
32168
|
async decide(req) {
|
|
32070
32169
|
const job = this.jobs.get(req.runId);
|
|
32071
32170
|
if (job) return { state: "running", startedAt: job.startedAt };
|
|
32072
|
-
const
|
|
32073
|
-
if (!
|
|
32074
|
-
const existing = meatArtifactOf(
|
|
32171
|
+
const record3 = await this.deps.store.get(req.runId);
|
|
32172
|
+
if (!record3) throw new AbridgeRefusal("not_found", "run not found");
|
|
32173
|
+
const existing = meatArtifactOf(record3);
|
|
32075
32174
|
if (existing && !req.force) return { state: "done", reused: true, artifact: summarize(existing) };
|
|
32076
32175
|
const failed = this.failures.get(req.runId);
|
|
32077
32176
|
if (failed && !req.force) return { state: "failed", ...failed };
|
|
32078
|
-
const git2 = gitArtifactOf(
|
|
32177
|
+
const git2 = gitArtifactOf(record3);
|
|
32079
32178
|
if (!git2)
|
|
32080
32179
|
throw new AbridgeRefusal("conflict", `run ${req.runId} carries no reading diff \u2014 only PR review runs record one`);
|
|
32081
32180
|
const startedAt = this.deps.clock();
|
|
32082
32181
|
const model = req.model ?? this.deps.defaultModel();
|
|
32083
32182
|
this.failures.delete(req.runId);
|
|
32084
|
-
const done = this.produce(
|
|
32183
|
+
const done = this.produce(record3, git2, model).finally(() => this.jobs.delete(req.runId));
|
|
32085
32184
|
this.jobs.set(req.runId, { startedAt, done });
|
|
32086
32185
|
return { state: "running", startedAt };
|
|
32087
32186
|
}
|
|
@@ -32091,8 +32190,8 @@ var init_reviewAbridge = __esm({
|
|
|
32091
32190
|
if (job) return { state: "running", startedAt: job.startedAt };
|
|
32092
32191
|
const failed = this.failures.get(runId3);
|
|
32093
32192
|
if (failed) return { state: "failed", ...failed };
|
|
32094
|
-
const
|
|
32095
|
-
const existing =
|
|
32193
|
+
const record3 = await this.deps.store.get(runId3);
|
|
32194
|
+
const existing = record3 ? meatArtifactOf(record3) : void 0;
|
|
32096
32195
|
return existing ? { state: "done", reused: true, artifact: summarize(existing) } : { state: "absent" };
|
|
32097
32196
|
}
|
|
32098
32197
|
/** The running job's final state, or the current state when none runs. */
|
|
@@ -32123,10 +32222,10 @@ var init_reviewAbridge = __esm({
|
|
|
32123
32222
|
if (!(err2 instanceof AbridgeRefusal)) this.deps.warn(`[reading-diff] ${runId3} abridge failed: ${describe(err2)}`);
|
|
32124
32223
|
}
|
|
32125
32224
|
}
|
|
32126
|
-
async produce(
|
|
32225
|
+
async produce(record3, git2, model) {
|
|
32127
32226
|
const at = () => this.deps.clock();
|
|
32128
32227
|
try {
|
|
32129
|
-
const input = await this.input(
|
|
32228
|
+
const input = await this.input(record3, git2);
|
|
32130
32229
|
const meat = await this.deps.meat({ diff: input.diff, model, timeoutMs: this.deps.timeoutMs() });
|
|
32131
32230
|
if (!meat.ok) throw new Error(meat.reason);
|
|
32132
32231
|
const capped4 = capDiff(sanitizeArtifactText(meat.result.diff));
|
|
@@ -32142,12 +32241,12 @@ var init_reviewAbridge = __esm({
|
|
|
32142
32241
|
inputBytes: input.bytes,
|
|
32143
32242
|
model
|
|
32144
32243
|
};
|
|
32145
|
-
const stored = await appendReviewArtifact(this.deps.store,
|
|
32146
|
-
return this.conclude(
|
|
32244
|
+
const stored = await appendReviewArtifact(this.deps.store, record3.id, artifact, at());
|
|
32245
|
+
return this.conclude(record3.id, { state: "done", reused: false, artifact: summarize(stored) });
|
|
32147
32246
|
} catch (err2) {
|
|
32148
32247
|
const failed = { reason: redactSecrets(describe(err2)), at: at() };
|
|
32149
|
-
this.failures.set(
|
|
32150
|
-
return this.conclude(
|
|
32248
|
+
this.failures.set(record3.id, failed);
|
|
32249
|
+
return this.conclude(record3.id, { state: "failed", ...failed });
|
|
32151
32250
|
}
|
|
32152
32251
|
}
|
|
32153
32252
|
conclude(runId3, state) {
|
|
@@ -32156,8 +32255,8 @@ var init_reviewAbridge = __esm({
|
|
|
32156
32255
|
}
|
|
32157
32256
|
/** The complete diff: GitHub's compare of base...head first; the recorded
|
|
32158
32257
|
* git diff only when that fails AND it is whole. */
|
|
32159
|
-
async input(
|
|
32160
|
-
const meta =
|
|
32258
|
+
async input(record3, git2) {
|
|
32259
|
+
const meta = record3.events.find((e) => e.type === "run_meta");
|
|
32161
32260
|
let compareFailure;
|
|
32162
32261
|
if (!meta?.repo || !meta.headSha) compareFailure = "the run records no repo and head to compare";
|
|
32163
32262
|
else {
|
|
@@ -32176,7 +32275,7 @@ var init_reviewAbridge = __esm({
|
|
|
32176
32275
|
}
|
|
32177
32276
|
}
|
|
32178
32277
|
if (git2.truncated) throw new Error(`diff unavailable: ${compareFailure} and the recorded diff is truncated`);
|
|
32179
|
-
this.deps.warn(`[reading-diff] ${
|
|
32278
|
+
this.deps.warn(`[reading-diff] ${record3.id} using the recorded diff: ${compareFailure}`);
|
|
32180
32279
|
return { diff: git2.diff, source: "recorded", bytes: Buffer.byteLength(git2.diff) };
|
|
32181
32280
|
}
|
|
32182
32281
|
};
|
|
@@ -32882,18 +32981,20 @@ function createRunsService(deps) {
|
|
|
32882
32981
|
try {
|
|
32883
32982
|
const row2 = await ledger.planeQueued(id);
|
|
32884
32983
|
if (!row2) return null;
|
|
32984
|
+
const stampedChannel = row2.request.channelId;
|
|
32885
32985
|
const channelAt = row2.threadKey.lastIndexOf(":");
|
|
32886
|
-
const
|
|
32986
|
+
const channelId = typeof stampedChannel === "string" ? stampedChannel : channelAt > 0 ? row2.threadKey.slice(0, channelAt) : void 0;
|
|
32987
|
+
const waiting3 = waitingWords(row2.conditions);
|
|
32887
32988
|
return {
|
|
32888
32989
|
id: row2.runId,
|
|
32889
|
-
...
|
|
32990
|
+
...channelId !== void 0 ? { channelId } : {},
|
|
32890
32991
|
userId: row2.requester,
|
|
32891
32992
|
threadKey: row2.threadKey,
|
|
32892
32993
|
startedAt: row2.queuedAt,
|
|
32893
32994
|
finished: false,
|
|
32894
32995
|
eventCount: 0,
|
|
32895
|
-
queued: { state: row2.state, position: row2.position, waiting:
|
|
32896
|
-
activity: row2.state === "waiting" ? `queued at position ${row2.position} \u2014 waiting on ${
|
|
32996
|
+
queued: { state: row2.state, position: row2.position, waiting: waiting3 },
|
|
32997
|
+
activity: row2.state === "waiting" ? `queued at position ${row2.position} \u2014 waiting on ${waiting3}` : row2.state === "admitted" ? "admitted \u2014 starting" : "withdrawn"
|
|
32897
32998
|
};
|
|
32898
32999
|
} catch (err2) {
|
|
32899
33000
|
warn(`[runs] plane queued read failed for ${id}: ${describe13(err2)}`);
|
|
@@ -32978,7 +33079,7 @@ function createRunsService(deps) {
|
|
|
32978
33079
|
if (row2) {
|
|
32979
33080
|
const finishedAt = snap?.finishedAt ?? at;
|
|
32980
33081
|
const m = row2.meta;
|
|
32981
|
-
const
|
|
33082
|
+
const record3 = assembleRunRecord({
|
|
32982
33083
|
run: { id, ...summary?.label !== void 0 ? { label: summary.label } : {} },
|
|
32983
33084
|
snap,
|
|
32984
33085
|
agent: m.agent ?? "ship",
|
|
@@ -33006,7 +33107,7 @@ function createRunsService(deps) {
|
|
|
33006
33107
|
}),
|
|
33007
33108
|
seal
|
|
33008
33109
|
});
|
|
33009
|
-
const done = await ledger.finish(id, row2.ownerGen,
|
|
33110
|
+
const done = await ledger.finish(id, row2.ownerGen, record3);
|
|
33010
33111
|
if (!done.ok) warn(`[runs] run ledger finish refused for ${id} (${done.reason ?? "unknown"})`);
|
|
33011
33112
|
}
|
|
33012
33113
|
} catch (err2) {
|
|
@@ -33104,12 +33205,12 @@ function createRunsService(deps) {
|
|
|
33104
33205
|
const queued = await queuedRun(id);
|
|
33105
33206
|
return queued ? { ok: true, value: queued } : notFound;
|
|
33106
33207
|
}
|
|
33107
|
-
const
|
|
33108
|
-
if (!
|
|
33208
|
+
const record3 = await storeGet(id);
|
|
33209
|
+
if (!record3) {
|
|
33109
33210
|
const queued = await queuedRun(id);
|
|
33110
33211
|
return queued ? { ok: true, value: queued } : notFound;
|
|
33111
33212
|
}
|
|
33112
|
-
const { events, ...rest } =
|
|
33213
|
+
const { events, ...rest } = record3;
|
|
33113
33214
|
const view = persistedView(rest, prices);
|
|
33114
33215
|
view.events = events;
|
|
33115
33216
|
return { ok: true, value: view };
|
|
@@ -34990,11 +35091,11 @@ var init_runLedgerWorker = __esm({
|
|
|
34990
35091
|
this.checkIds(runId3, gen);
|
|
34991
35092
|
return this.writeTurns(runId3, gen, turns, session);
|
|
34992
35093
|
}
|
|
34993
|
-
async step(runId3, gen,
|
|
35094
|
+
async step(runId3, gen, record3, turns, session) {
|
|
34994
35095
|
this.checkIds(runId3, gen);
|
|
34995
35096
|
const written = await this.writeTurns(runId3, gen, turns, session);
|
|
34996
35097
|
if (!written.ok) return written;
|
|
34997
|
-
return this.fenceResult(await this.post("/runs/step", { storeKey: this.opts.storeKey, runId: runId3, gen, record:
|
|
35098
|
+
return this.fenceResult(await this.post("/runs/step", { storeKey: this.opts.storeKey, runId: runId3, gen, record: record3 }));
|
|
34998
35099
|
}
|
|
34999
35100
|
checkSessionKey(key) {
|
|
35000
35101
|
if (!SESSION_KEY_PATTERN.test(key))
|
|
@@ -35165,20 +35266,20 @@ var init_runLedgerWorker = __esm({
|
|
|
35165
35266
|
this.checkIds(runId3, gen);
|
|
35166
35267
|
return this.fenceResult(await this.post("/runs/finishing", { storeKey: this.opts.storeKey, runId: runId3, gen }));
|
|
35167
35268
|
}
|
|
35168
|
-
async finish(runId3, gen,
|
|
35269
|
+
async finish(runId3, gen, record3) {
|
|
35169
35270
|
this.checkIds(runId3, gen);
|
|
35170
|
-
const point = pointOf(
|
|
35271
|
+
const point = pointOf(record3, this.opts.prices);
|
|
35171
35272
|
const r = await this.post("/runs/finish", {
|
|
35172
35273
|
storeKey: this.opts.storeKey,
|
|
35173
35274
|
runId: runId3,
|
|
35174
35275
|
gen,
|
|
35175
|
-
record:
|
|
35276
|
+
record: record3,
|
|
35176
35277
|
...point !== void 0 ? { point } : {}
|
|
35177
35278
|
});
|
|
35178
35279
|
const f = this.fenceResult(r);
|
|
35179
35280
|
if (!f.ok) return f;
|
|
35180
|
-
if (
|
|
35181
|
-
await this.post("/runs/session/clear-owner", { key:
|
|
35281
|
+
if (record3.session)
|
|
35282
|
+
await this.post("/runs/session/clear-owner", { key: record3.session.key, runId: runId3, gen }).catch(() => {
|
|
35182
35283
|
});
|
|
35183
35284
|
return { ok: true, stored: r.data.stored === true };
|
|
35184
35285
|
}
|
|
@@ -35877,6 +35978,19 @@ function runPageLink(id) {
|
|
|
35877
35978
|
if (!base) return void 0;
|
|
35878
35979
|
return `${base.replace(/\/+$/, "")}/runs/${encodeURIComponent(id)}`;
|
|
35879
35980
|
}
|
|
35981
|
+
function threadPageLink(threadKey, publicBaseUrl = process.env.PUBLIC_BASE_URL) {
|
|
35982
|
+
if (!publicBaseUrl?.trim()) return void 0;
|
|
35983
|
+
try {
|
|
35984
|
+
const base = new URL(publicBaseUrl.trim());
|
|
35985
|
+
if (!["https:", "http:"].includes(base.protocol) || base.username || base.password) return void 0;
|
|
35986
|
+
base.search = "";
|
|
35987
|
+
base.hash = "";
|
|
35988
|
+
base.pathname = `${base.pathname.replace(/\/+$/, "")}/threads/${encodeURIComponent(threadKey).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)}`;
|
|
35989
|
+
return base.href;
|
|
35990
|
+
} catch {
|
|
35991
|
+
return void 0;
|
|
35992
|
+
}
|
|
35993
|
+
}
|
|
35880
35994
|
function artifactLink(id, key, token2) {
|
|
35881
35995
|
const page2 = runPageLink(id);
|
|
35882
35996
|
if (!page2) return void 0;
|
|
@@ -36554,9 +36668,9 @@ function recordPrOf(ctx) {
|
|
|
36554
36668
|
}
|
|
36555
36669
|
function inheritedPr(thread, records, repo) {
|
|
36556
36670
|
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 &&
|
|
36671
|
+
const record3 = records?.pr && records.pr.repo === repo ? { repo, number: records.pr.number, source: "record", at: records.pr.at } : void 0;
|
|
36672
|
+
if (!turn || !record3) return turn ?? record3;
|
|
36673
|
+
return turn.at !== void 0 && record3.at !== void 0 && turn.at > record3.at ? turn : record3;
|
|
36560
36674
|
}
|
|
36561
36675
|
async function openPrHeadSha(pr) {
|
|
36562
36676
|
const head = await prHead(pr).catch(() => void 0);
|
|
@@ -37164,7 +37278,7 @@ async function classifyMove(fetchPrCommits, q) {
|
|
|
37164
37278
|
async function runReviewPostStep(input) {
|
|
37165
37279
|
const { agent, repoCtx, verdict, carried, logKey } = input;
|
|
37166
37280
|
const { reviewHead, observedHead } = input.heads;
|
|
37167
|
-
const
|
|
37281
|
+
const record3 = (outcome) => {
|
|
37168
37282
|
if (input.publish && agent.name === "review" && !input.hardStopped) {
|
|
37169
37283
|
const where = outcome.posted ? void 0 : repoCtx.repo && repoCtx.pr ? `${repoCtx.repo}#${repoCtx.pr}` : void 0;
|
|
37170
37284
|
input.publish(
|
|
@@ -37263,7 +37377,7 @@ ${carriedFooter(carried)}` : rendered;
|
|
|
37263
37377
|
console.error(`[review-post] ${logKey} failed for ${where}: ${reason2}`);
|
|
37264
37378
|
await input.reply(`\u2139\uFE0F Review not posted to ${where}: ${reason2} \u2014 this verdict is Slack-only.`).catch(() => {
|
|
37265
37379
|
});
|
|
37266
|
-
return
|
|
37380
|
+
return record3({ posted: false, reason: reason2 });
|
|
37267
37381
|
}
|
|
37268
37382
|
console.log(
|
|
37269
37383
|
`[review-post] ${logKey} \u2192 ${where} (${verdict?.verdict ?? "no verdict"})${carried ? ` carried ${carried.reviewed.slice(0, 7)} \u2192 ${pinned.slice(0, 7)}` : ""}`
|
|
@@ -37279,14 +37393,14 @@ ${carriedFooter(carried)}` : rendered;
|
|
|
37279
37393
|
await input.reply(moved).catch(() => {
|
|
37280
37394
|
});
|
|
37281
37395
|
}
|
|
37282
|
-
return
|
|
37396
|
+
return record3({
|
|
37283
37397
|
posted: true,
|
|
37284
37398
|
target: { repo: postTarget.repo, number: postTarget.number },
|
|
37285
37399
|
head: pinned,
|
|
37286
37400
|
...verdict !== void 0 ? { verdict: verdict.verdict } : {}
|
|
37287
37401
|
});
|
|
37288
37402
|
}
|
|
37289
|
-
return
|
|
37403
|
+
return record3({ posted: false, reason: skipReason });
|
|
37290
37404
|
}
|
|
37291
37405
|
var init_reviewRound = __esm({
|
|
37292
37406
|
"../../src/core/reviewRound.ts"() {
|
|
@@ -37643,11 +37757,11 @@ var init_authorize2 = __esm({
|
|
|
37643
37757
|
});
|
|
37644
37758
|
|
|
37645
37759
|
// ../../src/core/runHistoryWriter.ts
|
|
37646
|
-
async function putOnce(sink,
|
|
37760
|
+
async function putOnce(sink, record3) {
|
|
37647
37761
|
try {
|
|
37648
|
-
return await sink.put(
|
|
37762
|
+
return await sink.put(record3);
|
|
37649
37763
|
} catch (err2) {
|
|
37650
|
-
sink.abandoned(
|
|
37764
|
+
sink.abandoned(record3, describe2(err2));
|
|
37651
37765
|
throw err2;
|
|
37652
37766
|
}
|
|
37653
37767
|
}
|
|
@@ -37671,28 +37785,28 @@ function createRunHistoryWriter(opts) {
|
|
|
37671
37785
|
opts.warn(`[run-history] onPersisted hook failed for ${id}: ${describe2(err2)}`);
|
|
37672
37786
|
}
|
|
37673
37787
|
};
|
|
37674
|
-
const attemptAll = async (
|
|
37788
|
+
const attemptAll = async (record3, flag2, sink, span) => {
|
|
37675
37789
|
const attempts = RUN_HISTORY_RETRY_DELAYS_MS.length + 1;
|
|
37676
37790
|
const giveUp = async (why) => {
|
|
37677
37791
|
failures++;
|
|
37678
37792
|
try {
|
|
37679
|
-
sink.abandoned(
|
|
37793
|
+
sink.abandoned(record3, why);
|
|
37680
37794
|
} catch (err2) {
|
|
37681
|
-
opts.warn(`[run-history] abandoned hook failed for ${
|
|
37795
|
+
opts.warn(`[run-history] abandoned hook failed for ${record3.id}: ${describe2(err2)}`);
|
|
37682
37796
|
}
|
|
37683
37797
|
if (sink === opts.store || flag2?.superseded) return;
|
|
37684
37798
|
try {
|
|
37685
|
-
await opts.store.put(
|
|
37686
|
-
if (!flag2) persisted(
|
|
37799
|
+
await opts.store.put(record3, span ? { span } : void 0);
|
|
37800
|
+
if (!flag2) persisted(record3.id);
|
|
37687
37801
|
} catch (err2) {
|
|
37688
|
-
opts.warn(`[run-history] ${
|
|
37802
|
+
opts.warn(`[run-history] ${record3.id} store fallback after give-up failed: ${describe2(err2)}`);
|
|
37689
37803
|
}
|
|
37690
37804
|
};
|
|
37691
37805
|
for (let attempt = 1; ; attempt++) {
|
|
37692
37806
|
if (flag2?.superseded) return;
|
|
37693
37807
|
try {
|
|
37694
|
-
await sink.put(
|
|
37695
|
-
if (!flag2) persisted(
|
|
37808
|
+
await sink.put(record3, span ? { span } : void 0);
|
|
37809
|
+
if (!flag2) persisted(record3.id);
|
|
37696
37810
|
return;
|
|
37697
37811
|
} catch (err2) {
|
|
37698
37812
|
if (err2 instanceof RouteMissingError) {
|
|
@@ -37705,12 +37819,12 @@ function createRunHistoryWriter(opts) {
|
|
|
37705
37819
|
return;
|
|
37706
37820
|
}
|
|
37707
37821
|
if (err2 instanceof PermanentStoreError) {
|
|
37708
|
-
opts.warn(`[run-history] ${
|
|
37822
|
+
opts.warn(`[run-history] ${record3.id} not persisted (permanent, not retried): ${describe2(err2)}`);
|
|
37709
37823
|
await giveUp(describe2(err2));
|
|
37710
37824
|
return;
|
|
37711
37825
|
}
|
|
37712
37826
|
if (attempt >= attempts) {
|
|
37713
|
-
opts.warn(`[run-history] ${
|
|
37827
|
+
opts.warn(`[run-history] ${record3.id} not persisted after ${attempts} attempts: ${describe2(err2)}`);
|
|
37714
37828
|
await giveUp(`not persisted after ${attempts} attempts: ${describe2(err2)}`);
|
|
37715
37829
|
return;
|
|
37716
37830
|
}
|
|
@@ -37719,24 +37833,24 @@ function createRunHistoryWriter(opts) {
|
|
|
37719
37833
|
}
|
|
37720
37834
|
};
|
|
37721
37835
|
return {
|
|
37722
|
-
write(
|
|
37836
|
+
write(record3, writeOpts) {
|
|
37723
37837
|
let flag2;
|
|
37724
37838
|
if (writeOpts?.provisional === true) {
|
|
37725
|
-
if (finals.has(
|
|
37726
|
-
flag2 = { id:
|
|
37839
|
+
if (finals.has(record3.id)) return;
|
|
37840
|
+
flag2 = { id: record3.id, superseded: false };
|
|
37727
37841
|
provisionalFlags.add(flag2);
|
|
37728
37842
|
} else {
|
|
37729
|
-
finals.add(
|
|
37730
|
-
for (const f of provisionalFlags) if (f.id ===
|
|
37843
|
+
finals.add(record3.id);
|
|
37844
|
+
for (const f of provisionalFlags) if (f.id === record3.id) f.superseded = true;
|
|
37731
37845
|
}
|
|
37732
37846
|
const sink = writeOpts?.via ?? opts.store;
|
|
37733
|
-
const p = attemptAll(
|
|
37847
|
+
const p = attemptAll(record3, flag2, sink, writeOpts?.span).catch((err2) => {
|
|
37734
37848
|
failures++;
|
|
37735
|
-
opts.warn(`[run-history] ${
|
|
37849
|
+
opts.warn(`[run-history] ${record3.id} writer failed unexpectedly: ${describe2(err2)}`);
|
|
37736
37850
|
try {
|
|
37737
|
-
sink.abandoned(
|
|
37851
|
+
sink.abandoned(record3, `writer failed unexpectedly: ${describe2(err2)}`);
|
|
37738
37852
|
} catch (hookErr) {
|
|
37739
|
-
opts.warn(`[run-history] abandoned hook failed for ${
|
|
37853
|
+
opts.warn(`[run-history] abandoned hook failed for ${record3.id}: ${describe2(hookErr)}`);
|
|
37740
37854
|
}
|
|
37741
37855
|
}).finally(() => {
|
|
37742
37856
|
if (flag2) provisionalFlags.delete(flag2);
|
|
@@ -38099,12 +38213,12 @@ async function closeRestartRow(adopted, restart, why) {
|
|
|
38099
38213
|
async function closeResumedRow(adopted, resume, why, status3 = "interrupted", opts) {
|
|
38100
38214
|
try {
|
|
38101
38215
|
const finishedAt = (opts?.clock ?? systemClock)();
|
|
38102
|
-
const
|
|
38216
|
+
const record3 = {
|
|
38103
38217
|
...reclaimedRunRecord({ row: resume.row, events: resume.events, status: status3, finishedAt }),
|
|
38104
38218
|
...opts?.restarting === true ? { restarting: true, restartUntil: finishedAt + RESTART_CLAIM_GRACE_MS } : {}
|
|
38105
38219
|
};
|
|
38106
|
-
await putOnce(adopted.sink,
|
|
38107
|
-
return
|
|
38220
|
+
await putOnce(adopted.sink, record3);
|
|
38221
|
+
return record3;
|
|
38108
38222
|
} catch (err2) {
|
|
38109
38223
|
console.warn(
|
|
38110
38224
|
`[resume] ${resume.row.runId} could not be closed (${why}): ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
@@ -40521,11 +40635,13 @@ async function ownerOf2(runs, unitsOf, threadKey) {
|
|
|
40521
40635
|
const units = await unitsOf(instanceId).catch(() => []);
|
|
40522
40636
|
const unit = units.find((u) => u.threadKey === threadKey && u.ending === void 0);
|
|
40523
40637
|
if (unit !== void 0) return { kind: "unit", instanceId, unit };
|
|
40524
|
-
const ended = units.
|
|
40638
|
+
const ended = units.filter(
|
|
40525
40639
|
(u) => u.threadKey === threadKey && u.ending !== void 0 && u.ending.kind !== "merged" && u.ending.kind !== "already_landed"
|
|
40526
40640
|
);
|
|
40527
40641
|
const ship = runs.find((r) => r.instanceId === instanceId && r.finished);
|
|
40528
|
-
if (ended
|
|
40642
|
+
if (ended.length === 1 && ship !== void 0) return { kind: "pipeline", instanceId, run: ship, unit: ended[0] };
|
|
40643
|
+
if (ended.length > 1 && ship !== void 0)
|
|
40644
|
+
return { kind: "pipeline_ambiguous", instanceId, run: ship, units: ended };
|
|
40529
40645
|
}
|
|
40530
40646
|
const agent = stickyAgentOf(runs);
|
|
40531
40647
|
if (agent !== void 0) return { kind: "session", agent };
|
|
@@ -40736,8 +40852,9 @@ function ownedProjection(p) {
|
|
|
40736
40852
|
function ownerNote(owner) {
|
|
40737
40853
|
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
40854
|
const steer = owner.kind === "live" && owner.runId !== void 0 ? ` bind \`steer run ${owner.runId} <words>\` to deliver it,` : "";
|
|
40739
|
-
|
|
40740
|
-
|
|
40855
|
+
if (owner.kind === "pipeline")
|
|
40856
|
+
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.`;
|
|
40857
|
+
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
40858
|
}
|
|
40742
40859
|
function operatorProjection(input) {
|
|
40743
40860
|
return {
|
|
@@ -41122,14 +41239,13 @@ function runnableProposal(proposal, ctx) {
|
|
|
41122
41239
|
return (ctx.repositories?.length ?? 0) > 0 || /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+|(?:^|\s)[\w.-]+\/[\w.-]+(?:\s|[:#]|$)/i.test(request2);
|
|
41123
41240
|
}
|
|
41124
41241
|
function ownedDecisionRuns(event, owner, commands) {
|
|
41125
|
-
if (event.outcome !== "binds") return false;
|
|
41242
|
+
if (owner.kind === "pipeline" || event.outcome !== "binds") return false;
|
|
41126
41243
|
return (event.binds ?? []).every((bind) => {
|
|
41127
41244
|
const parsed = commands ? parseChatCommand(bind.line, commands) : null;
|
|
41128
41245
|
if (parsed?.kind !== "invoke") return false;
|
|
41129
41246
|
const def = commands.list().find((c) => c.id === parsed.id);
|
|
41130
41247
|
if (!def) return false;
|
|
41131
|
-
if (def.id === "steer.run")
|
|
41132
|
-
return owner.kind !== "pipeline" && !(owner.kind === "live" && owner.runId === void 0);
|
|
41248
|
+
if (def.id === "steer.run") return !(owner.kind === "live" && owner.runId === void 0);
|
|
41133
41249
|
return boundBlastRadius(def, parsed.input) === "read";
|
|
41134
41250
|
});
|
|
41135
41251
|
}
|
|
@@ -41452,6 +41568,7 @@ async function executeOperatorDecision(deps, ctx) {
|
|
|
41452
41568
|
"verbose"
|
|
41453
41569
|
);
|
|
41454
41570
|
const answered = { kind: "answered" };
|
|
41571
|
+
if (ctx.owner?.kind === "pipeline") return { kind: "fold" };
|
|
41455
41572
|
if (event.outcome === "question") {
|
|
41456
41573
|
await io.reply(event.question ?? "");
|
|
41457
41574
|
await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
|
|
@@ -41811,21 +41928,21 @@ function parseSkillMarkdown(raw) {
|
|
|
41811
41928
|
if (!fm || typeof fm !== "object" || Array.isArray(fm)) {
|
|
41812
41929
|
throw new Error("skill frontmatter is not a YAML mapping");
|
|
41813
41930
|
}
|
|
41814
|
-
const
|
|
41815
|
-
const name =
|
|
41931
|
+
const record3 = fm;
|
|
41932
|
+
const name = record3.name;
|
|
41816
41933
|
if (typeof name !== "string" || name.trim() === "") {
|
|
41817
41934
|
throw new Error("skill frontmatter is missing a non-empty string `name`");
|
|
41818
41935
|
}
|
|
41819
|
-
const description =
|
|
41936
|
+
const description = record3.description;
|
|
41820
41937
|
if (typeof description !== "string" || description.trim() === "") {
|
|
41821
41938
|
throw new Error(`skill "${name}" frontmatter is missing a non-empty string \`description\``);
|
|
41822
41939
|
}
|
|
41823
|
-
const agents =
|
|
41940
|
+
const agents = record3.agents;
|
|
41824
41941
|
if (!Array.isArray(agents) || agents.length === 0 || !agents.every((a) => typeof a === "string" && a.trim() !== "")) {
|
|
41825
41942
|
throw new Error(`skill "${name}" frontmatter needs a non-empty \`agents\` array of strings (e.g. [review])`);
|
|
41826
41943
|
}
|
|
41827
|
-
const source2 = typeof
|
|
41828
|
-
const upstream = parseUpstream(
|
|
41944
|
+
const source2 = typeof record3.source === "string" ? record3.source.trim() : void 0;
|
|
41945
|
+
const upstream = parseUpstream(record3.upstream, name);
|
|
41829
41946
|
return {
|
|
41830
41947
|
name: name.trim(),
|
|
41831
41948
|
description: description.trim(),
|
|
@@ -45038,9 +45155,13 @@ function mapStringLeaves(value, fn) {
|
|
|
45038
45155
|
}
|
|
45039
45156
|
return value;
|
|
45040
45157
|
}
|
|
45158
|
+
function requesterText(text) {
|
|
45159
|
+
return text.trim().replace(/\s+/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/[\\`*_{}[\]()#!|~]/g, "\\$&").replace(/@/g, "@");
|
|
45160
|
+
}
|
|
45041
45161
|
function requestedByLine(by) {
|
|
45042
|
-
const steered = by.steeredBy !== void 0 && by.steeredBy.length > 0 ? `; steered by ${by.steeredBy.join(", ")}` : "";
|
|
45043
|
-
|
|
45162
|
+
const steered = by.steeredBy !== void 0 && by.steeredBy.length > 0 ? `; steered by ${by.steeredBy.map(requesterText).join(", ")}` : "";
|
|
45163
|
+
const thread = by.threadUrl ? ` \xB7 [Thread](${by.threadUrl})` : "";
|
|
45164
|
+
return `Requested by **${requesterText(by.name) || "Unknown requester"}**${thread}${steered}`;
|
|
45044
45165
|
}
|
|
45045
45166
|
function encodeGithubPathSegments(path) {
|
|
45046
45167
|
return path.split("/").map(encodeURIComponent).join("/");
|
|
@@ -45063,6 +45184,7 @@ function renderPrDescriptionMarkdown(desc, ctx) {
|
|
|
45063
45184
|
if (!SHA_RE2.test(ctx.headSha))
|
|
45064
45185
|
throw new Error(`renderPrDescriptionMarkdown: headSha must be a full 40-char lowercase sha, got "${ctx.headSha}"`);
|
|
45065
45186
|
const out = [];
|
|
45187
|
+
if (ctx.requestedBy) out.push(requestedByLine(ctx.requestedBy), "");
|
|
45066
45188
|
out.push(desc.tldr, "");
|
|
45067
45189
|
out.push(`${MAP_LABELS.why} ${desc.why}`, "");
|
|
45068
45190
|
out.push(MAP_LABELS.whereToLook, "");
|
|
@@ -45089,10 +45211,7 @@ function renderPrDescriptionMarkdown(desc, ctx) {
|
|
|
45089
45211
|
...desc.validation.criteria.map((c) => `| ${cell2(c.criterion)} | ${cell2(c.proof)} |`)
|
|
45090
45212
|
])
|
|
45091
45213
|
);
|
|
45092
|
-
const agentLines = [
|
|
45093
|
-
...desc.agentNotes ? [desc.agentNotes] : [],
|
|
45094
|
-
...ctx.requestedBy ? [requestedByLine(ctx.requestedBy)] : []
|
|
45095
|
-
];
|
|
45214
|
+
const agentLines = desc.agentNotes ? [desc.agentNotes] : [];
|
|
45096
45215
|
if (agentLines.length > 0) out.push(...details(FOLD_SUMMARIES.agents, agentLines));
|
|
45097
45216
|
out.push(GENERATED_FOOTER, "");
|
|
45098
45217
|
return out.join("\n");
|
|
@@ -45100,6 +45219,8 @@ function renderPrDescriptionMarkdown(desc, ctx) {
|
|
|
45100
45219
|
function parsePrDescriptionMarkdown(body) {
|
|
45101
45220
|
const problems = [];
|
|
45102
45221
|
const lines = body.replace(/\r\n?/g, "\n").split("\n").filter((l) => l.trim() !== GENERATED_FOOTER);
|
|
45222
|
+
if (/^Requested by \*\*(?:\\.|[^*\\])+\*\*(?: · \[Thread\]\([^\s)]+\))?(?:; steered by .*)?$/.test(lines[0] ?? ""))
|
|
45223
|
+
lines.shift();
|
|
45103
45224
|
const fenced = fenceMask(lines);
|
|
45104
45225
|
const description = { pointers: [] };
|
|
45105
45226
|
if (lines.some((l, i) => !fenced[i] && /^##\s+Tour\s*$/i.test(l))) {
|
|
@@ -46300,12 +46421,12 @@ var init_contract4 = __esm({
|
|
|
46300
46421
|
};
|
|
46301
46422
|
RELAUNCH_CEILING = 2;
|
|
46302
46423
|
HarnessContainerReplacedError = class extends HarnessInterruptedError {
|
|
46303
|
-
constructor(message4, said2, was, now,
|
|
46424
|
+
constructor(message4, said2, was, now, record3, condition = "word") {
|
|
46304
46425
|
super(message4, "container replaced under the run", "container_replaced");
|
|
46305
46426
|
this.said = said2;
|
|
46306
46427
|
this.was = was;
|
|
46307
46428
|
this.now = now;
|
|
46308
|
-
this.record =
|
|
46429
|
+
this.record = record3;
|
|
46309
46430
|
this.condition = condition;
|
|
46310
46431
|
this.name = "HarnessContainerReplacedError";
|
|
46311
46432
|
if (condition !== "identity" !== (said2 !== void 0))
|
|
@@ -46392,11 +46513,11 @@ async function prepareRelaunch(deps, ctx) {
|
|
|
46392
46513
|
} else {
|
|
46393
46514
|
ctx.saveFacts(written);
|
|
46394
46515
|
}
|
|
46395
|
-
const { deadline, ...
|
|
46516
|
+
const { deadline, ...record3 } = replaced.record;
|
|
46396
46517
|
return {
|
|
46397
46518
|
kind: "relaunch",
|
|
46398
46519
|
resume: {
|
|
46399
|
-
...
|
|
46520
|
+
...record3,
|
|
46400
46521
|
remainingMs: Math.max(0, deadline - ctx.clock()),
|
|
46401
46522
|
facts: written,
|
|
46402
46523
|
relaunch: {
|
|
@@ -47559,7 +47680,7 @@ var init_runBearers = __esm({
|
|
|
47559
47680
|
* after `record` returned would carry the old hash ahead of the new one.
|
|
47560
47681
|
* Refuses by name the runs `issue` mints nothing for, and hands `record`
|
|
47561
47682
|
* nothing then. */
|
|
47562
|
-
rotate(runId3,
|
|
47683
|
+
rotate(runId3, record3) {
|
|
47563
47684
|
const entry = this.entries.get(runId3);
|
|
47564
47685
|
if (!entry) return { ok: false, reason: "unknown_run" };
|
|
47565
47686
|
if (entry.revoked) return { ok: false, reason: "revoked" };
|
|
@@ -47567,7 +47688,7 @@ var init_runBearers = __esm({
|
|
|
47567
47688
|
const secret2 = randomBytes4(SECRET_BYTES);
|
|
47568
47689
|
const hash = hashOf(secret2);
|
|
47569
47690
|
entry.hashes.push(hash);
|
|
47570
|
-
|
|
47691
|
+
record3(hash.toString("hex"));
|
|
47571
47692
|
entry.hashes = [hash];
|
|
47572
47693
|
return { ok: true, token: token(runId3, secret2), expiresAt: entry.grant.expiresAt };
|
|
47573
47694
|
}
|
|
@@ -49370,6 +49491,82 @@ var init_process = __esm({
|
|
|
49370
49491
|
}
|
|
49371
49492
|
});
|
|
49372
49493
|
|
|
49494
|
+
// ../../src/core/providerToolSchemas.ts
|
|
49495
|
+
function responsesRejectsPattern(pattern) {
|
|
49496
|
+
return /\(\?(?:[=!]|<[=!])/.test(pattern);
|
|
49497
|
+
}
|
|
49498
|
+
function shapeResponsesSchema(value, tool, degradations) {
|
|
49499
|
+
const schema = record(value);
|
|
49500
|
+
if (!schema) {
|
|
49501
|
+
if (!Array.isArray(value)) return { value, changed: false };
|
|
49502
|
+
let changed2 = false;
|
|
49503
|
+
const items = value.map((item) => {
|
|
49504
|
+
const shaped = shapeResponsesSchema(item, tool, degradations);
|
|
49505
|
+
changed2 ||= shaped.changed;
|
|
49506
|
+
return shaped.value;
|
|
49507
|
+
});
|
|
49508
|
+
return { value: changed2 ? items : value, changed: changed2 };
|
|
49509
|
+
}
|
|
49510
|
+
let changed = false;
|
|
49511
|
+
const out = {};
|
|
49512
|
+
for (const [keyword, item] of Object.entries(schema)) {
|
|
49513
|
+
if (keyword === "pattern" && typeof item === "string" && responsesRejectsPattern(item)) {
|
|
49514
|
+
degradations.push({
|
|
49515
|
+
tool,
|
|
49516
|
+
keyword,
|
|
49517
|
+
why: "the Responses wire does not accept regular-expression lookaround"
|
|
49518
|
+
});
|
|
49519
|
+
changed = true;
|
|
49520
|
+
continue;
|
|
49521
|
+
}
|
|
49522
|
+
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record(item)) {
|
|
49523
|
+
let mapChanged = false;
|
|
49524
|
+
const mapped = {};
|
|
49525
|
+
for (const [name, child] of Object.entries(record(item))) {
|
|
49526
|
+
const shaped = shapeResponsesSchema(child, tool, degradations);
|
|
49527
|
+
mapChanged ||= shaped.changed;
|
|
49528
|
+
mapped[name] = shaped.value;
|
|
49529
|
+
}
|
|
49530
|
+
out[keyword] = mapChanged ? mapped : item;
|
|
49531
|
+
changed ||= mapChanged;
|
|
49532
|
+
continue;
|
|
49533
|
+
}
|
|
49534
|
+
if (["additionalProperties", "allOf", "anyOf", "if", "items", "not", "oneOf", "then", "else"].includes(keyword) && typeof item === "object" && item !== null) {
|
|
49535
|
+
const shaped = shapeResponsesSchema(item, tool, degradations);
|
|
49536
|
+
out[keyword] = shaped.value;
|
|
49537
|
+
changed ||= shaped.changed;
|
|
49538
|
+
continue;
|
|
49539
|
+
}
|
|
49540
|
+
out[keyword] = item;
|
|
49541
|
+
}
|
|
49542
|
+
return { value: changed ? out : value, changed };
|
|
49543
|
+
}
|
|
49544
|
+
function shapeToolSchemasForWire(shape, body) {
|
|
49545
|
+
if (shape !== "openai-responses" || !Array.isArray(body.tools)) return { body, degradations: [] };
|
|
49546
|
+
const degradations = [];
|
|
49547
|
+
let changed = false;
|
|
49548
|
+
const tools = body.tools.map((item) => {
|
|
49549
|
+
const tool = record(item);
|
|
49550
|
+
if (!tool) return item;
|
|
49551
|
+
const name = typeof tool.name === "string" ? tool.name : "unnamed";
|
|
49552
|
+
const shaped = shapeResponsesSchema(tool.parameters, name, degradations);
|
|
49553
|
+
if (!shaped.changed) return item;
|
|
49554
|
+
changed = true;
|
|
49555
|
+
return { ...tool, parameters: shaped.value };
|
|
49556
|
+
});
|
|
49557
|
+
const unique = degradations.filter(
|
|
49558
|
+
(candidate, index, all) => all.findIndex((other) => other.tool === candidate.tool && other.keyword === candidate.keyword) === index
|
|
49559
|
+
);
|
|
49560
|
+
return { body: changed ? { ...body, tools } : body, degradations: unique };
|
|
49561
|
+
}
|
|
49562
|
+
var record;
|
|
49563
|
+
var init_providerToolSchemas = __esm({
|
|
49564
|
+
"../../src/core/providerToolSchemas.ts"() {
|
|
49565
|
+
"use strict";
|
|
49566
|
+
record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
49567
|
+
}
|
|
49568
|
+
});
|
|
49569
|
+
|
|
49373
49570
|
// ../../src/core/harness/piAi.ts
|
|
49374
49571
|
import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
|
|
49375
49572
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
@@ -49537,6 +49734,7 @@ var init_piAi = __esm({
|
|
|
49537
49734
|
"../../src/core/harness/piAi.ts"() {
|
|
49538
49735
|
"use strict";
|
|
49539
49736
|
init_provider();
|
|
49737
|
+
init_providerToolSchemas();
|
|
49540
49738
|
init_secrets2();
|
|
49541
49739
|
init_clock();
|
|
49542
49740
|
ANTHROPIC_BASE_URL = "https://api.anthropic.com";
|
|
@@ -49624,7 +49822,10 @@ var init_piAi = __esm({
|
|
|
49624
49822
|
...this.fetchImpl ? { fetch: this.fetchImpl } : {}
|
|
49625
49823
|
};
|
|
49626
49824
|
try {
|
|
49627
|
-
const
|
|
49825
|
+
const context = toPiContext(req, model, this.clock);
|
|
49826
|
+
const wire = this.api === "openai-completions" ? "openai-chat" : this.api;
|
|
49827
|
+
const shaped = shapeToolSchemasForWire(wire, context);
|
|
49828
|
+
const message4 = await this.api$().stream(model, shaped.body, options).result();
|
|
49628
49829
|
return fromPiMessage(message4, this.name, req.tools);
|
|
49629
49830
|
} catch (err2) {
|
|
49630
49831
|
const failure2 = providerFailureOf(err2);
|
|
@@ -51263,8 +51464,8 @@ ${stagedLine}` : prompt;
|
|
|
51263
51464
|
return;
|
|
51264
51465
|
}
|
|
51265
51466
|
const reissued = inputs.filter((item) => item.userId === PLANE_ACTOR_ID && isReissueSteerText(item.text));
|
|
51266
|
-
const
|
|
51267
|
-
if (
|
|
51467
|
+
const waiting3 = reissued.length === 0 ? inputs : inputs.filter((item) => !reissued.includes(item));
|
|
51468
|
+
if (waiting3.length > 0) run2.inbox?.requeue(waiting3);
|
|
51268
51469
|
for (const item of reissued) {
|
|
51269
51470
|
emit({
|
|
51270
51471
|
type: "input",
|
|
@@ -51575,13 +51776,13 @@ var init_harness = __esm({
|
|
|
51575
51776
|
}
|
|
51576
51777
|
};
|
|
51577
51778
|
PiContainerReplacedError = class extends HarnessContainerReplacedError {
|
|
51578
|
-
constructor(said2, was, now,
|
|
51779
|
+
constructor(said2, was, now, record3, condition = "word") {
|
|
51579
51780
|
super(
|
|
51580
51781
|
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
51782
|
said2,
|
|
51582
51783
|
was,
|
|
51583
51784
|
now,
|
|
51584
|
-
|
|
51785
|
+
record3,
|
|
51585
51786
|
condition
|
|
51586
51787
|
);
|
|
51587
51788
|
this.name = "PiContainerReplacedError";
|
|
@@ -51792,14 +51993,17 @@ var init_resume = __esm({
|
|
|
51792
51993
|
|
|
51793
51994
|
// ../../src/execution/githubComments.ts
|
|
51794
51995
|
async function postReviewComment(target, body) {
|
|
51996
|
+
const codePoints = [...body].length;
|
|
51997
|
+
if (codePoints > MAX_REVIEW_POST_CODE_POINTS) {
|
|
51998
|
+
throw new Error(
|
|
51999
|
+
`PR review body is ${codePoints} Unicode code points, over the ${MAX_REVIEW_POST_CODE_POINTS} limit; refusing to clip the structured verdict`
|
|
52000
|
+
);
|
|
52001
|
+
}
|
|
51795
52002
|
const token2 = await resolveGithubToken();
|
|
51796
52003
|
if (!token2) {
|
|
51797
52004
|
throw new Error("no GitHub credential available to post the PR review comment");
|
|
51798
52005
|
}
|
|
51799
|
-
const
|
|
51800
|
-
|
|
51801
|
-
_(review truncated to fit GitHub's comment size limit)_` : body;
|
|
51802
|
-
const payload2 = { event: "COMMENT", body: clipped };
|
|
52006
|
+
const payload2 = { event: "COMMENT", body };
|
|
51803
52007
|
if (target.commitId) payload2.commit_id = target.commitId;
|
|
51804
52008
|
const res = await fetch(`https://api.github.com/repos/${target.repo}/pulls/${target.number}/reviews`, {
|
|
51805
52009
|
method: "POST",
|
|
@@ -51817,13 +52021,12 @@ _(review truncated to fit GitHub's comment size limit)_` : body;
|
|
|
51817
52021
|
throw new Error(`PR comment post failed: HTTP ${res.status} ${redactAndCap(text, 300)}`);
|
|
51818
52022
|
}
|
|
51819
52023
|
}
|
|
51820
|
-
var MAX_COMMENT_CHARS;
|
|
51821
52024
|
var init_githubComments = __esm({
|
|
51822
52025
|
"../../src/execution/githubComments.ts"() {
|
|
51823
52026
|
"use strict";
|
|
51824
52027
|
init_githubApp();
|
|
51825
52028
|
init_redact();
|
|
51826
|
-
|
|
52029
|
+
init_reviewVerdict();
|
|
51827
52030
|
}
|
|
51828
52031
|
});
|
|
51829
52032
|
|
|
@@ -51875,13 +52078,13 @@ function parsedPrDescriptionArtifact(facts2, ctx) {
|
|
|
51875
52078
|
async function findSubmittedPrDescription(store, key, limit = SUBMITTED_LOOKUP_LIMIT) {
|
|
51876
52079
|
const rows = await store.list({ limit, visibleTo: { kind: "repos-in", repos: [key.repo] } });
|
|
51877
52080
|
for (const row2 of rows) {
|
|
51878
|
-
const
|
|
51879
|
-
if (!
|
|
51880
|
-
for (let i =
|
|
51881
|
-
const ev =
|
|
52081
|
+
const record3 = await store.get(row2.id);
|
|
52082
|
+
if (!record3) continue;
|
|
52083
|
+
for (let i = record3.events.length - 1; i >= 0; i--) {
|
|
52084
|
+
const ev = record3.events[i];
|
|
51882
52085
|
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
52086
|
const { type: _type, seq: _seq, at: _at, ...artifact } = ev;
|
|
51884
|
-
return { artifact: { ...artifact, fromRunId: artifact.fromRunId ??
|
|
52087
|
+
return { artifact: { ...artifact, fromRunId: artifact.fromRunId ?? record3.id }, runId: record3.id };
|
|
51885
52088
|
}
|
|
51886
52089
|
}
|
|
51887
52090
|
}
|
|
@@ -52144,7 +52347,7 @@ async function runCodingPrPostStep(input) {
|
|
|
52144
52347
|
});
|
|
52145
52348
|
}
|
|
52146
52349
|
const ownPr = target.ownPr;
|
|
52147
|
-
const requestedBy = input.
|
|
52350
|
+
const requestedBy = input.requestedBy;
|
|
52148
52351
|
const onOwnPrBranch = ownPr?.headBranch !== void 0 && branch2 === ownPr.headBranch;
|
|
52149
52352
|
const pushedPastOwnPr = ownPr !== void 0 && onOwnPrBranch && pushed && headSha !== void 0 && !sameCommit(headSha, ownPr.headSha) ? headSha : void 0;
|
|
52150
52353
|
if (prDescription && ownPr !== void 0 && ownPr.state !== "open" && pushedPastOwnPr !== void 0 && branch2 !== void 0) {
|
|
@@ -52355,16 +52558,17 @@ async function runCodingPrPostStep(input) {
|
|
|
52355
52558
|
);
|
|
52356
52559
|
}
|
|
52357
52560
|
}
|
|
52358
|
-
|
|
52359
|
-
|
|
52561
|
+
const requestedLogin = input.identity.requestedLogin;
|
|
52562
|
+
if (requestedLogin !== void 0) {
|
|
52563
|
+
const assignable = await input.identity.isAssignable(repo, requestedLogin).catch(() => void 0);
|
|
52360
52564
|
if (assignable === true)
|
|
52361
|
-
await input.identity.addAssignee(repo, opened.number,
|
|
52565
|
+
await input.identity.addAssignee(repo, opened.number, requestedLogin).catch(
|
|
52362
52566
|
(err2) => console.error(
|
|
52363
|
-
`[pr-post] ${logKey} assignee add failed for ${
|
|
52567
|
+
`[pr-post] ${logKey} assignee add failed for ${requestedLogin} on ${repo}#${opened.number}: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
52364
52568
|
)
|
|
52365
52569
|
);
|
|
52366
52570
|
else if (assignable === false)
|
|
52367
|
-
console.log(`[identity] ${logKey} ${
|
|
52571
|
+
console.log(`[identity] ${logKey} ${requestedLogin} is not assignable on ${repo}; skipped`);
|
|
52368
52572
|
}
|
|
52369
52573
|
}
|
|
52370
52574
|
console.log(
|
|
@@ -53175,9 +53379,9 @@ async function runLoop(deps, ctx) {
|
|
|
53175
53379
|
throw new Error(
|
|
53176
53380
|
`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
53381
|
);
|
|
53178
|
-
const
|
|
53382
|
+
const judge5 = harnessOf(deps.harness);
|
|
53179
53383
|
const container = deps.harness.containerFor?.(executor, profile.machine) ?? harnessContainerFor(executor, profile.machine);
|
|
53180
|
-
const found = await
|
|
53384
|
+
const found = await judge5.find(facts2, container).catch((err2) => {
|
|
53181
53385
|
if (!isContainerGone(err2)) throw err2;
|
|
53182
53386
|
return "gone";
|
|
53183
53387
|
});
|
|
@@ -53202,11 +53406,11 @@ async function runLoop(deps, ctx) {
|
|
|
53202
53406
|
onEvent({
|
|
53203
53407
|
type: "run_note",
|
|
53204
53408
|
kind: "resumed",
|
|
53205
|
-
summary: `the run's row carries ${facts2.harness} harness facts (pid ${facts2.pid}) that the ${
|
|
53409
|
+
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
53410
|
});
|
|
53207
53411
|
break;
|
|
53208
53412
|
default:
|
|
53209
|
-
await
|
|
53413
|
+
await judge5.end(facts2, container);
|
|
53210
53414
|
}
|
|
53211
53415
|
} else noteUnknownWord("the run finished on the answer it already had");
|
|
53212
53416
|
windDownEnding = windDownEndingUnder(finish2.answer, loopEnding);
|
|
@@ -53559,7 +53763,9 @@ async function runLoop(deps, ctx) {
|
|
|
53559
53763
|
});
|
|
53560
53764
|
}
|
|
53561
53765
|
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted) {
|
|
53562
|
-
const
|
|
53766
|
+
const instance = coordinator && deps.coordinatorInstances ? await deps.coordinatorInstances.get(coordinator.parentInstanceId).catch(() => void 0) : void 0;
|
|
53767
|
+
const requester = instance?.userId === msg.userId ? instance : msg;
|
|
53768
|
+
const requestedLogin = identitySeam !== void 0 ? await identitySeam.requestedLogin(requester.userId).catch(() => void 0) : void 0;
|
|
53563
53769
|
prNote = await root.span(
|
|
53564
53770
|
"run.pr_post_step",
|
|
53565
53771
|
() => runCodingPrPostStep({
|
|
@@ -53572,6 +53778,10 @@ async function runLoop(deps, ctx) {
|
|
|
53572
53778
|
remoteRepo: observedRemoteRepo
|
|
53573
53779
|
},
|
|
53574
53780
|
description: prDescription,
|
|
53781
|
+
requestedBy: {
|
|
53782
|
+
name: requester.userName?.trim() || requestedLogin || requester.userId,
|
|
53783
|
+
threadUrl: threadPageLink(requester.threadKey)
|
|
53784
|
+
},
|
|
53575
53785
|
target: prTarget,
|
|
53576
53786
|
openPullRequest: deps.openPullRequest ?? openPullRequest,
|
|
53577
53787
|
findOpenPr: deps.findOpenPrByHead ?? findOpenPrByHead,
|
|
@@ -53584,7 +53794,7 @@ async function runLoop(deps, ctx) {
|
|
|
53584
53794
|
pullRequestHead: identitySeam.pullRequestHead,
|
|
53585
53795
|
isAssignable: identitySeam.isAssignable,
|
|
53586
53796
|
addAssignee: identitySeam.addAssignee,
|
|
53587
|
-
...requestedLogin !== void 0 ? {
|
|
53797
|
+
...requestedLogin !== void 0 ? { requestedLogin } : {}
|
|
53588
53798
|
}
|
|
53589
53799
|
} : {},
|
|
53590
53800
|
descriptionTurnRan,
|
|
@@ -54221,7 +54431,7 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
|
|
|
54221
54431
|
p.graph.units.filter((u) => selected.includes(u.id)).map(async (u) => {
|
|
54222
54432
|
const previous = carried?.get(u.id);
|
|
54223
54433
|
const taskKey = p.recordTasks?.[u.id];
|
|
54224
|
-
const
|
|
54434
|
+
const record3 = taskKey !== void 0 ? await reserve(p.identity.repo, taskKey, previous?.record) : previous?.record;
|
|
54225
54435
|
return {
|
|
54226
54436
|
instanceId,
|
|
54227
54437
|
unit: u.id,
|
|
@@ -54232,7 +54442,7 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
|
|
|
54232
54442
|
rounds: [],
|
|
54233
54443
|
...p.resume !== void 0 ? { resume: p.resume } : {},
|
|
54234
54444
|
...previous?.lastPush !== void 0 ? { lastPush: previous.lastPush } : {},
|
|
54235
|
-
...
|
|
54445
|
+
...record3 !== void 0 ? { record: record3 } : {}
|
|
54236
54446
|
};
|
|
54237
54447
|
})
|
|
54238
54448
|
);
|
|
@@ -54648,7 +54858,11 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
54648
54858
|
return { hostedLive: false };
|
|
54649
54859
|
}
|
|
54650
54860
|
const entry = pre.entry;
|
|
54651
|
-
const
|
|
54861
|
+
const configuredCaps = resolveShipCaps(deps.config.config.ship);
|
|
54862
|
+
const caps = {
|
|
54863
|
+
maxRounds: Math.min(configuredCaps.maxRounds, ctx.reissueCaps?.maxRounds ?? configuredCaps.maxRounds),
|
|
54864
|
+
maxMinutes: Math.min(profile.minutes, ctx.reissueCaps?.maxMinutes ?? profile.minutes)
|
|
54865
|
+
};
|
|
54652
54866
|
const held2 = fit(caps);
|
|
54653
54867
|
if (!held2.ok) {
|
|
54654
54868
|
const reason2 = `budget ${caps.maxMinutes} min cannot hold the ship loop (${caps.maxRounds} review rounds need ${held2.need} min)`;
|
|
@@ -55359,41 +55573,41 @@ function prName(pr) {
|
|
|
55359
55573
|
const m = GITHUB_PULL_URL.exec(pr.url);
|
|
55360
55574
|
return m ? `${m[1]}#${pr.number} (${pr.url})` : `pull request ${pr.number} at ${pr.url}`;
|
|
55361
55575
|
}
|
|
55362
|
-
function runArtifacts(
|
|
55576
|
+
function runArtifacts(record3) {
|
|
55363
55577
|
const lines = [];
|
|
55364
|
-
if (
|
|
55365
|
-
const head =
|
|
55366
|
-
lines.push(`- pull request opened or edited: ${prName(
|
|
55578
|
+
if (record3.pr) {
|
|
55579
|
+
const head = record3.pr.head ? `, head branch ${record3.pr.head}` : "";
|
|
55580
|
+
lines.push(`- pull request opened or edited: ${prName(record3.pr)}${head}`);
|
|
55367
55581
|
}
|
|
55368
|
-
if (
|
|
55369
|
-
lines.push(`- verdict: ${
|
|
55370
|
-
const findings =
|
|
55582
|
+
if (record3.verdict) {
|
|
55583
|
+
lines.push(`- verdict: ${record3.verdict.verdict} \u2014 ${record3.verdict.summary}`);
|
|
55584
|
+
const findings = record3.verdict.findings ?? [];
|
|
55371
55585
|
lines.push(findings.length ? `- findings:
|
|
55372
55586
|
${indent(findings.map(formatFinding))}` : "- findings: none recorded");
|
|
55373
55587
|
}
|
|
55374
|
-
if (
|
|
55588
|
+
if (record3.dispositions) {
|
|
55375
55589
|
lines.push(
|
|
55376
|
-
|
|
55377
|
-
${indent(
|
|
55590
|
+
record3.dispositions.length ? `- dispositions:
|
|
55591
|
+
${indent(record3.dispositions.map(formatDisposition))}` : "- dispositions: none recorded"
|
|
55378
55592
|
);
|
|
55379
55593
|
}
|
|
55380
|
-
if (
|
|
55381
|
-
const entries = handoffLines(
|
|
55594
|
+
if (record3.handoff) {
|
|
55595
|
+
const entries = handoffLines(record3.handoff);
|
|
55382
55596
|
lines.push(
|
|
55383
55597
|
entries.length ? `- handoff:
|
|
55384
55598
|
${indent(entries)}` : "- handoff: nothing to hand off (three empty lists)"
|
|
55385
55599
|
);
|
|
55386
55600
|
}
|
|
55387
|
-
const tldr =
|
|
55601
|
+
const tldr = record3.pr ? tldrOf(record3.events) : void 0;
|
|
55388
55602
|
if (tldr !== void 0) lines.push(`- description TL;DR: ${tldr}`);
|
|
55389
|
-
if (
|
|
55390
|
-
const post =
|
|
55603
|
+
if (record3.reviewPost) {
|
|
55604
|
+
const post = record3.reviewPost;
|
|
55391
55605
|
lines.push(
|
|
55392
55606
|
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
55607
|
);
|
|
55394
55608
|
}
|
|
55395
55609
|
if (lines.length === 0) return void 0;
|
|
55396
|
-
return [`### run ${
|
|
55610
|
+
return [`### run ${record3.id} (${record3.agent ?? "agent unknown"})`, ...lines].join("\n");
|
|
55397
55611
|
}
|
|
55398
55612
|
function threadArtifactsBlock(records, opts) {
|
|
55399
55613
|
const maxRuns = opts.maxRuns ?? THREAD_ARTIFACTS_MAX_RUNS;
|
|
@@ -55654,6 +55868,23 @@ async function answerUnitOwnedThread(deps, ctx) {
|
|
|
55654
55868
|
function activeRunCount() {
|
|
55655
55869
|
return activeRuns;
|
|
55656
55870
|
}
|
|
55871
|
+
function continuationBudgetOf(instance, unit) {
|
|
55872
|
+
if (unit.ending?.kind === "wall_clock_cap" || unit.ending?.kind === "review_pending")
|
|
55873
|
+
return { kind: "exhausted", budget: "wall-clock" };
|
|
55874
|
+
if (unit.ending?.kind === "round_cap") return { kind: "exhausted", budget: "review-round" };
|
|
55875
|
+
const caps = instance.caps;
|
|
55876
|
+
if (caps === void 0 || unit.startedAt === void 0 || unit.ending === void 0 || caps.maxMinutes <= 0 || caps.maxRounds <= 0 || unit.ending.at < unit.startedAt)
|
|
55877
|
+
return { kind: "unavailable" };
|
|
55878
|
+
const remainingMinutes = caps.maxMinutes - (unit.ending.at - unit.startedAt) / MINUTE_MS;
|
|
55879
|
+
if (remainingMinutes <= 0) return { kind: "exhausted", budget: "wall-clock" };
|
|
55880
|
+
const reviewRounds = unit.rounds.reduce(
|
|
55881
|
+
(highest, round2) => round2.agent === "review" ? Math.max(highest, round2.index) : highest,
|
|
55882
|
+
0
|
|
55883
|
+
);
|
|
55884
|
+
const remainingRounds = caps.maxRounds - reviewRounds;
|
|
55885
|
+
if (remainingRounds <= 0) return { kind: "exhausted", budget: "review-round" };
|
|
55886
|
+
return { kind: "remaining", caps: { maxRounds: remainingRounds, maxMinutes: remainingMinutes } };
|
|
55887
|
+
}
|
|
55657
55888
|
async function dispatch(deps, msg, io, opts = {}) {
|
|
55658
55889
|
const ended = { status: "completed" };
|
|
55659
55890
|
const resume = opts.resume;
|
|
@@ -55748,22 +55979,46 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55748
55979
|
try {
|
|
55749
55980
|
const configuredOperator = !resume && !restart && !opts.parent && !opts.coordinator ? operatorModeOf(deps.config.config) : "off";
|
|
55750
55981
|
const typedDecision = parseDirectives(msg.text).agent !== void 0 || deps.commands !== void 0 && parseChatCommand(msg.text, deps.commands) !== null;
|
|
55751
|
-
|
|
55982
|
+
let operatorMode = configuredOperator === "on" && typedDecision ? "off" : configuredOperator;
|
|
55752
55983
|
let operatorEvent;
|
|
55753
55984
|
let operatorPreset;
|
|
55754
55985
|
let operatorRequest;
|
|
55755
55986
|
let operatorModel;
|
|
55756
55987
|
let operatorRepo;
|
|
55757
55988
|
let reissuePlanId;
|
|
55989
|
+
let reissueCaps;
|
|
55758
55990
|
let operatorThread;
|
|
55759
55991
|
let pageOwner;
|
|
55760
55992
|
if (operatorMode !== "off") {
|
|
55761
55993
|
const runsService2 = deps.runs ?? createRunsService({ registry, store: deps.runStore });
|
|
55762
55994
|
operatorThread = opts.thread ?? await readThread(runsService2, msg.threadKey);
|
|
55995
|
+
if (operatorThread !== void 0 && deps.coordinatorInstances !== void 0) {
|
|
55996
|
+
const unitReads = /* @__PURE__ */ new Map();
|
|
55997
|
+
const unitsOf = (id) => {
|
|
55998
|
+
let read = unitReads.get(id);
|
|
55999
|
+
if (read === void 0) {
|
|
56000
|
+
read = deps.coordinatorInstances.listUnits(id);
|
|
56001
|
+
unitReads.set(id, read);
|
|
56002
|
+
}
|
|
56003
|
+
return read;
|
|
56004
|
+
};
|
|
56005
|
+
pageOwner = await ownerOf2(operatorThread, unitsOf, msg.threadKey);
|
|
56006
|
+
if (operatorMode === "on" && parseDirectives(msg.text).agent === void 0) {
|
|
56007
|
+
const continuationOwner = pageOwner.kind === "pipeline" || pageOwner.kind === "pipeline_ambiguous" ? pageOwner : await ownerOf2(
|
|
56008
|
+
operatorThread.filter((run3) => run3.finished),
|
|
56009
|
+
unitsOf,
|
|
56010
|
+
msg.threadKey
|
|
56011
|
+
);
|
|
56012
|
+
if (continuationOwner.kind === "pipeline" || continuationOwner.kind === "pipeline_ambiguous") {
|
|
56013
|
+
pageOwner = continuationOwner;
|
|
56014
|
+
operatorMode = "off";
|
|
56015
|
+
}
|
|
56016
|
+
}
|
|
56017
|
+
}
|
|
56018
|
+
}
|
|
56019
|
+
if (operatorMode !== "off") {
|
|
55763
56020
|
const slot = admission.get(msg.threadKey);
|
|
55764
56021
|
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
56022
|
const pendingQuestion = operatorMode === "on" ? pendingQuestionOf(operatorThread) : void 0;
|
|
55768
56023
|
const joinedAnswer = pendingQuestion !== void 0 && !(pendingQuestion.proposal !== void 0 && isYesAnswer(msg.text)) ? joinedAnswerRequest(pendingQuestion, msg.text) : void 0;
|
|
55769
56024
|
const doorMsg = joinedAnswer !== void 0 ? { ...msg, text: joinedAnswer } : msg;
|
|
@@ -55775,7 +56030,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55775
56030
|
// other, and the fold runs on to the seed refusal below, which
|
|
55776
56031
|
// names the unit threads to reply in.
|
|
55777
56032
|
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;
|
|
56033
|
+
) : 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
56034
|
operatorEvent = await root.span(
|
|
55780
56035
|
"dispatch.operator",
|
|
55781
56036
|
() => operatorStage(deps, {
|
|
@@ -55879,20 +56134,120 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
55879
56134
|
await recordPendingOperator();
|
|
55880
56135
|
return ended;
|
|
55881
56136
|
}
|
|
56137
|
+
if (owner.kind === "pipeline_ambiguous" && directives.agent === void 0) {
|
|
56138
|
+
await io.reply(
|
|
56139
|
+
`This thread matches multiple ended plan units (${owner.units.map((unit) => unit.unit).join(", ")}), so continuation is ambiguous. Nothing started.`
|
|
56140
|
+
);
|
|
56141
|
+
await recordPendingOperator();
|
|
56142
|
+
return ended;
|
|
56143
|
+
}
|
|
55882
56144
|
if (owner.kind === "pipeline" && directives.agent === void 0) {
|
|
56145
|
+
const instance = await deps.coordinatorInstances.get(owner.instanceId).catch(() => null);
|
|
56146
|
+
if (instance === null) {
|
|
56147
|
+
await io.reply(
|
|
56148
|
+
`The ended pipeline's record \`${owner.instanceId}\` is unavailable, so continuation did not start.`
|
|
56149
|
+
);
|
|
56150
|
+
await recordPendingOperator();
|
|
56151
|
+
return ended;
|
|
56152
|
+
}
|
|
56153
|
+
const actor = chatActorOf(deps.config, msg);
|
|
56154
|
+
if (authorizeSteerOwner({
|
|
56155
|
+
caller: { ids: actorIdsOf(actor), grants: effectiveGrants(actor) },
|
|
56156
|
+
target: { runId: owner.run.id, requesterId: instance.userId }
|
|
56157
|
+
}).kind === "refused") {
|
|
56158
|
+
await io.reply(STEER_OWNER_REFUSED);
|
|
56159
|
+
await recordPendingOperator();
|
|
56160
|
+
return ended;
|
|
56161
|
+
}
|
|
56162
|
+
if (instance.threadKey !== msg.threadKey || instance.plan === void 0 || instance.plan.path !== void 0) {
|
|
56163
|
+
await io.reply(
|
|
56164
|
+
`The ended pipeline's durable generated-task identity does not match this thread, so continuation did not start.`
|
|
56165
|
+
);
|
|
56166
|
+
await recordPendingOperator();
|
|
56167
|
+
return ended;
|
|
56168
|
+
}
|
|
56169
|
+
if (instance.repo !== owner.run.repo || instance.branch !== owner.unit.branch) {
|
|
56170
|
+
await io.reply(
|
|
56171
|
+
`The ended pipeline's repository or branch identity no longer matches unit ${owner.unit.unit}, so continuation did not start.`
|
|
56172
|
+
);
|
|
56173
|
+
await recordPendingOperator();
|
|
56174
|
+
return ended;
|
|
56175
|
+
}
|
|
56176
|
+
const continuationBudget = continuationBudgetOf(instance, owner.unit);
|
|
56177
|
+
if (continuationBudget.kind === "unavailable") {
|
|
56178
|
+
await io.reply(
|
|
56179
|
+
`Unit ${owner.unit.unit} does not retain a complete durable wall-clock and review-round budget, so continuation did not start. Nothing else ran.`
|
|
56180
|
+
);
|
|
56181
|
+
await recordPendingOperator();
|
|
56182
|
+
return ended;
|
|
56183
|
+
}
|
|
56184
|
+
if (continuationBudget.kind === "exhausted") {
|
|
56185
|
+
await io.reply(
|
|
56186
|
+
`Unit ${owner.unit.unit} exhausted the ended pipeline's ${continuationBudget.budget} budget, so continuation did not start. Nothing else ran.`
|
|
56187
|
+
);
|
|
56188
|
+
await recordPendingOperator();
|
|
56189
|
+
return ended;
|
|
56190
|
+
}
|
|
55883
56191
|
const original = await shipRequestOf(runsService, owner.run.id);
|
|
55884
56192
|
if (original === void 0) {
|
|
55885
56193
|
await io.reply(
|
|
55886
|
-
|
|
56194
|
+
`The ended pipeline's original task is unavailable from run \`${owner.run.id}\`, so continuation did not start.`
|
|
55887
56195
|
);
|
|
55888
56196
|
await recordPendingOperator();
|
|
55889
56197
|
return ended;
|
|
55890
56198
|
}
|
|
55891
|
-
const
|
|
55892
|
-
if (
|
|
55893
|
-
|
|
56199
|
+
const recordedPr = owner.unit.pr;
|
|
56200
|
+
if (recordedPr === void 0) {
|
|
56201
|
+
await io.reply(
|
|
56202
|
+
`Unit ${owner.unit.unit} does not retain a durable pull request identity, so continuation did not start. Nothing else ran.`
|
|
56203
|
+
);
|
|
56204
|
+
await recordPendingOperator();
|
|
56205
|
+
return ended;
|
|
56206
|
+
}
|
|
56207
|
+
const expectedHead = owner.unit.lastPush;
|
|
56208
|
+
if (expectedHead === void 0) {
|
|
56209
|
+
await io.reply(
|
|
56210
|
+
`Unit ${owner.unit.unit} does not retain the pull request's durable expected head, so continuation did not start. Nothing else ran.`
|
|
56211
|
+
);
|
|
56212
|
+
await recordPendingOperator();
|
|
56213
|
+
return ended;
|
|
56214
|
+
}
|
|
56215
|
+
const target = { repo: instance.repo, number: recordedPr.number };
|
|
56216
|
+
const facts2 = await (deps.fetchPrFacts ?? fetchPullRequestFacts)(target).catch(() => void 0);
|
|
56217
|
+
if (facts2 === void 0) {
|
|
56218
|
+
await io.reply(
|
|
56219
|
+
`GitHub did not return current facts for ${instance.repo}#${recordedPr.number}, so continuation did not start.`
|
|
56220
|
+
);
|
|
56221
|
+
await recordPendingOperator();
|
|
56222
|
+
return ended;
|
|
56223
|
+
}
|
|
56224
|
+
if (facts2.state !== "open") {
|
|
56225
|
+
await io.reply(
|
|
56226
|
+
`${instance.repo}#${recordedPr.number} is closed, so this ended pipeline has no open pull request to continue. Nothing started.`
|
|
56227
|
+
);
|
|
56228
|
+
await recordPendingOperator();
|
|
56229
|
+
return ended;
|
|
56230
|
+
}
|
|
56231
|
+
const verifiedHead = facts2.verifiedHead;
|
|
56232
|
+
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) {
|
|
56233
|
+
await io.reply(
|
|
56234
|
+
`${instance.repo}#${recordedPr.number} no longer has the pipeline's verifiable \`${owner.unit.branch}\` head, so continuation did not start.`
|
|
56235
|
+
);
|
|
56236
|
+
await recordPendingOperator();
|
|
56237
|
+
return ended;
|
|
56238
|
+
}
|
|
56239
|
+
if (verifiedHead.sha !== expectedHead) {
|
|
56240
|
+
await io.reply(
|
|
56241
|
+
`${instance.repo}#${recordedPr.number} moved from the pipeline's expected head \`${expectedHead}\` to \`${verifiedHead.sha}\`, so continuation did not start. Nothing else ran.`
|
|
56242
|
+
);
|
|
56243
|
+
await recordPendingOperator();
|
|
56244
|
+
return ended;
|
|
56245
|
+
}
|
|
56246
|
+
reissuePlanId = instance.plan.id;
|
|
56247
|
+
reissueCaps = continuationBudget.caps;
|
|
56248
|
+
const durableRequest = parseDirectives(original);
|
|
56249
|
+
Object.assign(directives, durableRequest, { agent: void 0, budget: reissueCaps.maxMinutes });
|
|
55894
56250
|
operatorPreset = "ship";
|
|
55895
|
-
operatorRequest = original;
|
|
55896
56251
|
settled = resolveCurrent();
|
|
55897
56252
|
({ sticky, resolved, agentSource } = settled);
|
|
55898
56253
|
agentSource = "sticky";
|
|
@@ -56118,6 +56473,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
56118
56473
|
card,
|
|
56119
56474
|
directives,
|
|
56120
56475
|
...reissuePlanId !== void 0 ? { reissuePlanId } : {},
|
|
56476
|
+
...reissueCaps !== void 0 ? { reissueCaps: { ...reissueCaps, maxMinutes: Math.min(reissueCaps.maxMinutes, profile.minutes) } } : {},
|
|
56121
56477
|
sticky,
|
|
56122
56478
|
history,
|
|
56123
56479
|
repoCtx,
|
|
@@ -57046,6 +57402,7 @@ var init_dispatcher = __esm({
|
|
|
57046
57402
|
init_route();
|
|
57047
57403
|
init_operator();
|
|
57048
57404
|
init_authorize2();
|
|
57405
|
+
init_authorize();
|
|
57049
57406
|
init_messages();
|
|
57050
57407
|
init_staging();
|
|
57051
57408
|
init_runRecord();
|
|
@@ -57064,6 +57421,7 @@ var init_dispatcher = __esm({
|
|
|
57064
57421
|
init_settle();
|
|
57065
57422
|
init_reattach2();
|
|
57066
57423
|
init_factory();
|
|
57424
|
+
init_githubPulls();
|
|
57067
57425
|
init_sandboxErrors();
|
|
57068
57426
|
init_lineage();
|
|
57069
57427
|
init_seed();
|
|
@@ -57424,8 +57782,8 @@ function createLedgerWriteThrough(opts) {
|
|
|
57424
57782
|
settleLanding(this.runId, entry, outcome);
|
|
57425
57783
|
return value;
|
|
57426
57784
|
},
|
|
57427
|
-
abandoned: (
|
|
57428
|
-
if (
|
|
57785
|
+
abandoned: (record3, why) => {
|
|
57786
|
+
if (record3.id !== this.runId) return;
|
|
57429
57787
|
const entry = landing.get(this.runId);
|
|
57430
57788
|
if (entry !== void 0) settleLanding(this.runId, entry, { kind: "failed", why });
|
|
57431
57789
|
}
|
|
@@ -57435,18 +57793,18 @@ function createLedgerWriteThrough(opts) {
|
|
|
57435
57793
|
async land(plain7) {
|
|
57436
57794
|
await this.close();
|
|
57437
57795
|
const session = this.sessionOnRecord();
|
|
57438
|
-
const
|
|
57796
|
+
const record3 = session ? { ...plain7, session } : plain7;
|
|
57439
57797
|
let result;
|
|
57440
57798
|
try {
|
|
57441
|
-
result = await ledger.finish(this.runId, gen,
|
|
57799
|
+
result = await ledger.finish(this.runId, gen, record3);
|
|
57442
57800
|
} catch (err2) {
|
|
57443
57801
|
if (!(err2 instanceof RouteMissingError)) throw err2;
|
|
57444
57802
|
routeMissing();
|
|
57445
|
-
return { value: await fallback.put(
|
|
57803
|
+
return { value: await fallback.put(record3), outcome: { kind: "refused" } };
|
|
57446
57804
|
}
|
|
57447
57805
|
if (result.ok) return { value: result, outcome: { kind: "landed" } };
|
|
57448
57806
|
warn(`[ledger] ${this.threadKey} finish refused (${result.reason}) \u2014 record written to the store directly`);
|
|
57449
|
-
return { value: await fallback.put(
|
|
57807
|
+
return { value: await fallback.put(record3), outcome: { kind: "refused" } };
|
|
57450
57808
|
}
|
|
57451
57809
|
tracked() {
|
|
57452
57810
|
return !this.detached;
|
|
@@ -57516,7 +57874,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
57516
57874
|
}));
|
|
57517
57875
|
if (report2.compaction)
|
|
57518
57876
|
turns.push({ idx: this.rowIndex(report2.firstIdx + report2.turns.length), compaction: report2.compaction });
|
|
57519
|
-
const
|
|
57877
|
+
const record3 = {
|
|
57520
57878
|
step: ++this.stepNo,
|
|
57521
57879
|
seq: this.lastSeq,
|
|
57522
57880
|
turnIndex: report2.firstIdx + turns.length,
|
|
@@ -57527,10 +57885,10 @@ function createLedgerWriteThrough(opts) {
|
|
|
57527
57885
|
turn: report2.turn,
|
|
57528
57886
|
iteration: report2.iteration
|
|
57529
57887
|
};
|
|
57530
|
-
this.factRound =
|
|
57531
|
-
const kept = new Set(
|
|
57888
|
+
this.factRound = record3.step;
|
|
57889
|
+
const kept = new Set(record3.inFlight.map((c) => c.callId));
|
|
57532
57890
|
for (const id of this.factCallStarts.keys()) if (!kept.has(id)) this.factCallStarts.delete(id);
|
|
57533
|
-
const call2 =
|
|
57891
|
+
const call2 = record3.inFlight[0];
|
|
57534
57892
|
this.factInFlight = call2 ? {
|
|
57535
57893
|
callId: call2.callId,
|
|
57536
57894
|
tool: call2.tool,
|
|
@@ -57542,13 +57900,13 @@ function createLedgerWriteThrough(opts) {
|
|
|
57542
57900
|
} : void 0;
|
|
57543
57901
|
for (let attempt = 1; ; attempt++) {
|
|
57544
57902
|
try {
|
|
57545
|
-
const result = await ledger.step(this.runId, gen,
|
|
57546
|
-
if (!result.ok) this.detach(`step ${
|
|
57547
|
-
else this.turnsWritten =
|
|
57903
|
+
const result = await ledger.step(this.runId, gen, record3, turns, this.sessionRow?.key);
|
|
57904
|
+
if (!result.ok) this.detach(`step ${record3.step} refused (${result.reason})`);
|
|
57905
|
+
else this.turnsWritten = record3.turnIndex;
|
|
57548
57906
|
return;
|
|
57549
57907
|
} catch (err2) {
|
|
57550
57908
|
if (err2 instanceof RouteMissingError || err2 instanceof PermanentStoreError || attempt >= 2) {
|
|
57551
|
-
this.detach(`step ${
|
|
57909
|
+
this.detach(`step ${record3.step} failed: ${describe9(err2)}`);
|
|
57552
57910
|
return;
|
|
57553
57911
|
}
|
|
57554
57912
|
await sleep2(RETRY_MS[0]);
|
|
@@ -59376,8 +59734,8 @@ async function launchOpenCode(deps, spec, bearer) {
|
|
|
59376
59734
|
let consumed = 0;
|
|
59377
59735
|
for (const line4 of text.split("\n").slice(0, -1)) {
|
|
59378
59736
|
consumed += Buffer.byteLength(line4, "utf8") + 1;
|
|
59379
|
-
const
|
|
59380
|
-
if (
|
|
59737
|
+
const record3 = parseFeedRecord(line4);
|
|
59738
|
+
if (record3?.feed === "tailer" && record3.note === "connected") {
|
|
59381
59739
|
feedOffset = consumed;
|
|
59382
59740
|
break;
|
|
59383
59741
|
}
|
|
@@ -59457,9 +59815,9 @@ var init_process2 = __esm({
|
|
|
59457
59815
|
});
|
|
59458
59816
|
|
|
59459
59817
|
// ../../src/core/harness/opencode/bridge.ts
|
|
59460
|
-
function executionStartedFor(
|
|
59461
|
-
if (
|
|
59462
|
-
const data =
|
|
59818
|
+
function executionStartedFor(record3, sessionID) {
|
|
59819
|
+
if (record3.feed !== "event" || record3.event.type !== "session.execution.started") return false;
|
|
59820
|
+
const data = record3.event.data;
|
|
59463
59821
|
return data?.sessionID === sessionID;
|
|
59464
59822
|
}
|
|
59465
59823
|
function openCodeToolNameWord(name) {
|
|
@@ -60131,18 +60489,18 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
60131
60489
|
const after = transport.consumedOffset;
|
|
60132
60490
|
const reattachCatchUp = conn.reattach !== void 0 && after <= conn.reattach.catchUpTo;
|
|
60133
60491
|
lastRecord = next.value;
|
|
60134
|
-
const
|
|
60135
|
-
if (!
|
|
60136
|
-
if (!reattachCatchUp && executionStartedFor(
|
|
60492
|
+
const record3 = parseFeedRecord(next.value);
|
|
60493
|
+
if (!record3) continue;
|
|
60494
|
+
if (!reattachCatchUp && executionStartedFor(record3, conn.sessionID)) {
|
|
60137
60495
|
executionOwned = true;
|
|
60138
60496
|
awaiting = void 0;
|
|
60139
60497
|
}
|
|
60140
60498
|
bridge.observing = reattachCatchUp ? "catching-up" : executionOwned ? "own" : "earlier";
|
|
60141
|
-
const obs = bridge.observe(
|
|
60499
|
+
const obs = bridge.observe(record3);
|
|
60142
60500
|
if (!reattachCatchUp && obs.boundary === true) conn.boundaries?.reached();
|
|
60143
60501
|
if (!reattachCatchUp && obs.inboxDelivered !== void 0) conn.inboxFate?.deliver(obs.inboxDelivered);
|
|
60144
60502
|
if (!reattachCatchUp && obs.inboxDropped === true) conn.inboxFate?.interruptAll();
|
|
60145
|
-
if (
|
|
60503
|
+
if (record3.feed === "messages" && conn.saveOffset !== void 0) {
|
|
60146
60504
|
const save = conn.saveOffset;
|
|
60147
60505
|
void bridge.flush().then(() => save(after));
|
|
60148
60506
|
}
|
|
@@ -60229,14 +60587,14 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
60229
60587
|
);
|
|
60230
60588
|
}
|
|
60231
60589
|
if (replacedBy !== void 0) {
|
|
60232
|
-
const
|
|
60590
|
+
const record3 = bridge.record(recordBase, deadline);
|
|
60233
60591
|
bridge.closeOpenSpans((open2) => openCodeReplacedCallNote(open2.tool));
|
|
60234
60592
|
const replaced = replacedBy.condition === "word" ? new OpenCodeContainerReplacedError(
|
|
60235
60593
|
replacedBy.said.message,
|
|
60236
60594
|
conn.containerWord,
|
|
60237
60595
|
await conn.container.identity().catch(() => void 0),
|
|
60238
|
-
|
|
60239
|
-
) : new OpenCodeContainerReplacedError(void 0, replacedBy.was, replacedBy.now,
|
|
60596
|
+
record3
|
|
60597
|
+
) : new OpenCodeContainerReplacedError(void 0, replacedBy.was, replacedBy.now, record3, "identity");
|
|
60240
60598
|
note("sandbox_restarted", replaced.message);
|
|
60241
60599
|
throw replaced;
|
|
60242
60600
|
}
|
|
@@ -60376,13 +60734,13 @@ var init_bridge3 = __esm({
|
|
|
60376
60734
|
diagnostics;
|
|
60377
60735
|
};
|
|
60378
60736
|
OpenCodeContainerReplacedError = class extends HarnessContainerReplacedError {
|
|
60379
|
-
constructor(said2, was, now,
|
|
60737
|
+
constructor(said2, was, now, record3, condition = "word") {
|
|
60380
60738
|
super(
|
|
60381
60739
|
`the container running OpenCode was replaced (${was ?? "unknown"} \u2192 ${now ?? "unknown"}; ${replacedBecause(condition, said2)})`,
|
|
60382
60740
|
said2,
|
|
60383
60741
|
was,
|
|
60384
60742
|
now,
|
|
60385
|
-
|
|
60743
|
+
record3,
|
|
60386
60744
|
condition
|
|
60387
60745
|
);
|
|
60388
60746
|
this.name = "OpenCodeContainerReplacedError";
|
|
@@ -60831,25 +61189,25 @@ var init_bridge3 = __esm({
|
|
|
60831
61189
|
emit(event) {
|
|
60832
61190
|
this.deps.emit(event.at === void 0 ? { ...event, at: this.deps.clock() } : event);
|
|
60833
61191
|
}
|
|
60834
|
-
observe(
|
|
60835
|
-
switch (
|
|
61192
|
+
observe(record3) {
|
|
61193
|
+
switch (record3.feed) {
|
|
60836
61194
|
case "event":
|
|
60837
|
-
return this.onEvent(
|
|
61195
|
+
return this.onEvent(record3.event);
|
|
60838
61196
|
case "permissions":
|
|
60839
61197
|
if (this.observing === "earlier") return { replies: [], settled: false };
|
|
60840
|
-
return this.onPermissionsRefill(
|
|
61198
|
+
return this.onPermissionsRefill(record3.data);
|
|
60841
61199
|
case "messages": {
|
|
60842
|
-
const out = this.onMessagesRefill(
|
|
60843
|
-
if (this.settleOwedTo !== void 0 &&
|
|
61200
|
+
const out = this.onMessagesRefill(record3.data);
|
|
61201
|
+
if (this.settleOwedTo !== void 0 && record3.reason === this.settleOwedTo) {
|
|
60844
61202
|
this.settleOwedTo = void 0;
|
|
60845
61203
|
out.settled = true;
|
|
60846
61204
|
}
|
|
60847
61205
|
return out;
|
|
60848
61206
|
}
|
|
60849
61207
|
case "tailer":
|
|
60850
|
-
if (
|
|
60851
|
-
this.deps.onProgress?.(`opencode feed: ${redactAndCap(
|
|
60852
|
-
if (this.settleOwedTo !== void 0 && (
|
|
61208
|
+
if (record3.note === "stream closed" || record3.note.includes("failed"))
|
|
61209
|
+
this.deps.onProgress?.(`opencode feed: ${redactAndCap(record3.note, 120)}`);
|
|
61210
|
+
if (this.settleOwedTo !== void 0 && (record3.note === "message refill failed" || record3.note === "refill failed") && record3.reason === this.settleOwedTo) {
|
|
60853
61211
|
this.settleOwedTo = void 0;
|
|
60854
61212
|
return { replies: [], settled: true };
|
|
60855
61213
|
}
|
|
@@ -61809,7 +62167,7 @@ ${stagedLine}` : prompt;
|
|
|
61809
62167
|
return "lost";
|
|
61810
62168
|
return "running";
|
|
61811
62169
|
};
|
|
61812
|
-
const resolve13 = async (steerSeq, knownAtSteer, seen,
|
|
62170
|
+
const resolve13 = async (steerSeq, knownAtSteer, seen, waiting3) => {
|
|
61813
62171
|
let left = false;
|
|
61814
62172
|
for (; ; ) {
|
|
61815
62173
|
const verdict = await learnRow(steerSeq, knownAtSteer);
|
|
@@ -61824,7 +62182,7 @@ ${stagedLine}` : prompt;
|
|
|
61824
62182
|
"the follow-up's steer",
|
|
61825
62183
|
"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
62184
|
);
|
|
61827
|
-
|
|
62185
|
+
waiting3();
|
|
61828
62186
|
if (await conn.boundaries.wait(seen) === "left") left = true;
|
|
61829
62187
|
seen = conn.boundaries.count;
|
|
61830
62188
|
}
|
|
@@ -62136,6 +62494,14 @@ var init_requester = __esm({
|
|
|
62136
62494
|
|
|
62137
62495
|
// ../../src/channels/http.ts
|
|
62138
62496
|
import { timingSafeEqual as timingSafeEqual4 } from "node:crypto";
|
|
62497
|
+
function ingressComponentError(name, value) {
|
|
62498
|
+
if (value.length === 0) return `\`${name}\` must be a non-empty string`;
|
|
62499
|
+
if (value.length > INGRESS_COMPONENT_MAX_LENGTH) {
|
|
62500
|
+
return `\`${name}\` must be at most ${INGRESS_COMPONENT_MAX_LENGTH} characters`;
|
|
62501
|
+
}
|
|
62502
|
+
if (/[:#]/.test(value)) return `\`${name}\` must not contain ':' or '#'`;
|
|
62503
|
+
return void 0;
|
|
62504
|
+
}
|
|
62139
62505
|
function safeEqual2(a, b) {
|
|
62140
62506
|
const ab = Buffer.from(a, "utf8");
|
|
62141
62507
|
const bb = Buffer.from(b, "utf8");
|
|
@@ -62179,11 +62545,15 @@ function parseBody3(raw) {
|
|
|
62179
62545
|
if (typeof obj2.text !== "string" || obj2.text.trim() === "") {
|
|
62180
62546
|
return { error: "`text` is required and must be a non-empty string" };
|
|
62181
62547
|
}
|
|
62182
|
-
if (obj2.channel !== void 0
|
|
62183
|
-
return { error: "`channel` must be a string" };
|
|
62548
|
+
if (obj2.channel !== void 0) {
|
|
62549
|
+
if (typeof obj2.channel !== "string") return { error: "`channel` must be a string" };
|
|
62550
|
+
const error = ingressComponentError("channel", obj2.channel);
|
|
62551
|
+
if (error) return { error };
|
|
62184
62552
|
}
|
|
62185
|
-
if (obj2.thread !== void 0
|
|
62186
|
-
return { error: "`thread` must be a string" };
|
|
62553
|
+
if (obj2.thread !== void 0) {
|
|
62554
|
+
if (typeof obj2.thread !== "string") return { error: "`thread` must be a string" };
|
|
62555
|
+
const error = ingressComponentError("thread", obj2.thread);
|
|
62556
|
+
if (error) return { error };
|
|
62187
62557
|
}
|
|
62188
62558
|
if (obj2.async !== void 0 && typeof obj2.async !== "boolean") {
|
|
62189
62559
|
return { error: "`async` must be a boolean" };
|
|
@@ -62236,6 +62606,10 @@ async function handleAuthorized(identity2, body, deps, options) {
|
|
|
62236
62606
|
if ("error" in parsed) {
|
|
62237
62607
|
return { status: 400, body: { error: parsed.error } };
|
|
62238
62608
|
}
|
|
62609
|
+
if (identity2.channel !== void 0) {
|
|
62610
|
+
const error = ingressComponentError("channel", identity2.channel);
|
|
62611
|
+
if (error) return { status: 400, body: { error } };
|
|
62612
|
+
}
|
|
62239
62613
|
const receivedAt = systemClock();
|
|
62240
62614
|
const trace = startRequestRoot(deps, { channel: "http", receivedAt });
|
|
62241
62615
|
const msg = {
|
|
@@ -62316,7 +62690,7 @@ function parseIngressTokens(secrets) {
|
|
|
62316
62690
|
if (!parsed.ok) console.error(`[ingress] SWITCHBOARD_INGRESS_TOKENS is ${parsed.reason} \u2014 ingress disabled`);
|
|
62317
62691
|
return { tokens: parsed.tokens };
|
|
62318
62692
|
}
|
|
62319
|
-
var PLATFORM, DEFAULT_CHANNEL, DEFAULT_THREAD, MAX_BODY_BYTES2, HttpIO, DISPATCH_ACTION;
|
|
62693
|
+
var PLATFORM, DEFAULT_CHANNEL, DEFAULT_THREAD, INGRESS_COMPONENT_MAX_LENGTH, MAX_BODY_BYTES2, HttpIO, DISPATCH_ACTION;
|
|
62320
62694
|
var init_http = __esm({
|
|
62321
62695
|
"../../src/channels/http.ts"() {
|
|
62322
62696
|
"use strict";
|
|
@@ -62329,6 +62703,7 @@ var init_http = __esm({
|
|
|
62329
62703
|
PLATFORM = "http";
|
|
62330
62704
|
DEFAULT_CHANNEL = "default";
|
|
62331
62705
|
DEFAULT_THREAD = "default";
|
|
62706
|
+
INGRESS_COMPONENT_MAX_LENGTH = 128;
|
|
62332
62707
|
MAX_BODY_BYTES2 = 1e6;
|
|
62333
62708
|
HttpIO = class {
|
|
62334
62709
|
constructor(priorTurns = []) {
|
|
@@ -62525,7 +62900,7 @@ function openAiStopReason(raw) {
|
|
|
62525
62900
|
}
|
|
62526
62901
|
}
|
|
62527
62902
|
function meterAnthropicMessage(json4) {
|
|
62528
|
-
const msg =
|
|
62903
|
+
const msg = record2(json4);
|
|
62529
62904
|
if (!msg) return {};
|
|
62530
62905
|
const usage = usageFromAnthropic(msg.usage);
|
|
62531
62906
|
return {
|
|
@@ -62534,11 +62909,11 @@ function meterAnthropicMessage(json4) {
|
|
|
62534
62909
|
};
|
|
62535
62910
|
}
|
|
62536
62911
|
function meterOpenAiCompletion(json4) {
|
|
62537
|
-
const body =
|
|
62912
|
+
const body = record2(json4);
|
|
62538
62913
|
if (!body) return {};
|
|
62539
62914
|
const usage = usageFromOpenAI(body.usage);
|
|
62540
62915
|
const reported = reportedCostOf(body.usage);
|
|
62541
|
-
const choice = Array.isArray(body.choices) ?
|
|
62916
|
+
const choice = Array.isArray(body.choices) ? record2(body.choices[0]) : void 0;
|
|
62542
62917
|
return {
|
|
62543
62918
|
...usage ? { usage } : {},
|
|
62544
62919
|
...reported ? { reported } : {},
|
|
@@ -62548,15 +62923,15 @@ function meterOpenAiCompletion(json4) {
|
|
|
62548
62923
|
function responsesStopReason(response) {
|
|
62549
62924
|
const output = Array.isArray(response.output) ? response.output : [];
|
|
62550
62925
|
if (response.status === "completed") {
|
|
62551
|
-
return output.some((item) =>
|
|
62926
|
+
return output.some((item) => record2(item)?.type === "function_call") ? "tool_use" : "end_turn";
|
|
62552
62927
|
}
|
|
62553
62928
|
if (response.status === "incomplete") {
|
|
62554
|
-
return
|
|
62929
|
+
return record2(response.incomplete_details)?.reason === "max_output_tokens" ? "max_tokens" : "other";
|
|
62555
62930
|
}
|
|
62556
62931
|
return "other";
|
|
62557
62932
|
}
|
|
62558
62933
|
function meterResponses(json4) {
|
|
62559
|
-
const body =
|
|
62934
|
+
const body = record2(json4);
|
|
62560
62935
|
if (!body) return {};
|
|
62561
62936
|
const usage = usageFromResponses(body.usage);
|
|
62562
62937
|
const reported = reportedCostOf(body.usage);
|
|
@@ -62578,87 +62953,20 @@ function shapeTools(shape, body, marks) {
|
|
|
62578
62953
|
const allowed2 = new Set(marks.turn.tools);
|
|
62579
62954
|
const table = Array.isArray(body.tools) ? body.tools : [];
|
|
62580
62955
|
const kept = table.filter((t) => {
|
|
62581
|
-
const name = toolNameOf(shape,
|
|
62956
|
+
const name = toolNameOf(shape, record2(t));
|
|
62582
62957
|
return typeof name === "string" && allowed2.has(name);
|
|
62583
62958
|
});
|
|
62584
62959
|
const { tool_choice: _choice, ...rest } = body;
|
|
62585
62960
|
const shaped = { ...rest, tools: kept };
|
|
62586
62961
|
return { body: shaped, toolChoice: toolChoiceWord(shape, void 0, kept.length) };
|
|
62587
62962
|
}
|
|
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
62963
|
function toolNameOf(shape, tool) {
|
|
62656
62964
|
if (tool === void 0) return void 0;
|
|
62657
|
-
return shape === "openai-chat" ?
|
|
62965
|
+
return shape === "openai-chat" ? record2(tool.function)?.name : tool.name;
|
|
62658
62966
|
}
|
|
62659
62967
|
function toolSchemaOf(shape, tool) {
|
|
62660
62968
|
if (shape === "anthropic-messages") return tool.input_schema;
|
|
62661
|
-
if (shape === "openai-chat") return
|
|
62969
|
+
if (shape === "openai-chat") return record2(tool.function)?.parameters;
|
|
62662
62970
|
return tool.parameters;
|
|
62663
62971
|
}
|
|
62664
62972
|
function schemaKeywords(value, into = /* @__PURE__ */ new Set()) {
|
|
@@ -62666,12 +62974,12 @@ function schemaKeywords(value, into = /* @__PURE__ */ new Set()) {
|
|
|
62666
62974
|
for (const item of value) schemaKeywords(item, into);
|
|
62667
62975
|
return into;
|
|
62668
62976
|
}
|
|
62669
|
-
const schema =
|
|
62977
|
+
const schema = record2(value);
|
|
62670
62978
|
if (!schema) return into;
|
|
62671
62979
|
for (const [keyword, item] of Object.entries(schema)) {
|
|
62672
62980
|
into.add(keyword);
|
|
62673
|
-
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") &&
|
|
62674
|
-
for (const child of Object.values(
|
|
62981
|
+
if ((keyword === "properties" || keyword === "$defs" || keyword === "definitions") && record2(item)) {
|
|
62982
|
+
for (const child of Object.values(record2(item))) schemaKeywords(child, into);
|
|
62675
62983
|
} else {
|
|
62676
62984
|
schemaKeywords(item, into);
|
|
62677
62985
|
}
|
|
@@ -62682,7 +62990,7 @@ function providerErrorProse(body) {
|
|
|
62682
62990
|
const strings2 = (value, into = []) => {
|
|
62683
62991
|
if (typeof value === "string") into.push(value);
|
|
62684
62992
|
else if (Array.isArray(value)) for (const item of value) strings2(item, into);
|
|
62685
|
-
else if (
|
|
62993
|
+
else if (record2(value)) for (const item of Object.values(record2(value))) strings2(item, into);
|
|
62686
62994
|
return into;
|
|
62687
62995
|
};
|
|
62688
62996
|
try {
|
|
@@ -62700,7 +63008,7 @@ function providerSchemaRejectionOf2(shape, body, errorBody) {
|
|
|
62700
63008
|
prose2
|
|
62701
63009
|
))
|
|
62702
63010
|
return void 0;
|
|
62703
|
-
const candidates2 = (Array.isArray(body.tools) ? body.tools : []).map((item) =>
|
|
63011
|
+
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
63012
|
(tool) => typeof tool.name === "string" && quoted(prose2, tool.name)
|
|
62705
63013
|
);
|
|
62706
63014
|
if (candidates2.length !== 1) return void 0;
|
|
@@ -62723,7 +63031,7 @@ function toolsOffered(shape, body) {
|
|
|
62723
63031
|
const table = Array.isArray(body.tools) ? body.tools : [];
|
|
62724
63032
|
const names = [];
|
|
62725
63033
|
for (const t of table) {
|
|
62726
|
-
const name = toolNameOf(shape,
|
|
63034
|
+
const name = toolNameOf(shape, record2(t));
|
|
62727
63035
|
if (typeof name === "string") names.push(name);
|
|
62728
63036
|
}
|
|
62729
63037
|
names.sort();
|
|
@@ -62736,7 +63044,7 @@ function toolsOffered(shape, body) {
|
|
|
62736
63044
|
function toolChoiceWord(shape, choice, tools) {
|
|
62737
63045
|
if (choice === void 0 || choice === null) return tools > 0 ? "auto" : "none";
|
|
62738
63046
|
if (shape === "anthropic-messages") {
|
|
62739
|
-
const type =
|
|
63047
|
+
const type = record2(choice)?.type;
|
|
62740
63048
|
if (type === "none" || type === "any" || type === "tool") return type;
|
|
62741
63049
|
if (type === "auto") return tools > 0 ? "auto" : "none";
|
|
62742
63050
|
return tools > 0 ? "auto" : "none";
|
|
@@ -62744,7 +63052,7 @@ function toolChoiceWord(shape, choice, tools) {
|
|
|
62744
63052
|
if (choice === "none") return "none";
|
|
62745
63053
|
if (choice === "required") return "any";
|
|
62746
63054
|
if (choice === "auto") return tools > 0 ? "auto" : "none";
|
|
62747
|
-
return
|
|
63055
|
+
return record2(choice) ? "tool" : tools > 0 ? "auto" : "none";
|
|
62748
63056
|
}
|
|
62749
63057
|
function turnAttrs(grant2, meter, ttftMs, offered, price) {
|
|
62750
63058
|
const u = meter.usage;
|
|
@@ -62853,7 +63161,7 @@ async function handleAdmitted(door, req, deps) {
|
|
|
62853
63161
|
} catch {
|
|
62854
63162
|
return refusalResponse(shape, 400, "invalid_body", "the body is not JSON");
|
|
62855
63163
|
}
|
|
62856
|
-
const body =
|
|
63164
|
+
const body = record2(parsed);
|
|
62857
63165
|
if (!body) return refusalResponse(shape, 400, "invalid_body", "the body must be a JSON object");
|
|
62858
63166
|
const upstream = upstreamFor(
|
|
62859
63167
|
shape,
|
|
@@ -63124,7 +63432,7 @@ function createModelProxyHandler(deps) {
|
|
|
63124
63432
|
})();
|
|
63125
63433
|
};
|
|
63126
63434
|
}
|
|
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,
|
|
63435
|
+
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
63436
|
var init_modelProxy = __esm({
|
|
63129
63437
|
"../../src/channels/modelProxy.ts"() {
|
|
63130
63438
|
"use strict";
|
|
@@ -63132,6 +63440,7 @@ var init_modelProxy = __esm({
|
|
|
63132
63440
|
init_providerFailureAuth();
|
|
63133
63441
|
init_usage();
|
|
63134
63442
|
init_modelPricing();
|
|
63443
|
+
init_providerToolSchemas();
|
|
63135
63444
|
init_provider();
|
|
63136
63445
|
init_http();
|
|
63137
63446
|
ANTHROPIC_MESSAGES_PATH = "/v1/messages";
|
|
@@ -63148,7 +63457,7 @@ var init_modelProxy = __esm({
|
|
|
63148
63457
|
DEFAULT_ANTHROPIC_VERSION = "2023-06-01";
|
|
63149
63458
|
FORWARDED_REQUEST_HEADERS = ["accept", "anthropic-version", "anthropic-beta"];
|
|
63150
63459
|
FORWARDED_RESPONSE_HEADERS = ["content-type", "request-id", "x-request-id"];
|
|
63151
|
-
|
|
63460
|
+
record2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : void 0;
|
|
63152
63461
|
SseMeter = class {
|
|
63153
63462
|
constructor(shape) {
|
|
63154
63463
|
this.shape = shape;
|
|
@@ -63192,7 +63501,7 @@ var init_modelProxy = __esm({
|
|
|
63192
63501
|
};
|
|
63193
63502
|
}
|
|
63194
63503
|
apply(json4) {
|
|
63195
|
-
const event =
|
|
63504
|
+
const event = record2(json4);
|
|
63196
63505
|
if (!event) return;
|
|
63197
63506
|
if (this.shape === "openai-responses") {
|
|
63198
63507
|
if (event.type !== "response.completed" && event.type !== "response.incomplete" && event.type !== "response.failed")
|
|
@@ -63205,12 +63514,12 @@ var init_modelProxy = __esm({
|
|
|
63205
63514
|
}
|
|
63206
63515
|
if (this.shape === "anthropic-messages") {
|
|
63207
63516
|
if (event.type === "message_start") {
|
|
63208
|
-
const usage2 = usageFromAnthropic(
|
|
63517
|
+
const usage2 = usageFromAnthropic(record2(event.message)?.usage);
|
|
63209
63518
|
if (usage2) this.usage = { ...this.usage, ...usage2 };
|
|
63210
63519
|
} else if (event.type === "message_delta") {
|
|
63211
|
-
const stop =
|
|
63520
|
+
const stop = record2(event.delta)?.stop_reason;
|
|
63212
63521
|
if (typeof stop === "string") this.stopReason = anthropicStopReason(stop);
|
|
63213
|
-
const usage2 =
|
|
63522
|
+
const usage2 = record2(event.usage);
|
|
63214
63523
|
if (usage2) {
|
|
63215
63524
|
if (typeof usage2.output_tokens === "number") this.usage.outputTokens = usage2.output_tokens;
|
|
63216
63525
|
if (typeof usage2.input_tokens === "number") this.usage.inputTokens = usage2.input_tokens;
|
|
@@ -63223,7 +63532,7 @@ var init_modelProxy = __esm({
|
|
|
63223
63532
|
}
|
|
63224
63533
|
return;
|
|
63225
63534
|
}
|
|
63226
|
-
const choice = Array.isArray(event.choices) ?
|
|
63535
|
+
const choice = Array.isArray(event.choices) ? record2(event.choices[0]) : void 0;
|
|
63227
63536
|
if (typeof choice?.finish_reason === "string") this.stopReason = openAiStopReason(choice.finish_reason);
|
|
63228
63537
|
const usage = usageFromOpenAI(event.usage);
|
|
63229
63538
|
if (usage) this.usage = { ...this.usage, ...usage };
|
|
@@ -63245,7 +63554,7 @@ function bearerOf(headers) {
|
|
|
63245
63554
|
const m = value ? /^Bearer\s+(\S+)$/i.exec(value.trim()) : null;
|
|
63246
63555
|
return m ? m[1] : void 0;
|
|
63247
63556
|
}
|
|
63248
|
-
function
|
|
63557
|
+
function judge4(deps, presented) {
|
|
63249
63558
|
const verdict = deps.bearers.verify(presented);
|
|
63250
63559
|
if (!verdict.ok) {
|
|
63251
63560
|
const status3 = verdict.reason === "unknown_run" ? 404 : verdict.reason === "malformed" ? 401 : verdict.reason === "unknown_bearer" ? 401 : 403;
|
|
@@ -63267,12 +63576,12 @@ function held(path, reason2, runId3) {
|
|
|
63267
63576
|
async function admitHarnessRequest(deps, headers, path) {
|
|
63268
63577
|
const presented = bearerOf(headers);
|
|
63269
63578
|
if (!presented) return { ok: false, response: { status: 401, body: { error: "missing_bearer" } } };
|
|
63270
|
-
let verdict =
|
|
63579
|
+
let verdict = judge4(deps, presented);
|
|
63271
63580
|
if (verdict.ok) return verdict;
|
|
63272
63581
|
if (!RESUME_CAN_OPEN.has(verdict.reason)) return refused6(verdict);
|
|
63273
63582
|
if (!deps.takeover.settled) {
|
|
63274
63583
|
await deps.takeover.whenSettled(DOOR_HOLD_MS);
|
|
63275
|
-
verdict =
|
|
63584
|
+
verdict = judge4(deps, presented);
|
|
63276
63585
|
if (verdict.ok) return verdict;
|
|
63277
63586
|
if (!RESUME_CAN_OPEN.has(verdict.reason)) return refused6(verdict);
|
|
63278
63587
|
if (!deps.takeover.settled) return held(path, "reclaim_pending", verdict.runId);
|
|
@@ -64608,13 +64917,13 @@ async function catchUpMissedMentions(opts) {
|
|
|
64608
64917
|
const orphanCutoffMs = now - (opts.orphanWindowMs ?? ORPHAN_CARD_WINDOW_MS);
|
|
64609
64918
|
const threadCutoffMs = sweep ? Math.min(cutoffMs, orphanCutoffMs) : cutoffMs;
|
|
64610
64919
|
const { client, botUserId } = opts;
|
|
64611
|
-
const
|
|
64920
|
+
const record3 = opts.record ?? recordCatchUpOutcome;
|
|
64612
64921
|
let channels;
|
|
64613
64922
|
try {
|
|
64614
64923
|
channels = await listChannels(client);
|
|
64615
64924
|
} catch (err2) {
|
|
64616
64925
|
log(`[catch-up] cannot list channels: ${errMsg(err2)}`);
|
|
64617
|
-
|
|
64926
|
+
record3({ at: now, channels: 0, missed: 0, skippedChannels: 0, error: errMsg(err2) });
|
|
64618
64927
|
return { channels: 0, missed: 0, orphans: 0, skippedChannels: 0 };
|
|
64619
64928
|
}
|
|
64620
64929
|
const scans = await mapLimit(channels, CATCH_UP_CHANNEL_CONCURRENCY, async (channel) => {
|
|
@@ -64683,7 +64992,7 @@ async function catchUpMissedMentions(opts) {
|
|
|
64683
64992
|
log(
|
|
64684
64993
|
`[catch-up] scanned ${channels.length} channel(s): ${missed} missed message(s), ${orphans} orphaned card(s), ${skippedChannels} skipped`
|
|
64685
64994
|
);
|
|
64686
|
-
|
|
64995
|
+
record3({ at: now, channels: channels.length, missed, skippedChannels });
|
|
64687
64996
|
return { channels: channels.length, missed, orphans, skippedChannels };
|
|
64688
64997
|
}
|
|
64689
64998
|
async function listChannels(client) {
|
|
@@ -66571,11 +66880,19 @@ ${JSON.stringify(result.value)}` }] });
|
|
|
66571
66880
|
if (typeof args.text !== "string" || args.text.trim() === "") {
|
|
66572
66881
|
return err(id, INVALID_PARAMS, "`text` is required and must be a non-empty string");
|
|
66573
66882
|
}
|
|
66574
|
-
if (args.channel !== void 0
|
|
66575
|
-
return err(id, INVALID_PARAMS, "`channel` must be a string");
|
|
66883
|
+
if (args.channel !== void 0) {
|
|
66884
|
+
if (typeof args.channel !== "string") return err(id, INVALID_PARAMS, "`channel` must be a string");
|
|
66885
|
+
const error = ingressComponentError("channel", args.channel);
|
|
66886
|
+
if (error) return err(id, INVALID_PARAMS, error);
|
|
66576
66887
|
}
|
|
66577
|
-
if (args.thread !== void 0
|
|
66578
|
-
return err(id, INVALID_PARAMS, "`thread` must be a string");
|
|
66888
|
+
if (args.thread !== void 0) {
|
|
66889
|
+
if (typeof args.thread !== "string") return err(id, INVALID_PARAMS, "`thread` must be a string");
|
|
66890
|
+
const error = ingressComponentError("thread", args.thread);
|
|
66891
|
+
if (error) return err(id, INVALID_PARAMS, error);
|
|
66892
|
+
}
|
|
66893
|
+
if (identity2.channel !== void 0) {
|
|
66894
|
+
const error = ingressComponentError("channel", identity2.channel);
|
|
66895
|
+
if (error) return err(id, INVALID_PARAMS, error);
|
|
66579
66896
|
}
|
|
66580
66897
|
const receivedAt = systemClock();
|
|
66581
66898
|
const trace = startRequestRoot(deps, { channel: "mcp", receivedAt });
|
|
@@ -68211,16 +68528,16 @@ function residentStateTone(state) {
|
|
|
68211
68528
|
}
|
|
68212
68529
|
function residentsFleetTone(records) {
|
|
68213
68530
|
if (records.length === 0) return "grey";
|
|
68214
|
-
const tones = new Set(records.map((
|
|
68531
|
+
const tones = new Set(records.map((record3) => residentStateTone(residentLive(rec(record3)).state)));
|
|
68215
68532
|
if (tones.has("red")) return "red";
|
|
68216
68533
|
if (tones.has("amber")) return "amber";
|
|
68217
68534
|
return tones.size === 1 && tones.has("green") ? "green" : "grey";
|
|
68218
68535
|
}
|
|
68219
|
-
function residentSlug(
|
|
68220
|
-
return str8(
|
|
68536
|
+
function residentSlug(record3) {
|
|
68537
|
+
return str8(record3.resource).replace(/^repo:/, "");
|
|
68221
68538
|
}
|
|
68222
|
-
function residentLive(
|
|
68223
|
-
const live = rec(
|
|
68539
|
+
function residentLive(record3) {
|
|
68540
|
+
const live = rec(record3.live);
|
|
68224
68541
|
if (typeof live.error === "string") return { ...live, state: "unreachable", reason: live.error };
|
|
68225
68542
|
return { ...live, state: str8(live.state) || "unknown", reason: str8(live.reason) };
|
|
68226
68543
|
}
|
|
@@ -68520,12 +68837,12 @@ function createResidentsViewHandler(deps) {
|
|
|
68520
68837
|
finish2(200);
|
|
68521
68838
|
return;
|
|
68522
68839
|
}
|
|
68523
|
-
const
|
|
68524
|
-
if (!
|
|
68840
|
+
const record3 = listing.residents.find((x) => residentSlug(x) === route2.slug);
|
|
68841
|
+
if (!record3) {
|
|
68525
68842
|
plainEnd(404, `${route2.slug} is not onboarded as a resident`);
|
|
68526
68843
|
return;
|
|
68527
68844
|
}
|
|
68528
|
-
page2(req, res, 200, ctx.actor, route2.slug, { page: "resident", slug: route2.slug, record:
|
|
68845
|
+
page2(req, res, 200, ctx.actor, route2.slug, { page: "resident", slug: route2.slug, record: record3 });
|
|
68529
68846
|
finish2(200);
|
|
68530
68847
|
});
|
|
68531
68848
|
return true;
|
|
@@ -70667,9 +70984,9 @@ function startReclaimSweep(opts) {
|
|
|
70667
70984
|
return { stop: () => stop(timer) };
|
|
70668
70985
|
}
|
|
70669
70986
|
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:
|
|
70987
|
+
const record3 = reclaimedRunRecord(input);
|
|
70988
|
+
const result = await ledger.finish(input.row.runId, gen, record3);
|
|
70989
|
+
return result.ok ? { ok: true, record: record3 } : { ok: false, reason: result.reason ?? "refused" };
|
|
70673
70990
|
}
|
|
70674
70991
|
async function completenessVerdict(ledger, row2, lastStep) {
|
|
70675
70992
|
if (!lastStep) return { resumable: false, why: "no step record: killed before its conversation was stored" };
|
|
@@ -72219,11 +72536,11 @@ async function reviewPostedAtPatiently(deps, pr, verdict, head) {
|
|
|
72219
72536
|
}
|
|
72220
72537
|
return answer;
|
|
72221
72538
|
}
|
|
72222
|
-
function reviewPostedByRecord(
|
|
72223
|
-
const post =
|
|
72224
|
-
if (post === void 0 ||
|
|
72539
|
+
function reviewPostedByRecord(record3, pr) {
|
|
72540
|
+
const post = record3.reviewPost;
|
|
72541
|
+
if (post === void 0 || record3.reviewHead === void 0 || record3.verdict === void 0) return void 0;
|
|
72225
72542
|
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(),
|
|
72543
|
+
const same = post.target.repo === pr.repo && post.target.number === pr.number && sameCommit(post.head.toLowerCase(), record3.reviewHead) && post.verdict === record3.verdict.verdict;
|
|
72227
72544
|
return same ? { reviewPosted: true } : void 0;
|
|
72228
72545
|
}
|
|
72229
72546
|
function headBranchStateUnknown(facts2) {
|
|
@@ -72240,6 +72557,7 @@ function pullRequestState(number, fallbackUrl, facts2) {
|
|
|
72240
72557
|
state: "merged",
|
|
72241
72558
|
prNumber: number,
|
|
72242
72559
|
url,
|
|
72560
|
+
...facts2.headSha !== void 0 ? { headSha: facts2.headSha } : {},
|
|
72243
72561
|
sha: facts2.mergeCommitSha,
|
|
72244
72562
|
mergedAt: facts2.mergedAt,
|
|
72245
72563
|
...facts2.mergedBy !== void 0 ? { mergedBy: facts2.mergedBy } : {}
|
|
@@ -72310,15 +72628,16 @@ async function readRecord(body, deps) {
|
|
|
72310
72628
|
at
|
|
72311
72629
|
});
|
|
72312
72630
|
const full = await deps.runs.getRun(body.runId, { include: "messages" });
|
|
72313
|
-
const
|
|
72314
|
-
const finalReply = finalReplyOf2(
|
|
72315
|
-
const reviewAskedAt = reviewAskedAtOf(
|
|
72316
|
-
const
|
|
72631
|
+
const record3 = full.ok ? full.value : view;
|
|
72632
|
+
const finalReply = finalReplyOf2(record3.events);
|
|
72633
|
+
const reviewAskedAt = reviewAskedAtOf(record3.events);
|
|
72634
|
+
const description = record3.events?.some((event) => event.type === "pr_description") === true;
|
|
72635
|
+
const opened = prOpenedOf(record3.events);
|
|
72317
72636
|
const pr = opened !== void 0 ? { number: opened.number, url: opened.url, created: opened.created } : void 0;
|
|
72318
72637
|
let coordinatorUnit;
|
|
72319
72638
|
if (instanceRow !== null && typeof body.unit === "string") {
|
|
72320
72639
|
coordinatorUnit = (await deps.instances.listUnits(instanceRow.id)).find((u) => u.unit === body.unit);
|
|
72321
|
-
const pushedPrRef = opened?.head !== void 0 &&
|
|
72640
|
+
const pushedPrRef = opened?.head !== void 0 && record3.pushed?.some((pushed) => pushed.ref === opened.head) ? opened.head : void 0;
|
|
72322
72641
|
if (coordinatorUnit !== void 0 && pushedPrRef !== void 0 && coordinatorUnit.branch !== pushedPrRef) {
|
|
72323
72642
|
coordinatorUnit = { ...coordinatorUnit, branch: pushedPrRef };
|
|
72324
72643
|
await deps.instances.putUnits([coordinatorUnit]);
|
|
@@ -72345,13 +72664,13 @@ async function readRecord(body, deps) {
|
|
|
72345
72664
|
});
|
|
72346
72665
|
}
|
|
72347
72666
|
let posted;
|
|
72348
|
-
if (
|
|
72667
|
+
if (record3.verdict !== void 0 && record3.reviewHead !== void 0 && typeof body.unit === "string") {
|
|
72349
72668
|
const instance = instanceRow;
|
|
72350
72669
|
if (instance && coordinatorUnit?.pr !== void 0) {
|
|
72351
72670
|
const unitPr = { repo: instance.repo, number: coordinatorUnit.pr.number };
|
|
72352
|
-
posted = reviewPostedByRecord(
|
|
72671
|
+
posted = reviewPostedByRecord(record3, unitPr);
|
|
72353
72672
|
if (posted === void 0) {
|
|
72354
|
-
const seen = await reviewPostedAtPatiently(deps, unitPr,
|
|
72673
|
+
const seen = await reviewPostedAtPatiently(deps, unitPr, record3.verdict.verdict, record3.reviewHead);
|
|
72355
72674
|
if (seen !== void 0) posted = { reviewPosted: seen };
|
|
72356
72675
|
}
|
|
72357
72676
|
}
|
|
@@ -72363,27 +72682,31 @@ async function readRecord(body, deps) {
|
|
|
72363
72682
|
run: {
|
|
72364
72683
|
...coordinatorRunView(view, id.value, finalReply),
|
|
72365
72684
|
...pr !== void 0 ? { pr } : {},
|
|
72366
|
-
...
|
|
72367
|
-
...
|
|
72685
|
+
...record3.verdict !== void 0 ? { verdict: record3.verdict } : {},
|
|
72686
|
+
...record3.reviewHead !== void 0 ? { reviewHead: record3.reviewHead } : {},
|
|
72368
72687
|
...reviewAskedAt !== void 0 ? { reviewAskedAt } : {},
|
|
72369
72688
|
...posted !== void 0 ? { reviewPosted: posted.reviewPosted } : {},
|
|
72370
72689
|
...posted?.reviewPostReason !== void 0 ? { reviewPostReason: posted.reviewPostReason } : {},
|
|
72371
|
-
...
|
|
72372
|
-
|
|
72690
|
+
...record3.dispositions !== void 0 ? { dispositions: record3.dispositions } : {},
|
|
72691
|
+
// Findings readiness requires the typed description output. Its event is
|
|
72692
|
+
// durable on the child record, so the runner can verify it without
|
|
72693
|
+
// trusting final-reply prose.
|
|
72694
|
+
...description ? { description: true } : {},
|
|
72695
|
+
...record3.handoff !== void 0 ? { handoff: true } : {},
|
|
72373
72696
|
// The renewal's facts (decision 0046): progress is read off these.
|
|
72374
|
-
...
|
|
72375
|
-
...
|
|
72697
|
+
...record3.pushed !== void 0 ? { pushed: record3.pushed } : {},
|
|
72698
|
+
...record3.lease !== void 0 ? { leaseStartedAt: record3.lease.startedAt } : {},
|
|
72376
72699
|
// What the child cost, as the runs service prices it (costs.md item 4c):
|
|
72377
72700
|
// null when unknown — no usage on the record, or a model without a price
|
|
72378
72701
|
// — so a capped grant never renews on an understated total.
|
|
72379
|
-
costUsd:
|
|
72380
|
-
...
|
|
72702
|
+
costUsd: record3.cost?.usd ?? null,
|
|
72703
|
+
...record3.handoff !== void 0 ? { handoffLists: record3.handoff } : {},
|
|
72381
72704
|
// The failure by name (run-history item 57): a `provider_transient` lets
|
|
72382
72705
|
// the machine re-run a round-0 child that pushed nothing (issue 1932).
|
|
72383
|
-
...
|
|
72706
|
+
...record3.failure !== void 0 ? { failure: record3.failure } : {},
|
|
72384
72707
|
// What ended an interrupted child (issue 1876), off its record's own
|
|
72385
72708
|
// events: the unit's ending names the actual cause in the user's nouns.
|
|
72386
|
-
...view.status === "interrupted" ? interruptionOf(
|
|
72709
|
+
...view.status === "interrupted" ? interruptionOf(record3.events) : {}
|
|
72387
72710
|
},
|
|
72388
72711
|
at
|
|
72389
72712
|
});
|
|
@@ -72462,6 +72785,13 @@ async function recoverPushedBranch(deps, instance, row2, branch2, runId3) {
|
|
|
72462
72785
|
if (subject2 !== void 0 && checkPrTitle(subject2, prTitleVocabulary_default).ok) title = subject2;
|
|
72463
72786
|
}
|
|
72464
72787
|
title ??= recoveredFallbackTitle(row2, branch2, instance.plan?.id ?? parsePlanBranch(branch2)?.planId);
|
|
72788
|
+
const header4 = requestedByLine({
|
|
72789
|
+
name: instance.userName?.trim() || instance.userId,
|
|
72790
|
+
threadUrl: threadPageLink(instance.threadKey, deps.runPageBase?.replace(/\/runs\/?$/, "") ?? "")
|
|
72791
|
+
});
|
|
72792
|
+
prBody = `${header4}
|
|
72793
|
+
|
|
72794
|
+
${prBody}`;
|
|
72465
72795
|
if (deps.rewriteIdentities !== void 0) {
|
|
72466
72796
|
const rewritten = await deps.rewriteIdentities({
|
|
72467
72797
|
repo: instance.repo,
|
|
@@ -73711,7 +74041,7 @@ ${planSummary(units)}`).catch(() => {
|
|
|
73711
74041
|
const summary = deps.registry.getById(host.runId);
|
|
73712
74042
|
const visibility = await deps.channelVisibilityOf(instance.channelId);
|
|
73713
74043
|
const finishedAt = snap?.finishedAt ?? at;
|
|
73714
|
-
const
|
|
74044
|
+
const record3 = assembleRunRecord({
|
|
73715
74045
|
run: { id: host.runId, ...summary?.label !== void 0 ? { label: summary.label } : {} },
|
|
73716
74046
|
snap,
|
|
73717
74047
|
agent: "ship",
|
|
@@ -73741,7 +74071,7 @@ ${planSummary(units)}`).catch(() => {
|
|
|
73741
74071
|
seal
|
|
73742
74072
|
});
|
|
73743
74073
|
const handle2 = deps.ledgerRuns().find((run2) => run2.runId === host.runId);
|
|
73744
|
-
deps.runHistoryWriter.write(
|
|
74074
|
+
deps.runHistoryWriter.write(record3, handle2 !== void 0 ? { via: handle2.sink } : void 0);
|
|
73745
74075
|
}
|
|
73746
74076
|
(deps.log ?? console.log)(`[coordinator] ${instance.id}: finished ${body.outcome} \u2014 record ${runId3}`);
|
|
73747
74077
|
return json3(200, { ok: true, runId: runId3, at });
|
|
@@ -73867,6 +74197,8 @@ var init_adminCoordinator = __esm({
|
|
|
73867
74197
|
"use strict";
|
|
73868
74198
|
init_budgets();
|
|
73869
74199
|
init_verbosity();
|
|
74200
|
+
init_prDescription();
|
|
74201
|
+
init_reply();
|
|
73870
74202
|
init_registry();
|
|
73871
74203
|
init_authorize();
|
|
73872
74204
|
init_actor();
|