@coreplane/switchboard 1.261.1 → 1.261.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/coordinator/contract.ts +39 -0
- package/dist/assets/src/core/runEvents.ts +15 -1
- package/dist/assets/src/core/runLedger/types.ts +4 -0
- package/dist/assets/src/core/ship/coordinator.ts +3 -0
- package/dist/assets/src/core/types.ts +6 -2
- package/dist/cli.js +422 -152
- package/package.json +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.261.
|
|
3
|
+
"version": "1.261.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "switchboard",
|
|
9
|
-
"version": "1.261.
|
|
9
|
+
"version": "1.261.2",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"web",
|
|
@@ -21060,7 +21060,7 @@
|
|
|
21060
21060
|
},
|
|
21061
21061
|
"packages/switchboard": {
|
|
21062
21062
|
"name": "@coreplane/switchboard",
|
|
21063
|
-
"version": "1.261.
|
|
21063
|
+
"version": "1.261.2",
|
|
21064
21064
|
"license": "Apache-2.0",
|
|
21065
21065
|
"dependencies": {
|
|
21066
21066
|
"@earendil-works/pi-ai": "0.85.1",
|
package/dist/assets/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.261.
|
|
3
|
+
"version": "1.261.2",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/dist/assets/source.json
CHANGED
|
@@ -293,6 +293,20 @@ export interface RunFinishedPayload {
|
|
|
293
293
|
parentInstanceId: string;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
/** Durable authority for publishing another commit to an existing pull
|
|
297
|
+
* request. Every identity field is repeated deliberately: publication is
|
|
298
|
+
* allowed only when the unit, checkout and a fresh remote read all agree with
|
|
299
|
+
* this exact record. */
|
|
300
|
+
export interface ExistingPrPublicationBinding {
|
|
301
|
+
repo: string;
|
|
302
|
+
pr: number;
|
|
303
|
+
headRef: string;
|
|
304
|
+
baseRef: string;
|
|
305
|
+
expectedHeadSha: string;
|
|
306
|
+
publicationRef: string;
|
|
307
|
+
owner: { instanceId: string; unit: string };
|
|
308
|
+
}
|
|
309
|
+
|
|
296
310
|
/** What a coordinator's spawn stamps on the child's every row: the instance
|
|
297
311
|
* the child belongs to and the key the spawn carried — and, for the child's
|
|
298
312
|
* own post-step, the base its pull request targets. */
|
|
@@ -310,6 +324,10 @@ export interface CoordinatorTag {
|
|
|
310
324
|
* back off its ledger events; `coordinatorFields` still leaves it off the
|
|
311
325
|
* rows, so rows and records keep the shape written before it existed. */
|
|
312
326
|
base?: string;
|
|
327
|
+
/** The existing-PR publication authority this child received from its
|
|
328
|
+
* durable unit row and current Workflow round. Published on the run's event
|
|
329
|
+
* stream so a rehost retains the same fence. */
|
|
330
|
+
publication?: ExistingPrPublicationBinding;
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
/** The tag as the two flat record fields, or nothing — so a row, a summary
|
|
@@ -489,6 +507,9 @@ export interface CoordinatorUnit {
|
|
|
489
507
|
* — no pre-check, no branch, no round 0. A task string's row only; written by
|
|
490
508
|
* the hand-off, read by the driver into the machine's input. */
|
|
491
509
|
resume?: { pr: number; headSha?: string; url?: string };
|
|
510
|
+
/** Exact existing-PR publication authority. Absent for a fresh unit branch;
|
|
511
|
+
* an adopted or resumed PR may publish only through this binding. */
|
|
512
|
+
publication?: ExistingPrPublicationBinding;
|
|
492
513
|
/** The decision-record number reserved at admission for this unit. A unit
|
|
493
514
|
* that writes a record carries it through every attempt and briefs its child
|
|
494
515
|
* as `record: NNNN`; the child never scans the directory for a number. */
|
|
@@ -549,6 +570,23 @@ const isResume = (v: unknown): boolean =>
|
|
|
549
570
|
isFinite(v.pr) &&
|
|
550
571
|
(v.headSha === undefined || isText(v.headSha)) &&
|
|
551
572
|
(v.url === undefined || isText(v.url, 2048));
|
|
573
|
+
const isFullSha = (v: unknown): v is string => typeof v === "string" && /^[0-9a-f]{40}$/i.test(v);
|
|
574
|
+
const isPublication = (v: unknown): v is ExistingPrPublicationBinding =>
|
|
575
|
+
isObject(v) &&
|
|
576
|
+
typeof v.repo === "string" &&
|
|
577
|
+
REPO_SLUG.test(v.repo) &&
|
|
578
|
+
typeof v.pr === "number" &&
|
|
579
|
+
Number.isInteger(v.pr) &&
|
|
580
|
+
v.pr > 0 &&
|
|
581
|
+
isText(v.headRef) &&
|
|
582
|
+
isText(v.baseRef) &&
|
|
583
|
+
isFullSha(v.expectedHeadSha) &&
|
|
584
|
+
isText(v.publicationRef) &&
|
|
585
|
+
isObject(v.owner) &&
|
|
586
|
+
typeof v.owner.instanceId === "string" &&
|
|
587
|
+
INSTANCE_ID_PATTERN.test(v.owner.instanceId) &&
|
|
588
|
+
typeof v.owner.unit === "string" &&
|
|
589
|
+
UNIT_PATTERN.test(v.owner.unit);
|
|
552
590
|
const isThread = (v: unknown): boolean => isObject(v) && isText(v.threadKey) && isOptionalText(v.sourceUrl);
|
|
553
591
|
|
|
554
592
|
/** Structural check on a record from outside the process (a Worker response, an HTTP body). */
|
|
@@ -663,6 +701,7 @@ export function isCoordinatorUnit(v: unknown): v is CoordinatorUnit {
|
|
|
663
701
|
if (r.issue !== undefined && !isFinite(r.issue)) return false;
|
|
664
702
|
if (r.pr !== undefined && !isPr(r.pr)) return false;
|
|
665
703
|
if (r.resume !== undefined && !isResume(r.resume)) return false;
|
|
704
|
+
if (r.publication !== undefined && !isPublication(r.publication)) return false;
|
|
666
705
|
if (r.record !== undefined && (typeof r.record !== "string" || !/^\d{4}$/.test(r.record))) return false;
|
|
667
706
|
if (r.lastPush !== undefined && !isText(r.lastPush)) return false;
|
|
668
707
|
if (r.segments !== undefined && (!Array.isArray(r.segments) || !r.segments.every(isSegment))) return false;
|
|
@@ -208,6 +208,11 @@ export type RunNoteKind =
|
|
|
208
208
|
* after the model's last turn — the release itself runs after the record
|
|
209
209
|
* is sealed — and set on the card's label too, so the loss is never silent. */
|
|
210
210
|
| "work_left_behind"
|
|
211
|
+
/** An existing pull request's exact publication fence denied the push while
|
|
212
|
+
* the workspace held a local checkpoint. The workspace stays attached
|
|
213
|
+
* instead of being discarded or redirected; the summary names the head,
|
|
214
|
+
* counts, denial and that retention beyond the run remains unverified. */
|
|
215
|
+
| "publication_blocked"
|
|
211
216
|
/** The run's ending may have left a command running in its workspace — a
|
|
212
217
|
* call cut by the ending's abort or interrupt, or open when the run failed —
|
|
213
218
|
* so the release tears the workspace down rather than pair it for the
|
|
@@ -356,6 +361,7 @@ export const RUN_NOTE_KINDS = [
|
|
|
356
361
|
"ledger_untracked",
|
|
357
362
|
"rebind_refused",
|
|
358
363
|
"work_left_behind",
|
|
364
|
+
"publication_blocked",
|
|
359
365
|
"workspace_torn_down",
|
|
360
366
|
"pr_not_opened",
|
|
361
367
|
"pr_head_unverified",
|
|
@@ -953,7 +959,15 @@ export type RunEvent =
|
|
|
953
959
|
* instead of letting the binding ref — the unit branch itself — stand in.
|
|
954
960
|
* `base` is absent when the spawn knew none; the post-step then falls to
|
|
955
961
|
* the coordinator store's `instance.base`. Additive: unknown → ignored. */
|
|
956
|
-
| {
|
|
962
|
+
| {
|
|
963
|
+
type: "coordinator_tag";
|
|
964
|
+
parentInstanceId: string;
|
|
965
|
+
unit?: string;
|
|
966
|
+
base?: string;
|
|
967
|
+
publication?: import("./coordinator/contract.js").ExistingPrPublicationBinding;
|
|
968
|
+
seq?: number;
|
|
969
|
+
at?: number;
|
|
970
|
+
}
|
|
957
971
|
/** The plan runner instance a ship run's hand-off created (record 0051 R2;
|
|
958
972
|
* docs/reference/specs/run-history.md item 2): published by the ship branch
|
|
959
973
|
* after `handOffToCoordinator` succeeds, straight to the registry like
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { ChatMessage } from "../chatMessage.js";
|
|
7
7
|
import { PROVIDER_FAILURE_CAUSES, type ProviderFailureCause, type ToolDef } from "../provider.js";
|
|
8
|
+
import type { Verbosity } from "../verbosity.js";
|
|
8
9
|
import type { ChannelVisibility } from "../authz/types.js";
|
|
9
10
|
import type { RunProfile } from "../../config/profile.js";
|
|
10
11
|
import type { RunEvent } from "../runEvents.js";
|
|
@@ -60,6 +61,9 @@ export interface LiveRunMeta {
|
|
|
60
61
|
/** The app that relayed the request for the person (authorization.md item 14): a resume or restart keeps app ∩ person at the gates. */
|
|
61
62
|
postedBy?: string;
|
|
62
63
|
effort?: string;
|
|
64
|
+
/** The request's resolved display level. Hosted parents have no model loop
|
|
65
|
+
* to rebuild it from, and their input event deliberately omits directives. */
|
|
66
|
+
verbosity?: Verbosity;
|
|
63
67
|
/** Decision-record reservation assigned before the attach, so a restart of
|
|
64
68
|
* the admitted task keeps the same brief and process environment. */
|
|
65
69
|
record?: string;
|
|
@@ -502,6 +502,8 @@ export type Brief =
|
|
|
502
502
|
kind: "findings";
|
|
503
503
|
unit: string;
|
|
504
504
|
pr: number;
|
|
505
|
+
/** The exact reviewed head the fix round may replace. */
|
|
506
|
+
headSha?: string;
|
|
505
507
|
/** The review run normally supplies the findings. An adopted attempt
|
|
506
508
|
* can recover them from the last posted verdict marker instead. */
|
|
507
509
|
reviewRunId?: string;
|
|
@@ -1466,6 +1468,7 @@ function briefFor(s: UnitPipelineState, round: RoundRef): Brief {
|
|
|
1466
1468
|
kind: "findings",
|
|
1467
1469
|
unit,
|
|
1468
1470
|
pr,
|
|
1471
|
+
...(s.lastReviewHead !== undefined ? { headSha: s.lastReviewHead } : {}),
|
|
1469
1472
|
...(reviewRunId !== undefined ? { reviewRunId } : {}),
|
|
1470
1473
|
...(reviewRunId === undefined || (answers !== undefined && answers.length > 0) ? { findings: issued } : {}),
|
|
1471
1474
|
...(answers !== undefined && answers.length > 0 ? { answers } : {}),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Verbosity } from "./verbosity.js";
|
|
2
|
+
|
|
1
3
|
// Channel abstraction. A channel (Slack, CLI, Discord, HTTP, ...) is only a
|
|
2
4
|
// transport: it receives text from a user somewhere, hands it to the core
|
|
3
5
|
// dispatcher as an IncomingMessage, and provides a ChannelIO for the core to
|
|
@@ -306,8 +308,10 @@ export interface ChannelIO {
|
|
|
306
308
|
* the row lives in the config object until the click or the expiry.
|
|
307
309
|
*/
|
|
308
310
|
offer?(offer: ConfirmationOffer): Promise<void>;
|
|
309
|
-
/** Create a progress indicator. Adapters may return a no-op handle.
|
|
310
|
-
|
|
311
|
+
/** Create a progress indicator. Adapters may return a no-op handle. The
|
|
312
|
+
* resolved verbosity reaches the transport so native lifecycle chrome can
|
|
313
|
+
* follow the same display contract as replies and cards. */
|
|
314
|
+
status(initial: StatusUpdate, display?: { verbosity?: Verbosity }): Promise<StatusHandle>;
|
|
311
315
|
/**
|
|
312
316
|
* Prior turns of this conversation, oldest first, excluding the triggering
|
|
313
317
|
* message and any bot status noise. Adapters without history return [].
|
package/dist/cli.js
CHANGED
|
@@ -4186,6 +4186,7 @@ var init_runEvents = __esm({
|
|
|
4186
4186
|
"ledger_untracked",
|
|
4187
4187
|
"rebind_refused",
|
|
4188
4188
|
"work_left_behind",
|
|
4189
|
+
"publication_blocked",
|
|
4189
4190
|
"workspace_torn_down",
|
|
4190
4191
|
"pr_not_opened",
|
|
4191
4192
|
"pr_head_unverified",
|
|
@@ -5792,6 +5793,7 @@ function isCoordinatorUnit(v) {
|
|
|
5792
5793
|
if (r.issue !== void 0 && !isFinite(r.issue)) return false;
|
|
5793
5794
|
if (r.pr !== void 0 && !isPr(r.pr)) return false;
|
|
5794
5795
|
if (r.resume !== void 0 && !isResume(r.resume)) return false;
|
|
5796
|
+
if (r.publication !== void 0 && !isPublication(r.publication)) return false;
|
|
5795
5797
|
if (r.record !== void 0 && (typeof r.record !== "string" || !/^\d{4}$/.test(r.record))) return false;
|
|
5796
5798
|
if (r.lastPush !== void 0 && !isText(r.lastPush)) return false;
|
|
5797
5799
|
if (r.segments !== void 0 && (!Array.isArray(r.segments) || !r.segments.every(isSegment))) return false;
|
|
@@ -5855,7 +5857,7 @@ async function sendChildSignal(workflow, signal2) {
|
|
|
5855
5857
|
return { kind: "failed", instance, type, reason: err2 instanceof Error ? err2.message : String(err2) };
|
|
5856
5858
|
}
|
|
5857
5859
|
}
|
|
5858
|
-
var COORDINATOR_IDENTITY, COORDINATOR_STEP_ACTION, PLAN_MERGE_ACTION, COORDINATOR_STEP_PATH_PREFIX, INSTANCE_ID_PATTERN, STEP_NAME_PATTERN, IDEMPOTENCY_KEY_PATTERN, UNIT_PATTERN, UNIT_KEY_PATTERN, CHILD_INTERRUPTED_EVENT_PREFIX, CHILD_RESUMED_EVENT_PREFIX, THREAD_EVENT_MAX_BYTES, serializedBytes, isThreadEventStagedFile, isThreadEventAttachment, isThreadEventMode, UNIT_NUDGE_EVENT_PREFIX, CHECKS_SETTLED_EVENT_PREFIX, PULL_MERGED_EVENT_PREFIX, IDLE_WHY_MAX, REPO_SLUG, MAX_TEXT, MAX_REPORT, MAX_ROUNDS, isText, isOptionalText, isFinite, isCount, isObject, isPr, isResume, isThread, isRoundGate, isHumanGatePending, isUnitIdle, isSegment, isUnitWakeAnswer;
|
|
5860
|
+
var COORDINATOR_IDENTITY, COORDINATOR_STEP_ACTION, PLAN_MERGE_ACTION, COORDINATOR_STEP_PATH_PREFIX, INSTANCE_ID_PATTERN, STEP_NAME_PATTERN, IDEMPOTENCY_KEY_PATTERN, UNIT_PATTERN, UNIT_KEY_PATTERN, CHILD_INTERRUPTED_EVENT_PREFIX, CHILD_RESUMED_EVENT_PREFIX, THREAD_EVENT_MAX_BYTES, serializedBytes, isThreadEventStagedFile, isThreadEventAttachment, isThreadEventMode, UNIT_NUDGE_EVENT_PREFIX, CHECKS_SETTLED_EVENT_PREFIX, PULL_MERGED_EVENT_PREFIX, IDLE_WHY_MAX, REPO_SLUG, MAX_TEXT, MAX_REPORT, MAX_ROUNDS, isText, isOptionalText, isFinite, isCount, isObject, isPr, isResume, isFullSha, isPublication, isThread, isRoundGate, isHumanGatePending, isUnitIdle, isSegment, isUnitWakeAnswer;
|
|
5859
5861
|
var init_contract2 = __esm({
|
|
5860
5862
|
"../../src/core/coordinator/contract.ts"() {
|
|
5861
5863
|
"use strict";
|
|
@@ -5895,6 +5897,8 @@ var init_contract2 = __esm({
|
|
|
5895
5897
|
isObject = (v) => typeof v === "object" && v !== null;
|
|
5896
5898
|
isPr = (v) => isObject(v) && isFinite(v.number) && isText(v.url, 2048);
|
|
5897
5899
|
isResume = (v) => isObject(v) && isFinite(v.pr) && (v.headSha === void 0 || isText(v.headSha)) && (v.url === void 0 || isText(v.url, 2048));
|
|
5900
|
+
isFullSha = (v) => typeof v === "string" && /^[0-9a-f]{40}$/i.test(v);
|
|
5901
|
+
isPublication = (v) => isObject(v) && typeof v.repo === "string" && REPO_SLUG.test(v.repo) && typeof v.pr === "number" && Number.isInteger(v.pr) && v.pr > 0 && isText(v.headRef) && isText(v.baseRef) && isFullSha(v.expectedHeadSha) && isText(v.publicationRef) && isObject(v.owner) && typeof v.owner.instanceId === "string" && INSTANCE_ID_PATTERN.test(v.owner.instanceId) && typeof v.owner.unit === "string" && UNIT_PATTERN.test(v.owner.unit);
|
|
5898
5902
|
isThread = (v) => isObject(v) && isText(v.threadKey) && isOptionalText(v.sourceUrl);
|
|
5899
5903
|
isRoundGate = (v) => isObject(v) && isAddressSeverity(v.level) && Array.isArray(v.findings) && v.findings.every((f) => typeof f === "string");
|
|
5900
5904
|
isHumanGatePending = (v) => isObject(v) && isObject(v.pr) && typeof v.pr.number === "number" && Number.isInteger(v.pr.number) && v.pr.number > 0 && isText(v.pr.url) && typeof v.round === "number" && Number.isInteger(v.round) && v.round >= 1 && Array.isArray(v.findings) && v.findings.every((finding) => isFindingShape(finding)) && (v.verdict === "approve" || v.verdict === "request_changes") && (v.reviewRunId === void 0 || isText(v.reviewRunId)) && (v.headSha === void 0 || isText(v.headSha)) && (v.askedAt === void 0 || isFinite(v.askedAt));
|
|
@@ -7128,9 +7132,9 @@ function makeSsrfLookup(resolve13) {
|
|
|
7128
7132
|
const family = (ip) => ip.includes(":") ? 6 : 4;
|
|
7129
7133
|
resolve13(hostname3).then(
|
|
7130
7134
|
(ips) => {
|
|
7131
|
-
const
|
|
7132
|
-
if (
|
|
7133
|
-
cb(new BlockedUrlError(`host ${hostname3} resolves to blocked address ${
|
|
7135
|
+
const blocked2 = ips.find((ip) => ipInBlockedRange(ip));
|
|
7136
|
+
if (blocked2) {
|
|
7137
|
+
cb(new BlockedUrlError(`host ${hostname3} resolves to blocked address ${blocked2}`));
|
|
7134
7138
|
return;
|
|
7135
7139
|
}
|
|
7136
7140
|
if (ips.length === 0) {
|
|
@@ -36133,7 +36137,7 @@ function cardActivity(e) {
|
|
|
36133
36137
|
if (e.type === "tool_call" && e.command !== void 0) return { kind: "command", tool: e.tool, command: e.command };
|
|
36134
36138
|
return { kind: "line", text: activityLine(e) };
|
|
36135
36139
|
}
|
|
36136
|
-
function
|
|
36140
|
+
function compactActivity(activity) {
|
|
36137
36141
|
if (activity?.kind === "command") return { kind: "line", text: `\u2192 ${activity.tool}` };
|
|
36138
36142
|
return activity;
|
|
36139
36143
|
}
|
|
@@ -36238,7 +36242,9 @@ function cardLines(trace, opts) {
|
|
|
36238
36242
|
});
|
|
36239
36243
|
return { ...shape ? { shape } : {}, ...opts.queued ? { queued: opts.queued } : {} };
|
|
36240
36244
|
}
|
|
36241
|
-
async function replyCommandOutput(io, parsed, text) {
|
|
36245
|
+
async function replyCommandOutput(io, parsed, text, display = {}) {
|
|
36246
|
+
if (parsed.kind === "invoke" && parsed.id === "steer.run" && display.ok === true && !shows(display.verbosity ?? "quiet", "verbose"))
|
|
36247
|
+
return;
|
|
36242
36248
|
if (!io.attach || text.length <= LONG_COMMAND_REPLY_CHARS) return io.reply(text);
|
|
36243
36249
|
const nl = text.indexOf("\n");
|
|
36244
36250
|
const lead = nl === -1 ? text : text.slice(0, nl);
|
|
@@ -38783,6 +38789,79 @@ var init_commandChat = __esm({
|
|
|
38783
38789
|
}
|
|
38784
38790
|
});
|
|
38785
38791
|
|
|
38792
|
+
// ../../src/directives.ts
|
|
38793
|
+
function parseBudgetMinutes(value) {
|
|
38794
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
38795
|
+
const minutes = Number(value);
|
|
38796
|
+
return minutes >= MIN_BOUNDARY_MINUTES ? minutes : void 0;
|
|
38797
|
+
}
|
|
38798
|
+
function lastThreadDirectives(history) {
|
|
38799
|
+
const out = {};
|
|
38800
|
+
for (const h of history) {
|
|
38801
|
+
if (h.role !== "user") continue;
|
|
38802
|
+
for (const m of h.text.matchAll(DIRECTIVE_RE)) {
|
|
38803
|
+
if (m[1] === "model") out.model = m[2];
|
|
38804
|
+
else if (m[1] === "effort" && isEffort(m[2])) out.effort = m[2];
|
|
38805
|
+
else if (m[1] === "verbosity" && isVerbosity(m[2])) out.verbosity = m[2];
|
|
38806
|
+
}
|
|
38807
|
+
}
|
|
38808
|
+
return out;
|
|
38809
|
+
}
|
|
38810
|
+
function parseRenewals(value) {
|
|
38811
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
38812
|
+
const count = Number(value);
|
|
38813
|
+
return count <= GRANT_RENEWALS_MAX ? count : void 0;
|
|
38814
|
+
}
|
|
38815
|
+
function parseDirectives(input) {
|
|
38816
|
+
const out = { text: input };
|
|
38817
|
+
let text = input;
|
|
38818
|
+
const head = HEAD_AGENT_RE.exec(input);
|
|
38819
|
+
if (head !== null && AGENTS[head[1]] !== void 0) {
|
|
38820
|
+
out.agent = head[1];
|
|
38821
|
+
text = text.replace(head[0], " ");
|
|
38822
|
+
}
|
|
38823
|
+
for (const m of input.matchAll(DIRECTIVE_RE)) {
|
|
38824
|
+
const [token2, key, value] = m;
|
|
38825
|
+
if (key === "model") {
|
|
38826
|
+
out.model = value;
|
|
38827
|
+
} else if (key === "effort") {
|
|
38828
|
+
if (!isEffort(value)) continue;
|
|
38829
|
+
out.effort = value;
|
|
38830
|
+
} else if (key === "budget") {
|
|
38831
|
+
const minutes = parseBudgetMinutes(value);
|
|
38832
|
+
if (minutes === void 0) continue;
|
|
38833
|
+
out.budget = minutes;
|
|
38834
|
+
} else if (key === "severity") {
|
|
38835
|
+
if (!isAddressSeverity(value)) continue;
|
|
38836
|
+
out.severity = value;
|
|
38837
|
+
} else if (key === "renewals") {
|
|
38838
|
+
const count = parseRenewals(value);
|
|
38839
|
+
if (count === void 0) continue;
|
|
38840
|
+
out.renewals = count;
|
|
38841
|
+
} else {
|
|
38842
|
+
if (!isVerbosity(value)) continue;
|
|
38843
|
+
out.verbosity = value;
|
|
38844
|
+
}
|
|
38845
|
+
text = text.replace(token2, " ");
|
|
38846
|
+
}
|
|
38847
|
+
out.text = text.replace(/\s+/g, " ").trim();
|
|
38848
|
+
return out;
|
|
38849
|
+
}
|
|
38850
|
+
var DIRECTIVE_RE, HEAD_AGENT_RE;
|
|
38851
|
+
var init_directives = __esm({
|
|
38852
|
+
"../../src/directives.ts"() {
|
|
38853
|
+
"use strict";
|
|
38854
|
+
init_registry();
|
|
38855
|
+
init_validate();
|
|
38856
|
+
init_budgets();
|
|
38857
|
+
init_effort();
|
|
38858
|
+
init_shipPipeline();
|
|
38859
|
+
init_verbosity();
|
|
38860
|
+
DIRECTIVE_RE = /(?:^|\s)(model|effort|budget|severity|renewals|verbosity)[:=](\S+)/g;
|
|
38861
|
+
HEAD_AGENT_RE = /^\s*agent[:=](\S+)/;
|
|
38862
|
+
}
|
|
38863
|
+
});
|
|
38864
|
+
|
|
38786
38865
|
// ../../src/core/runRegistry/runControl.ts
|
|
38787
38866
|
var RunControl;
|
|
38788
38867
|
var init_runControl = __esm({
|
|
@@ -39782,6 +39861,7 @@ var init_commandRun = __esm({
|
|
|
39782
39861
|
async function answerChatCommand(deps, ctx) {
|
|
39783
39862
|
const { msg, io, ending, trace } = ctx;
|
|
39784
39863
|
const root = trace.root;
|
|
39864
|
+
const verbosity2 = deps.config.verbosityFor(msg.channelId, msg.userId, parseDirectives(msg.text).verbosity);
|
|
39785
39865
|
if (deps.commands) {
|
|
39786
39866
|
const chatCmd = parseChatCommand(msg.text, deps.commands);
|
|
39787
39867
|
if (chatCmd) {
|
|
@@ -39801,7 +39881,7 @@ async function answerChatCommand(deps, ctx) {
|
|
|
39801
39881
|
confirmations: deps.confirmations
|
|
39802
39882
|
})
|
|
39803
39883
|
);
|
|
39804
|
-
} : () => root.span("post.reply", () => replyCommandOutput(io, chatCmd, res.text));
|
|
39884
|
+
} : () => root.span("post.reply", () => replyCommandOutput(io, chatCmd, res.text, { verbosity: verbosity2, ok: res.ok }));
|
|
39805
39885
|
await ending.sealAfterReply(async () => {
|
|
39806
39886
|
}, replyFn);
|
|
39807
39887
|
if (res.followUp) postSettledOutcome(res.followUp, io, root);
|
|
@@ -39817,6 +39897,7 @@ var init_fastPath = __esm({
|
|
|
39817
39897
|
init_commandChat();
|
|
39818
39898
|
init_reply();
|
|
39819
39899
|
init_refusal();
|
|
39900
|
+
init_directives();
|
|
39820
39901
|
init_commandRun();
|
|
39821
39902
|
init_commandRun();
|
|
39822
39903
|
}
|
|
@@ -40110,79 +40191,6 @@ var init_references = __esm({
|
|
|
40110
40191
|
}
|
|
40111
40192
|
});
|
|
40112
40193
|
|
|
40113
|
-
// ../../src/directives.ts
|
|
40114
|
-
function parseBudgetMinutes(value) {
|
|
40115
|
-
if (!/^\d+$/.test(value)) return void 0;
|
|
40116
|
-
const minutes = Number(value);
|
|
40117
|
-
return minutes >= MIN_BOUNDARY_MINUTES ? minutes : void 0;
|
|
40118
|
-
}
|
|
40119
|
-
function lastThreadDirectives(history) {
|
|
40120
|
-
const out = {};
|
|
40121
|
-
for (const h of history) {
|
|
40122
|
-
if (h.role !== "user") continue;
|
|
40123
|
-
for (const m of h.text.matchAll(DIRECTIVE_RE)) {
|
|
40124
|
-
if (m[1] === "model") out.model = m[2];
|
|
40125
|
-
else if (m[1] === "effort" && isEffort(m[2])) out.effort = m[2];
|
|
40126
|
-
else if (m[1] === "verbosity" && isVerbosity(m[2])) out.verbosity = m[2];
|
|
40127
|
-
}
|
|
40128
|
-
}
|
|
40129
|
-
return out;
|
|
40130
|
-
}
|
|
40131
|
-
function parseRenewals(value) {
|
|
40132
|
-
if (!/^\d+$/.test(value)) return void 0;
|
|
40133
|
-
const count = Number(value);
|
|
40134
|
-
return count <= GRANT_RENEWALS_MAX ? count : void 0;
|
|
40135
|
-
}
|
|
40136
|
-
function parseDirectives(input) {
|
|
40137
|
-
const out = { text: input };
|
|
40138
|
-
let text = input;
|
|
40139
|
-
const head = HEAD_AGENT_RE.exec(input);
|
|
40140
|
-
if (head !== null && AGENTS[head[1]] !== void 0) {
|
|
40141
|
-
out.agent = head[1];
|
|
40142
|
-
text = text.replace(head[0], " ");
|
|
40143
|
-
}
|
|
40144
|
-
for (const m of input.matchAll(DIRECTIVE_RE)) {
|
|
40145
|
-
const [token2, key, value] = m;
|
|
40146
|
-
if (key === "model") {
|
|
40147
|
-
out.model = value;
|
|
40148
|
-
} else if (key === "effort") {
|
|
40149
|
-
if (!isEffort(value)) continue;
|
|
40150
|
-
out.effort = value;
|
|
40151
|
-
} else if (key === "budget") {
|
|
40152
|
-
const minutes = parseBudgetMinutes(value);
|
|
40153
|
-
if (minutes === void 0) continue;
|
|
40154
|
-
out.budget = minutes;
|
|
40155
|
-
} else if (key === "severity") {
|
|
40156
|
-
if (!isAddressSeverity(value)) continue;
|
|
40157
|
-
out.severity = value;
|
|
40158
|
-
} else if (key === "renewals") {
|
|
40159
|
-
const count = parseRenewals(value);
|
|
40160
|
-
if (count === void 0) continue;
|
|
40161
|
-
out.renewals = count;
|
|
40162
|
-
} else {
|
|
40163
|
-
if (!isVerbosity(value)) continue;
|
|
40164
|
-
out.verbosity = value;
|
|
40165
|
-
}
|
|
40166
|
-
text = text.replace(token2, " ");
|
|
40167
|
-
}
|
|
40168
|
-
out.text = text.replace(/\s+/g, " ").trim();
|
|
40169
|
-
return out;
|
|
40170
|
-
}
|
|
40171
|
-
var DIRECTIVE_RE, HEAD_AGENT_RE;
|
|
40172
|
-
var init_directives = __esm({
|
|
40173
|
-
"../../src/directives.ts"() {
|
|
40174
|
-
"use strict";
|
|
40175
|
-
init_registry();
|
|
40176
|
-
init_validate();
|
|
40177
|
-
init_budgets();
|
|
40178
|
-
init_effort();
|
|
40179
|
-
init_shipPipeline();
|
|
40180
|
-
init_verbosity();
|
|
40181
|
-
DIRECTIVE_RE = /(?:^|\s)(model|effort|budget|severity|renewals|verbosity)[:=](\S+)/g;
|
|
40182
|
-
HEAD_AGENT_RE = /^\s*agent[:=](\S+)/;
|
|
40183
|
-
}
|
|
40184
|
-
});
|
|
40185
|
-
|
|
40186
40194
|
// ../../src/execution/githubRepoProbe.ts
|
|
40187
40195
|
function githubRepoProbe(opts) {
|
|
40188
40196
|
const fetchImpl = opts.fetch ?? fetch;
|
|
@@ -41563,10 +41571,8 @@ function presetRequestOf(line4) {
|
|
|
41563
41571
|
async function executeOperatorDecision(deps, ctx) {
|
|
41564
41572
|
const { event, io, msg } = ctx;
|
|
41565
41573
|
const surface = io.offer ? "chat" : "typed";
|
|
41566
|
-
const
|
|
41567
|
-
|
|
41568
|
-
"verbose"
|
|
41569
|
-
);
|
|
41574
|
+
const verbosity2 = deps.config.verbosityFor(msg.channelId, msg.userId, parseDirectives(msg.text).verbosity);
|
|
41575
|
+
const verbose = shows(verbosity2, "verbose");
|
|
41570
41576
|
const answered = { kind: "answered" };
|
|
41571
41577
|
if (ctx.owner?.kind === "pipeline") return { kind: "fold" };
|
|
41572
41578
|
if (event.outcome === "question") {
|
|
@@ -41673,7 +41679,7 @@ async function executeOperatorDecision(deps, ctx) {
|
|
|
41673
41679
|
if (verbose) await io.reply(receipt);
|
|
41674
41680
|
const res = await runChatCommand(deps, msg, io, invocation, ctx.ending, ctx.trace, { operator: executedEvent });
|
|
41675
41681
|
carried = true;
|
|
41676
|
-
if (res.text.length > 0) await io.
|
|
41682
|
+
if (res.text.length > 0) await replyCommandOutput(io, invocation, res.text, { verbosity: verbosity2, ok: res.ok });
|
|
41677
41683
|
}
|
|
41678
41684
|
if (!carried) await recordOperatorDecision(deps, msg, event, ctx.ending, ctx.trace);
|
|
41679
41685
|
return answered;
|
|
@@ -42352,7 +42358,7 @@ async function openAckCard(deps, ctx) {
|
|
|
42352
42358
|
});
|
|
42353
42359
|
if (route2) shell.note("debug", routeReasonLabel(route2.reason, route2.collapsed));
|
|
42354
42360
|
const card = coalesceStatus(
|
|
42355
|
-
await root.span("dispatch.ack_card", () => io.status(shell.ack())),
|
|
42361
|
+
await root.span("dispatch.ack_card", () => io.status(shell.ack(), { verbosity: resolved.verbosity })),
|
|
42356
42362
|
deps.statusUpdateMinMs ?? STATUS_UPDATE_MIN_MS
|
|
42357
42363
|
);
|
|
42358
42364
|
trace.bindCard({ setupLabel: (label) => shell.setSetupLabel(label) });
|
|
@@ -44079,7 +44085,7 @@ function watchedChild(io, on) {
|
|
|
44079
44085
|
on.replied(text);
|
|
44080
44086
|
await io.reply(text);
|
|
44081
44087
|
},
|
|
44082
|
-
status: (initial) => io.status(initial),
|
|
44088
|
+
status: (initial, display) => io.status(initial, display),
|
|
44083
44089
|
history: () => io.history(),
|
|
44084
44090
|
runStarted: (started) => {
|
|
44085
44091
|
io.runStarted?.(started);
|
|
@@ -46164,6 +46170,7 @@ async function claimRun(deps, ctx) {
|
|
|
46164
46170
|
parentInstanceId: coordinator.parentInstanceId,
|
|
46165
46171
|
...unit !== void 0 ? { unit } : {},
|
|
46166
46172
|
...coordinator.base !== void 0 ? { base: coordinator.base } : {},
|
|
46173
|
+
...coordinator.publication !== void 0 ? { publication: coordinator.publication } : {},
|
|
46167
46174
|
at: clock4()
|
|
46168
46175
|
});
|
|
46169
46176
|
}
|
|
@@ -47446,7 +47453,7 @@ function established(facts2) {
|
|
|
47446
47453
|
else if (w?.kind === "salvaged")
|
|
47447
47454
|
tree = `What the tree held was pushed to \`${w.branch}\`${w.head ? ` at \`${shortSha2(w.head)}\`` : ""} by the budget salvage, unreviewed \u2014 a follow-up starts from it.`;
|
|
47448
47455
|
else if (w?.kind === "left")
|
|
47449
|
-
tree = w.fate === "kept" ? `${counted(w)} sit in the workspace, kept for this thread until it idles out \u2014 a follow-up here reuses them.` : w.fate === "discarded" ? `${counted(w)} were left in the tree and discarded at the run's end.` : `${counted(w)} were left in the tree, which is torn down since a command may still be running in it.`;
|
|
47456
|
+
tree = w.fate === "kept" ? `${counted(w)} sit in the workspace, kept for this thread until it idles out \u2014 a follow-up here reuses them.` : w.fate === "retained_unverified" ? `${counted(w)} remain in the attached workspace after the existing-PR push was blocked; retention beyond this run is unverified, so no continuation is promised.` : w.fate === "discarded" ? `${counted(w)} were left in the tree and discarded at the run's end.` : `${counted(w)} were left in the tree, which is torn down since a command may still be running in it.`;
|
|
47450
47457
|
const description = facts2?.description === "submitted" ? "The PR description was submitted." : facts2?.description === "not_submitted" ? "No PR description was submitted." : "";
|
|
47451
47458
|
return [tree, description].filter((s) => s !== "").join(" ");
|
|
47452
47459
|
}
|
|
@@ -49979,10 +49986,28 @@ function judgeBashTimeout(timeout, ctx) {
|
|
|
49979
49986
|
return refused4(commandPastLoopEndRefusal(Math.round(timeout), Math.max(0, Math.floor(leftMs / 1e3))));
|
|
49980
49987
|
}
|
|
49981
49988
|
function judgePush(tail, ctx) {
|
|
49982
|
-
const [remote, refspec] = pushArguments(tail);
|
|
49989
|
+
const [remote, refspec, ...additionalRefspecs] = pushArguments(tail);
|
|
49983
49990
|
if (remote !== void 0 && remote !== "origin") {
|
|
49984
49991
|
return refused4(`repo:use \u2014 push to remote \`${remote}\`, not the run's repository (origin)`);
|
|
49985
49992
|
}
|
|
49993
|
+
if (ctx.publication !== void 0) {
|
|
49994
|
+
const publication = ctx.publication.authority;
|
|
49995
|
+
if ("blocked" in publication) return refused4(`repo:use \u2014 existing-PR publication blocked: ${publication.blocked}`);
|
|
49996
|
+
if (refspec === void 0)
|
|
49997
|
+
return refused4("repo:use \u2014 existing-PR publication requires the explicit owned destination");
|
|
49998
|
+
if (additionalRefspecs.length > 0)
|
|
49999
|
+
return refused4("repo:use \u2014 existing-PR publication allows exactly one owned destination");
|
|
50000
|
+
const destination2 = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
|
|
50001
|
+
const branch3 = destination2 === "HEAD" ? ctx.branch : destination2.replace(/^refs\/heads\//, "");
|
|
50002
|
+
if (branch3 !== publication.ref)
|
|
50003
|
+
return refused4(`repo:use \u2014 push to \`${branch3}\`, not the owned publication ref ${publication.ref}`);
|
|
50004
|
+
const lease = `--force-with-lease=refs/heads/${publication.ref}:${publication.expectedHeadSha}`;
|
|
50005
|
+
if (!tail.split(/\s+/).includes(lease))
|
|
50006
|
+
return refused4(
|
|
50007
|
+
`repo:use \u2014 existing-PR publication requires \`${lease}\` so concurrent movement fails atomically`
|
|
50008
|
+
);
|
|
50009
|
+
return allowed;
|
|
50010
|
+
}
|
|
49986
50011
|
if (refspec === void 0) return allowed;
|
|
49987
50012
|
const destination = refspec.replace(/^\+/, "").split(":").pop() ?? refspec;
|
|
49988
50013
|
if (ctx.branch !== void 0) {
|
|
@@ -50102,7 +50127,7 @@ function relayedToolDefinitions(harness) {
|
|
|
50102
50127
|
}
|
|
50103
50128
|
function authorizeToolCall(harness, ask) {
|
|
50104
50129
|
harness.gateSaw(ask.toolCallId);
|
|
50105
|
-
const
|
|
50130
|
+
const blocked2 = harness.toolsBlocked();
|
|
50106
50131
|
const refuse3 = (reason2) => {
|
|
50107
50132
|
harness.emit({
|
|
50108
50133
|
type: "run_note",
|
|
@@ -50111,7 +50136,7 @@ function authorizeToolCall(harness, ask) {
|
|
|
50111
50136
|
});
|
|
50112
50137
|
return { allow: false, reason: reason2 };
|
|
50113
50138
|
};
|
|
50114
|
-
if (
|
|
50139
|
+
if (blocked2 !== void 0) return refuse3(blocked2);
|
|
50115
50140
|
if (harness.tools.some((t) => t.name === ask.tool)) return { allow: true };
|
|
50116
50141
|
const verdict = judgeToolCall(ask.tool, ask.input, harness.rules);
|
|
50117
50142
|
if (verdict.verdict === "allowed") return { allow: true };
|
|
@@ -51991,6 +52016,49 @@ var init_resume = __esm({
|
|
|
51991
52016
|
}
|
|
51992
52017
|
});
|
|
51993
52018
|
|
|
52019
|
+
// ../../src/core/existingPrPublication.ts
|
|
52020
|
+
function verifyExistingPrPublication(durable, local, remote) {
|
|
52021
|
+
if (durable === void 0) return blocked("the durable existing-PR binding is missing");
|
|
52022
|
+
if (!fullSha(durable.expectedHeadSha)) return blocked("the durable expected head is not a full commit SHA");
|
|
52023
|
+
if (durable.headRef !== durable.publicationRef)
|
|
52024
|
+
return blocked("the durable head ref and owned publication ref do not agree");
|
|
52025
|
+
if (local.repo !== durable.repo) return blocked("the resolved repository does not match the durable binding");
|
|
52026
|
+
if (local.pr !== durable.pr) return blocked("the resolved pull request does not match the durable binding");
|
|
52027
|
+
if (local.ref !== durable.headRef) return blocked("the resolved head ref does not match the durable binding");
|
|
52028
|
+
if (local.baseRef !== durable.baseRef) return blocked("the resolved base ref does not match the durable binding");
|
|
52029
|
+
if (!fullSha(local.requestHeadSha)) return blocked("the request head is missing or not a full commit SHA");
|
|
52030
|
+
if (local.requestHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52031
|
+
return blocked("the request head does not match the durable expected commit");
|
|
52032
|
+
if (local.workspaceRef !== durable.publicationRef)
|
|
52033
|
+
return blocked("the checked-out publication ref does not match the durable binding");
|
|
52034
|
+
if (!fullSha(local.workspaceHeadSha)) return blocked("the checked-out head is missing or not a full commit SHA");
|
|
52035
|
+
if (local.workspaceHeadSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52036
|
+
return blocked("the checked-out head does not match the durable expected commit");
|
|
52037
|
+
if (!sameOwner(local.owner, durable.owner)) return blocked("the publication owner changed");
|
|
52038
|
+
if (remote === void 0) return blocked("the fresh pull-request read is unavailable");
|
|
52039
|
+
if (remote.state !== "open") return blocked("the bound pull request is no longer open");
|
|
52040
|
+
if (remote.sameRepoHead !== true) return blocked("the pull request head is not in the bound repository");
|
|
52041
|
+
if (remote.headBranchExists !== true) return blocked("the pull request head branch is missing or unknown");
|
|
52042
|
+
if (remote.headRef !== durable.headRef) return blocked("the pull request head ref moved");
|
|
52043
|
+
if (remote.baseRef !== durable.baseRef) return blocked("the pull request base ref moved");
|
|
52044
|
+
if (!fullSha(remote.headSha)) return blocked("the fresh pull-request head is missing or not a full commit SHA");
|
|
52045
|
+
if (remote.headSha.toLowerCase() !== durable.expectedHeadSha.toLowerCase())
|
|
52046
|
+
return blocked("the pull request head moved from the durable expected commit");
|
|
52047
|
+
return {
|
|
52048
|
+
ok: true,
|
|
52049
|
+
publication: { ref: durable.publicationRef, expectedHeadSha: durable.expectedHeadSha.toLowerCase() }
|
|
52050
|
+
};
|
|
52051
|
+
}
|
|
52052
|
+
var fullSha, sameOwner, blocked;
|
|
52053
|
+
var init_existingPrPublication = __esm({
|
|
52054
|
+
"../../src/core/existingPrPublication.ts"() {
|
|
52055
|
+
"use strict";
|
|
52056
|
+
fullSha = (value) => value !== void 0 && /^[0-9a-f]{40}$/i.test(value);
|
|
52057
|
+
sameOwner = (left, right) => left?.instanceId === right.instanceId && left.unit === right.unit;
|
|
52058
|
+
blocked = (reason2) => ({ ok: false, reason: reason2 });
|
|
52059
|
+
}
|
|
52060
|
+
});
|
|
52061
|
+
|
|
51994
52062
|
// ../../src/execution/githubComments.ts
|
|
51995
52063
|
async function postReviewComment(target, body) {
|
|
51996
52064
|
const codePoints = [...body].length;
|
|
@@ -52234,7 +52302,26 @@ async function salvageBudgetPush(executor, opts, span) {
|
|
|
52234
52302
|
}
|
|
52235
52303
|
const unpushed = parseCountOutput(await run2("git rev-list --count HEAD --not --remotes")) ?? 0;
|
|
52236
52304
|
if (!dirty && !endingCheckpoint && unpushed === 0) return { pushed: false, summary: words.nothing };
|
|
52237
|
-
|
|
52305
|
+
if (opts.publication !== void 0 && "blocked" in opts.publication)
|
|
52306
|
+
return {
|
|
52307
|
+
pushed: false,
|
|
52308
|
+
summary: `${words.failedLead}: existing-PR publication is blocked (${opts.publication.blocked}) \u2014 the commit remains unpublished in the bound workspace; no alternate ref was created`
|
|
52309
|
+
};
|
|
52310
|
+
const lease = opts.publication !== void 0 ? ` --force-with-lease=${shellQuote(`refs/heads/${opts.publication.ref}:${opts.publication.expectedHeadSha}`)}` : "";
|
|
52311
|
+
try {
|
|
52312
|
+
await run2(`git push${lease} origin ${shellQuote(`HEAD:refs/heads/${opts.branch}`)}`);
|
|
52313
|
+
} catch (err2) {
|
|
52314
|
+
if (opts.publication !== void 0) {
|
|
52315
|
+
const detail = err2 instanceof Error ? err2.message : String(err2);
|
|
52316
|
+
const publicationBlocked = `the atomic leased push was rejected: ${detail}`;
|
|
52317
|
+
return {
|
|
52318
|
+
pushed: false,
|
|
52319
|
+
publicationBlocked,
|
|
52320
|
+
summary: `${words.failedLead}: existing-PR publication is blocked (${publicationBlocked}) \u2014 the commit remains unpublished in the bound workspace; no alternate ref was created`
|
|
52321
|
+
};
|
|
52322
|
+
}
|
|
52323
|
+
throw err2;
|
|
52324
|
+
}
|
|
52238
52325
|
const head = parseRevParseOutput(await probe("git rev-parse HEAD"));
|
|
52239
52326
|
return {
|
|
52240
52327
|
pushed: true,
|
|
@@ -52961,6 +53048,12 @@ async function runLoop(deps, ctx) {
|
|
|
52961
53048
|
return void 0;
|
|
52962
53049
|
}
|
|
52963
53050
|
})();
|
|
53051
|
+
let existingPrPublication;
|
|
53052
|
+
let existingPrPublicationFence;
|
|
53053
|
+
const blockExistingPrPublication = (reason2) => {
|
|
53054
|
+
existingPrPublication = { blocked: reason2 };
|
|
53055
|
+
if (existingPrPublicationFence !== void 0) existingPrPublicationFence.authority = existingPrPublication;
|
|
53056
|
+
};
|
|
52964
53057
|
const identitySeam = deps.identityRewrite;
|
|
52965
53058
|
const startStateRead = identitySeam !== void 0 && isCodingPrRun && repoCtx.repo !== void 0 ? (async () => {
|
|
52966
53059
|
const repo = repoCtx.repo;
|
|
@@ -52997,14 +53090,14 @@ async function runLoop(deps, ctx) {
|
|
|
52997
53090
|
);
|
|
52998
53091
|
return pace ? ` \xB7 ${pace}` : "";
|
|
52999
53092
|
};
|
|
53093
|
+
const showsActivity = shows(resolved.verbosity, "verbose");
|
|
53000
53094
|
const chatty = shows(resolved.verbosity, "debug");
|
|
53001
53095
|
const showBookkeeping = shows(resolved.verbosity, "verbose");
|
|
53002
53096
|
const currentFrame = () => shell.live({
|
|
53003
|
-
|
|
53004
|
-
suffix: chatty ? `${quietSuffix(clock4() - lastActivityAt, inFlightTool)}${cardPace()}` : cardPace(),
|
|
53097
|
+
suffix: showsActivity ? chatty ? `${quietSuffix(clock4() - lastActivityAt, inFlightTool)}${cardPace()}` : cardPace() : "",
|
|
53005
53098
|
notice: shutdownNotice(),
|
|
53006
53099
|
detail: [checklist],
|
|
53007
|
-
activity: chatty ? lastActivity :
|
|
53100
|
+
activity: showsActivity ? chatty ? lastActivity : compactActivity(lastActivity) : void 0
|
|
53008
53101
|
});
|
|
53009
53102
|
const checklistAsLeft = () => checklist;
|
|
53010
53103
|
const checklistCheckedOff = () => checklist?.replace(/^(\s*)[○✱](?=\s)/gm, "$1\u2713");
|
|
@@ -53239,8 +53332,18 @@ async function runLoop(deps, ctx) {
|
|
|
53239
53332
|
});
|
|
53240
53333
|
const salvaged = "skipped" in target ? { pushed: false, summary: target.skipped } : await root.span(
|
|
53241
53334
|
cue === "budget" ? "run.budget_salvage" : "run.work_salvage",
|
|
53242
|
-
(span) => salvageBudgetPush(
|
|
53335
|
+
(span) => salvageBudgetPush(
|
|
53336
|
+
executor,
|
|
53337
|
+
{
|
|
53338
|
+
branch: target.branch,
|
|
53339
|
+
cue,
|
|
53340
|
+
...existingPrPublication !== void 0 ? { publication: existingPrPublication } : {}
|
|
53341
|
+
},
|
|
53342
|
+
span
|
|
53343
|
+
)
|
|
53243
53344
|
);
|
|
53345
|
+
if ("publicationBlocked" in salvaged && salvaged.publicationBlocked !== void 0)
|
|
53346
|
+
blockExistingPrPublication(salvaged.publicationBlocked);
|
|
53244
53347
|
const cleanCompletion = cue === "completion" && !salvaged.pushed && (/ended with nothing to preserve/i.test(salvaged.summary) || "skipped" in target && (observedUncommitted ?? 0) === 0 && (observedUnpushed ?? 0) === 0);
|
|
53245
53348
|
if (cleanCompletion) return;
|
|
53246
53349
|
workSalvageAttempted = true;
|
|
@@ -53255,7 +53358,10 @@ async function runLoop(deps, ctx) {
|
|
|
53255
53358
|
salvagedTo = { branch: target.branch, head: salvaged.head };
|
|
53256
53359
|
} else if (salvaged.pushed && !("skipped" in target)) salvagedTo = { branch: target.branch };
|
|
53257
53360
|
if (salvaged.pushed) await observeWorkspaceNow();
|
|
53258
|
-
else if ("
|
|
53361
|
+
else if (existingPrPublication !== void 0 && "blocked" in existingPrPublication) {
|
|
53362
|
+
await observeWorkspaceNow();
|
|
53363
|
+
shell.note("quiet", salvaged.summary);
|
|
53364
|
+
} else if ("skipped" in target || /failed|not attempted/i.test(salvaged.summary))
|
|
53259
53365
|
shell.note("quiet", salvaged.summary);
|
|
53260
53366
|
};
|
|
53261
53367
|
let relaunchEndedRun = false;
|
|
@@ -53281,7 +53387,9 @@ async function runLoop(deps, ctx) {
|
|
|
53281
53387
|
return true;
|
|
53282
53388
|
};
|
|
53283
53389
|
const tailSkipped = () => run2.control.requested === "hard" || relaunchEndedRun || latchReviewStoppedBeforeStart();
|
|
53390
|
+
const keepBlockedPublicationWorkspace = () => existingPrPublication !== void 0 && "blocked" in existingPrPublication && run2.control.requested !== "hard" && !commandInFlight && !gateBypassed && ((observedUncommitted ?? 0) > 0 || (observedUnpushed ?? 0) > 0);
|
|
53284
53391
|
const releaseWorkspace = (span) => {
|
|
53392
|
+
if (keepBlockedPublicationWorkspace()) return Promise.resolve();
|
|
53285
53393
|
const events2 = recordEvents();
|
|
53286
53394
|
const pushed = pushedBranchesOf(events2);
|
|
53287
53395
|
return round2.release({
|
|
@@ -53434,7 +53542,38 @@ async function runLoop(deps, ctx) {
|
|
|
53434
53542
|
if (!providerCfg)
|
|
53435
53543
|
throw new Error(`the ${harness.name} harness found no provider named ${providerName} in the config`);
|
|
53436
53544
|
const prBase = repoCtx.baseRef ?? await coordinatorBase;
|
|
53437
|
-
|
|
53545
|
+
if (coordinator?.publication !== void 0) {
|
|
53546
|
+
const publication = coordinator.publication;
|
|
53547
|
+
const fresh = await (deps.fetchPrFacts ?? fetchPullRequestFacts)({
|
|
53548
|
+
repo: publication.repo,
|
|
53549
|
+
number: publication.pr
|
|
53550
|
+
}).catch(() => void 0);
|
|
53551
|
+
const verified = verifyExistingPrPublication(
|
|
53552
|
+
publication,
|
|
53553
|
+
{
|
|
53554
|
+
repo: repoCtx.repo,
|
|
53555
|
+
pr: repoCtx.pr,
|
|
53556
|
+
ref: repoCtx.ref,
|
|
53557
|
+
baseRef: prBase,
|
|
53558
|
+
requestHeadSha: repoCtx.headSha,
|
|
53559
|
+
workspaceRef: binding?.ref,
|
|
53560
|
+
workspaceHeadSha: binding?.sha,
|
|
53561
|
+
owner: {
|
|
53562
|
+
instanceId: coordinator.parentInstanceId,
|
|
53563
|
+
unit: unitOfIdempotencyKey(coordinator.idempotencyKey) ?? ""
|
|
53564
|
+
}
|
|
53565
|
+
},
|
|
53566
|
+
fresh
|
|
53567
|
+
);
|
|
53568
|
+
existingPrPublication = verified.ok ? verified.publication : { blocked: verified.reason };
|
|
53569
|
+
existingPrPublicationFence = { authority: existingPrPublication };
|
|
53570
|
+
if (!verified.ok)
|
|
53571
|
+
shell.note(
|
|
53572
|
+
"quiet",
|
|
53573
|
+
`Existing-PR publication is blocked: ${verified.reason}. Work stays on the bound checkout; no alternate branch or pull request will be published.`
|
|
53574
|
+
);
|
|
53575
|
+
}
|
|
53576
|
+
const ownBranch = coordinator?.publication !== void 0 ? coordinator.publication.publicationRef : prBase !== void 0 ? binding?.ref ?? repoCtx.ref : void 0;
|
|
53438
53577
|
const protectedBranches = [
|
|
53439
53578
|
...new Set(
|
|
53440
53579
|
(prBase !== void 0 ? [prBase] : [binding?.ref, repoCtx.ref]).filter(
|
|
@@ -53494,8 +53633,18 @@ async function runLoop(deps, ctx) {
|
|
|
53494
53633
|
const branch2 = ownBranch;
|
|
53495
53634
|
const salvaged = await root.span(
|
|
53496
53635
|
"run.compaction_salvage",
|
|
53497
|
-
(span) => salvageBudgetPush(
|
|
53636
|
+
(span) => salvageBudgetPush(
|
|
53637
|
+
executor,
|
|
53638
|
+
{
|
|
53639
|
+
branch: branch2,
|
|
53640
|
+
cue: "compaction",
|
|
53641
|
+
...existingPrPublication !== void 0 ? { publication: existingPrPublication } : {}
|
|
53642
|
+
},
|
|
53643
|
+
span
|
|
53644
|
+
)
|
|
53498
53645
|
);
|
|
53646
|
+
if (salvaged.publicationBlocked !== void 0)
|
|
53647
|
+
blockExistingPrPublication(salvaged.publicationBlocked);
|
|
53499
53648
|
onEvent({
|
|
53500
53649
|
type: "run_note",
|
|
53501
53650
|
kind: "compaction_salvage",
|
|
@@ -53508,7 +53657,8 @@ async function runLoop(deps, ctx) {
|
|
|
53508
53657
|
rules: {
|
|
53509
53658
|
checkout: binding?.workspace ?? "/workspace",
|
|
53510
53659
|
...ownBranch !== void 0 ? { branch: ownBranch } : {},
|
|
53511
|
-
protectedBranches
|
|
53660
|
+
protectedBranches,
|
|
53661
|
+
...existingPrPublicationFence !== void 0 ? { publication: existingPrPublicationFence } : {}
|
|
53512
53662
|
},
|
|
53513
53663
|
...round2.selection.backend ? { backend: round2.selection.backend } : {},
|
|
53514
53664
|
span: root,
|
|
@@ -53724,6 +53874,12 @@ async function runLoop(deps, ctx) {
|
|
|
53724
53874
|
if (descriptionTurnRan) await preserveCodingChildWork("completion");
|
|
53725
53875
|
await endHarness();
|
|
53726
53876
|
const leftBehind = isCodingPrRun && !tailSkipped() && !workSalvageAttempted ? workLeftBehindOf({ uncommittedChanges: observedUncommitted, unpushedCommits: observedUnpushed }) : void 0;
|
|
53877
|
+
const blockedCheckpoint = keepBlockedPublicationWorkspace() && existingPrPublication !== void 0 && "blocked" in existingPrPublication && observedUncommitted !== void 0 && observedUnpushed !== void 0 && (observedUncommitted > 0 || observedUnpushed > 0) ? {
|
|
53878
|
+
reason: existingPrPublication.blocked,
|
|
53879
|
+
uncommitted: observedUncommitted,
|
|
53880
|
+
unpushed: observedUnpushed,
|
|
53881
|
+
...observedHead !== void 0 ? { head: observedHead } : {}
|
|
53882
|
+
} : void 0;
|
|
53727
53883
|
const endingFacts = () => {
|
|
53728
53884
|
if (profile.machine === "none") return { workspace: { kind: "none" } };
|
|
53729
53885
|
if (!isCodingPrRun || tailSkipped()) return { workspace: { kind: "unread" } };
|
|
@@ -53742,11 +53898,16 @@ async function runLoop(deps, ctx) {
|
|
|
53742
53898
|
kind: "left",
|
|
53743
53899
|
uncommitted: observedUncommitted,
|
|
53744
53900
|
unpushed: observedUnpushed,
|
|
53745
|
-
fate: commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
53901
|
+
fate: blockedCheckpoint !== void 0 ? "retained_unverified" : commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
53746
53902
|
};
|
|
53747
53903
|
return { workspace, description: prDescription !== void 0 ? "submitted" : "not_submitted" };
|
|
53748
53904
|
};
|
|
53749
|
-
if (
|
|
53905
|
+
if (blockedCheckpoint !== void 0) {
|
|
53906
|
+
const checkpoint = blockedCheckpoint.head ? ` at ${blockedCheckpoint.head.slice(0, 7)}` : "";
|
|
53907
|
+
const summary = `the existing-PR push is blocked (${blockedCheckpoint.reason}); kept the local checkpoint${checkpoint} with ${blockedCheckpoint.uncommitted} uncommitted change(s) and ${blockedCheckpoint.unpushed} unpushed commit(s) in the run workspace; no alternate ref or pull request was created, and retention beyond this run is unverified`;
|
|
53908
|
+
events.publish({ type: "run_note", kind: "publication_blocked", summary: oneLine(summary), at: clock4() });
|
|
53909
|
+
shell.note("quiet", `\u26A0\uFE0F ${summary}`);
|
|
53910
|
+
} else if (leftBehind) {
|
|
53750
53911
|
events.publish({
|
|
53751
53912
|
type: "run_note",
|
|
53752
53913
|
kind: "work_left_behind",
|
|
@@ -53762,7 +53923,7 @@ async function runLoop(deps, ctx) {
|
|
|
53762
53923
|
at: clock4()
|
|
53763
53924
|
});
|
|
53764
53925
|
}
|
|
53765
|
-
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted) {
|
|
53926
|
+
if (isCodingPrRun && !tailSkipped() && !endingSalvageAttempted && !(existingPrPublication !== void 0 && "blocked" in existingPrPublication)) {
|
|
53766
53927
|
const instance = coordinator && deps.coordinatorInstances ? await deps.coordinatorInstances.get(coordinator.parentInstanceId).catch(() => void 0) : void 0;
|
|
53767
53928
|
const requester = instance?.userId === msg.userId ? instance : msg;
|
|
53768
53929
|
const requestedLogin = identitySeam !== void 0 ? await identitySeam.requestedLogin(requester.userId).catch(() => void 0) : void 0;
|
|
@@ -54033,6 +54194,7 @@ var init_runLoop = __esm({
|
|
|
54033
54194
|
"use strict";
|
|
54034
54195
|
init_actor();
|
|
54035
54196
|
init_predicate();
|
|
54197
|
+
init_contract2();
|
|
54036
54198
|
init_decisionRecordReservation();
|
|
54037
54199
|
init_profile();
|
|
54038
54200
|
init_provider();
|
|
@@ -54048,6 +54210,7 @@ var init_runLoop = __esm({
|
|
|
54048
54210
|
init_resume();
|
|
54049
54211
|
init_githubPulls();
|
|
54050
54212
|
init_repoContext();
|
|
54213
|
+
init_existingPrPublication();
|
|
54051
54214
|
init_prDescription();
|
|
54052
54215
|
init_handoff();
|
|
54053
54216
|
init_reviewVerdict();
|
|
@@ -54202,7 +54365,7 @@ async function shipPreflight(input) {
|
|
|
54202
54365
|
`\u{1F6AB} ${where}'s head branch lives on a fork, not on \`${repo}\` \u2014 ship cannot drive it.`
|
|
54203
54366
|
);
|
|
54204
54367
|
}
|
|
54205
|
-
const branch2 = facts2.headRef
|
|
54368
|
+
const branch2 = facts2.headRef;
|
|
54206
54369
|
if (!branch2) {
|
|
54207
54370
|
return refuse2(
|
|
54208
54371
|
"ship_preflight_head_unknown",
|
|
@@ -54211,10 +54374,34 @@ async function shipPreflight(input) {
|
|
|
54211
54374
|
`\u{1F6AB} Could not determine ${where}'s head branch, so ship cannot bind the thread's worktree to it \u2014 refusing fail-closed.`
|
|
54212
54375
|
);
|
|
54213
54376
|
}
|
|
54214
|
-
const
|
|
54377
|
+
const headSha = facts2.headSha;
|
|
54378
|
+
if (headSha === void 0 || !/^[0-9a-f]{40}$/i.test(headSha)) {
|
|
54379
|
+
return refuse2(
|
|
54380
|
+
"ship_preflight_head_unknown",
|
|
54381
|
+
"full head commit unknown",
|
|
54382
|
+
"not started (head commit unknown)",
|
|
54383
|
+
`\u{1F6AB} Could not determine ${where}'s full head commit, so ship cannot fence publication to it \u2014 refusing fail-closed.`
|
|
54384
|
+
);
|
|
54385
|
+
}
|
|
54386
|
+
if (facts2.baseRef === void 0) {
|
|
54387
|
+
return refuse2(
|
|
54388
|
+
"ship_preflight_head_unknown",
|
|
54389
|
+
"base branch unknown",
|
|
54390
|
+
"not started (base branch unknown)",
|
|
54391
|
+
`\u{1F6AB} Could not determine ${where}'s base branch, so ship cannot bind publication to it \u2014 refusing fail-closed.`
|
|
54392
|
+
);
|
|
54393
|
+
}
|
|
54394
|
+
const ownBase = facts2.baseRef;
|
|
54215
54395
|
const ownBaseExists = ownBase !== void 0 && input.refExists ? await input.refExists(repo, ownBase).catch(() => void 0) : void 0;
|
|
54216
|
-
|
|
54217
|
-
|
|
54396
|
+
if (ownBaseExists === false) {
|
|
54397
|
+
return refuse2(
|
|
54398
|
+
"ship_preflight_head_unknown",
|
|
54399
|
+
"pull request base branch missing",
|
|
54400
|
+
"not started (base branch missing)",
|
|
54401
|
+
`\u{1F6AB} ${where}'s base branch \`${ownBase}\` is not present in \`${repo}\`, so ship cannot establish an exact publication binding \u2014 refusing fail-closed.`
|
|
54402
|
+
);
|
|
54403
|
+
}
|
|
54404
|
+
const base = resolveBaseRef([ownBase], info?.defaultBranch);
|
|
54218
54405
|
const autoMerge = facts2.autoMergeEnabled !== void 0 ? { autoMergeEnabled: facts2.autoMergeEnabled } : {};
|
|
54219
54406
|
if (task) {
|
|
54220
54407
|
return {
|
|
@@ -54223,8 +54410,7 @@ async function shipPreflight(input) {
|
|
|
54223
54410
|
repo,
|
|
54224
54411
|
branch: branch2,
|
|
54225
54412
|
base,
|
|
54226
|
-
adopt: { pr: repoCtx.pr, ...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {} },
|
|
54227
|
-
...baseFallback !== void 0 ? { baseFallback } : {},
|
|
54413
|
+
adopt: { pr: repoCtx.pr, headSha, ...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {} },
|
|
54228
54414
|
...autoMerge
|
|
54229
54415
|
}
|
|
54230
54416
|
};
|
|
@@ -54237,10 +54423,9 @@ async function shipPreflight(input) {
|
|
|
54237
54423
|
base,
|
|
54238
54424
|
resume: {
|
|
54239
54425
|
pr: repoCtx.pr,
|
|
54240
|
-
headSha
|
|
54426
|
+
headSha,
|
|
54241
54427
|
...facts2.htmlUrl !== void 0 ? { url: facts2.htmlUrl } : {}
|
|
54242
54428
|
},
|
|
54243
|
-
...baseFallback !== void 0 ? { baseFallback } : {},
|
|
54244
54429
|
...autoMerge
|
|
54245
54430
|
}
|
|
54246
54431
|
};
|
|
@@ -54432,15 +54617,27 @@ async function rowsFor(deps, p, selected, instanceId, carried) {
|
|
|
54432
54617
|
const previous = carried?.get(u.id);
|
|
54433
54618
|
const taskKey = p.recordTasks?.[u.id];
|
|
54434
54619
|
const record3 = taskKey !== void 0 ? await reserve(p.identity.repo, taskKey, previous?.record) : previous?.record;
|
|
54620
|
+
const branch2 = p.entryBranch ?? u.branch;
|
|
54621
|
+
const existing = p.adopt ?? p.resume;
|
|
54622
|
+
const publication = existing !== void 0 && p.entryBranch !== void 0 ? {
|
|
54623
|
+
repo: p.identity.repo,
|
|
54624
|
+
pr: existing.pr,
|
|
54625
|
+
headRef: p.entryBranch,
|
|
54626
|
+
baseRef: p.base,
|
|
54627
|
+
expectedHeadSha: existing.headSha,
|
|
54628
|
+
publicationRef: p.entryBranch,
|
|
54629
|
+
owner: { instanceId, unit: u.id }
|
|
54630
|
+
} : void 0;
|
|
54435
54631
|
return {
|
|
54436
54632
|
instanceId,
|
|
54437
54633
|
unit: u.id,
|
|
54438
54634
|
slug: u.slug,
|
|
54439
54635
|
title: u.title,
|
|
54440
|
-
branch:
|
|
54636
|
+
branch: branch2,
|
|
54441
54637
|
dependsOn: u.dependsOn,
|
|
54442
54638
|
rounds: [],
|
|
54443
54639
|
...p.resume !== void 0 ? { resume: p.resume } : {},
|
|
54640
|
+
...publication !== void 0 ? { publication } : {},
|
|
54444
54641
|
...previous?.lastPush !== void 0 ? { lastPush: previous.lastPush } : {},
|
|
54445
54642
|
...record3 !== void 0 ? { record: record3 } : {}
|
|
54446
54643
|
};
|
|
@@ -55006,6 +55203,7 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
55006
55203
|
...msg.authenticatedAs !== void 0 ? { authenticatedAs: msg.authenticatedAs } : {},
|
|
55007
55204
|
...msg.postedBy !== void 0 ? { postedBy: msg.postedBy } : {},
|
|
55008
55205
|
...entry.resume !== void 0 ? { pr: entry.resume.pr } : {},
|
|
55206
|
+
verbosity: ctx.verbosity,
|
|
55009
55207
|
profile
|
|
55010
55208
|
},
|
|
55011
55209
|
card: card.handle ?? null,
|
|
@@ -55349,7 +55547,13 @@ function carriedCoordinatorTag(row2, events) {
|
|
|
55349
55547
|
if (typeof parentInstanceId !== "string" || typeof idempotencyKey !== "string") return void 0;
|
|
55350
55548
|
const tag = events.find((e) => e.type === "coordinator_tag");
|
|
55351
55549
|
const base = tag?.type === "coordinator_tag" ? tag.base : void 0;
|
|
55352
|
-
|
|
55550
|
+
const publication = tag?.type === "coordinator_tag" ? tag.publication : void 0;
|
|
55551
|
+
return {
|
|
55552
|
+
parentInstanceId,
|
|
55553
|
+
idempotencyKey,
|
|
55554
|
+
...base !== void 0 ? { base } : {},
|
|
55555
|
+
...publication !== void 0 ? { publication } : {}
|
|
55556
|
+
};
|
|
55353
55557
|
}
|
|
55354
55558
|
function lostWorkspaceNote(why, restarts) {
|
|
55355
55559
|
const lost = `resumed after a restart: the run's workspace could not be re-attached (${why})`;
|
|
@@ -65712,7 +65916,8 @@ async function handle(deps, { client, statusClient }, ev, intake) {
|
|
|
65712
65916
|
span,
|
|
65713
65917
|
{
|
|
65714
65918
|
staging: deps.artifacts !== void 0,
|
|
65715
|
-
maxBytesPerMessage: deps.config.config.artifacts?.inbound?.maxBytesPerMessage ?? ARTIFACT_DEFAULTS.maxBytesPerMessage
|
|
65919
|
+
maxBytesPerMessage: deps.config.config.artifacts?.inbound?.maxBytesPerMessage ?? ARTIFACT_DEFAULTS.maxBytesPerMessage,
|
|
65920
|
+
verbosityFor: (channelId, userId, request2) => deps.config.verbosityFor(channelId, userId, request2)
|
|
65716
65921
|
},
|
|
65717
65922
|
relayAppsOf(deps),
|
|
65718
65923
|
intake
|
|
@@ -65794,10 +65999,25 @@ async function receiveSlackMessage(client, ev, span, policy, relayApps, intake)
|
|
|
65794
65999
|
threadRuns = gated.thread;
|
|
65795
66000
|
}
|
|
65796
66001
|
}
|
|
65797
|
-
|
|
65798
|
-
|
|
65799
|
-
|
|
65800
|
-
|
|
66002
|
+
const requester = await resolveSlackRequester(
|
|
66003
|
+
client,
|
|
66004
|
+
{
|
|
66005
|
+
channel: ev.channel,
|
|
66006
|
+
ts: ev.ts,
|
|
66007
|
+
threadTs: ev.threadTs,
|
|
66008
|
+
...ev.user !== void 0 ? { user: ev.user } : {},
|
|
66009
|
+
text: ev.rawText ?? ev.text,
|
|
66010
|
+
...ev.poster !== void 0 ? { poster: ev.poster } : {}
|
|
66011
|
+
},
|
|
66012
|
+
relayApps
|
|
66013
|
+
);
|
|
66014
|
+
span.setAttrs({ requester: requester.resolvedBy });
|
|
66015
|
+
const verbosity2 = policy.verbosityFor?.(`${PLATFORM3}:${ev.channel}`, requester.userId, parseDirectives(ev.text).verbosity) ?? policy.verbosity ?? DEFAULT_VERBOSITY;
|
|
66016
|
+
if (shows(verbosity2, "verbose"))
|
|
66017
|
+
client.reactions.add({ channel: ev.channel, timestamp: ev.ts, name: ACK_EMOJI }).catch((err2) => {
|
|
66018
|
+
if (!err2.message.includes("already_reacted")) console.error(`[ack] ${err2.message}`);
|
|
66019
|
+
});
|
|
66020
|
+
const delayNote = ev.caughtUp && shows(verbosity2, "verbose") ? new SlackIO(client, ev).reply(catchUpDelayNote(ev.ts, systemClock())).catch((err2) => {
|
|
65801
66021
|
console.error(`[catch-up] ${ev.channel}:${ev.ts} delay note failed: ${err2.message}`);
|
|
65802
66022
|
}) : Promise.resolve();
|
|
65803
66023
|
const [imagePass, documentPass] = await Promise.all([
|
|
@@ -65820,19 +66040,6 @@ async function receiveSlackMessage(client, ev, span, policy, relayApps, intake)
|
|
|
65820
66040
|
});
|
|
65821
66041
|
const unsupported = imagePass.skippedFiles.filter((f) => documentPass.skippedFiles.includes(f));
|
|
65822
66042
|
const { staged, skipped } = stagedFiles(unsupported, { ...policy, messageId: ev.ts });
|
|
65823
|
-
const requester = await resolveSlackRequester(
|
|
65824
|
-
client,
|
|
65825
|
-
{
|
|
65826
|
-
channel: ev.channel,
|
|
65827
|
-
ts: ev.ts,
|
|
65828
|
-
threadTs: ev.threadTs,
|
|
65829
|
-
...ev.user !== void 0 ? { user: ev.user } : {},
|
|
65830
|
-
text: ev.rawText ?? ev.text,
|
|
65831
|
-
...ev.poster !== void 0 ? { poster: ev.poster } : {}
|
|
65832
|
-
},
|
|
65833
|
-
relayApps
|
|
65834
|
-
);
|
|
65835
|
-
span.setAttrs({ requester: requester.resolvedBy });
|
|
65836
66043
|
const [channelName, userName, team] = await Promise.all([
|
|
65837
66044
|
resolveChannelName(client, ev.channel),
|
|
65838
66045
|
requester.slackUserId !== void 0 ? resolveUserName(client, requester.slackUserId) : Promise.resolve(requester.userName),
|
|
@@ -66152,6 +66359,8 @@ var init_slack = __esm({
|
|
|
66152
66359
|
init_intake();
|
|
66153
66360
|
init_thread();
|
|
66154
66361
|
init_operator();
|
|
66362
|
+
init_directives();
|
|
66363
|
+
init_verbosity();
|
|
66155
66364
|
init_confirmations();
|
|
66156
66365
|
init_threadTurns();
|
|
66157
66366
|
init_statusBudget();
|
|
@@ -66337,14 +66546,15 @@ ${file.text}`));
|
|
|
66337
66546
|
io
|
|
66338
66547
|
};
|
|
66339
66548
|
}
|
|
66340
|
-
async status(initial) {
|
|
66549
|
+
async status(initial, display = {}) {
|
|
66341
66550
|
const statusClient = this.opts.statusClient ?? this.client;
|
|
66342
66551
|
const budget = this.opts.statusBudget ?? processStatusBudget;
|
|
66343
|
-
const
|
|
66552
|
+
const shimmerVisible = shows(display.verbosity ?? DEFAULT_VERBOSITY, "verbose");
|
|
66553
|
+
const setShimmer = (status3 = LOADING_PHRASES[0]) => statusClient.assistant.threads.setStatus({
|
|
66344
66554
|
channel_id: this.ev.channel,
|
|
66345
66555
|
thread_ts: this.ev.threadTs,
|
|
66346
|
-
status:
|
|
66347
|
-
loading_messages: LOADING_PHRASES
|
|
66556
|
+
status: status3,
|
|
66557
|
+
...status3 === "" ? {} : { loading_messages: LOADING_PHRASES }
|
|
66348
66558
|
}).catch((err2) => console.error(`[shimmer] ${err2.message}`));
|
|
66349
66559
|
let ts;
|
|
66350
66560
|
if (this.opts.existingCard) {
|
|
@@ -66369,11 +66579,17 @@ ${file.text}`));
|
|
|
66369
66579
|
liveCards.add(liveCardKey(this.ev.channel, ts));
|
|
66370
66580
|
const card = `${this.ev.channel}:${ts}`;
|
|
66371
66581
|
const shimmerKey = `${this.ev.channel}:${this.ev.threadTs}`;
|
|
66372
|
-
|
|
66373
|
-
|
|
66374
|
-
|
|
66375
|
-
|
|
66376
|
-
|
|
66582
|
+
let shimmerTimer;
|
|
66583
|
+
if (shimmerVisible) {
|
|
66584
|
+
shimmerOwners.set(shimmerKey, card);
|
|
66585
|
+
await setShimmer();
|
|
66586
|
+
shimmerTimer = setInterval(() => {
|
|
66587
|
+
if (shimmerOwners.get(shimmerKey) === card) void setShimmer();
|
|
66588
|
+
}, 75e3);
|
|
66589
|
+
} else {
|
|
66590
|
+
shimmerOwners.delete(shimmerKey);
|
|
66591
|
+
await setShimmer("");
|
|
66592
|
+
}
|
|
66377
66593
|
budget.open(card);
|
|
66378
66594
|
const edit = (frame2) => statusClient.chat.update({ channel: this.ev.channel, ts, ...render2(frame2) });
|
|
66379
66595
|
let dropped = 0;
|
|
@@ -66402,7 +66618,7 @@ ${file.text}`));
|
|
|
66402
66618
|
});
|
|
66403
66619
|
},
|
|
66404
66620
|
done: async (frame2) => {
|
|
66405
|
-
clearInterval(shimmerTimer);
|
|
66621
|
+
if (shimmerTimer !== void 0) clearInterval(shimmerTimer);
|
|
66406
66622
|
liveCards.delete(liveCardKey(this.ev.channel, ts));
|
|
66407
66623
|
budget.close(card);
|
|
66408
66624
|
const waitMs = budget.takeTerminal(this.ev.channel);
|
|
@@ -66414,7 +66630,7 @@ ${file.text}`));
|
|
|
66414
66630
|
if (dropped > 0) console.log(`[slack] card ${card}: ${dropped} progress frames dropped by the status budget`);
|
|
66415
66631
|
if (shimmerOwners.get(shimmerKey) !== card) return;
|
|
66416
66632
|
shimmerOwners.delete(shimmerKey);
|
|
66417
|
-
await
|
|
66633
|
+
await setShimmer("").catch(() => {
|
|
66418
66634
|
});
|
|
66419
66635
|
}
|
|
66420
66636
|
};
|
|
@@ -71187,7 +71403,12 @@ async function launchResumes(deps, resumable, opts) {
|
|
|
71187
71403
|
startedAt: row2.startedAt,
|
|
71188
71404
|
now: deps.clock ?? Date.now
|
|
71189
71405
|
});
|
|
71190
|
-
|
|
71406
|
+
const verbosity2 = row2.meta.verbosity ?? deps.config.verbosityFor(
|
|
71407
|
+
row2.meta.channelId,
|
|
71408
|
+
row2.meta.userId,
|
|
71409
|
+
parseDirectives(inputTextOf(run2.events)).verbosity
|
|
71410
|
+
);
|
|
71411
|
+
await io2.status(shell.live({ detail: [rehostCardDetail(run2)] }), { verbosity: verbosity2 });
|
|
71191
71412
|
} catch (err2) {
|
|
71192
71413
|
warn(
|
|
71193
71414
|
`[resume] ${row2.runId} ${row2.threadKey}: restart card could not be written: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
@@ -71266,6 +71487,7 @@ var PI_READ_ONLY_TOOLS;
|
|
|
71266
71487
|
var init_resumeLaunch = __esm({
|
|
71267
71488
|
"../../src/core/resumeLaunch.ts"() {
|
|
71268
71489
|
"use strict";
|
|
71490
|
+
init_directives();
|
|
71269
71491
|
init_inboxMessage();
|
|
71270
71492
|
init_resume();
|
|
71271
71493
|
init_runRegistry();
|
|
@@ -72242,7 +72464,7 @@ function watched(io, on) {
|
|
|
72242
72464
|
on.replied(text);
|
|
72243
72465
|
await io.reply(text);
|
|
72244
72466
|
},
|
|
72245
|
-
status: (initial) => io.status(initial),
|
|
72467
|
+
status: (initial, display) => io.status(initial, display),
|
|
72246
72468
|
history: () => io.history(),
|
|
72247
72469
|
runStarted: (started) => {
|
|
72248
72470
|
io.runStarted?.(started);
|
|
@@ -72316,6 +72538,33 @@ The thread since the last step:
|
|
|
72316
72538
|
${foldThreadEvents(folded)}` };
|
|
72317
72539
|
}
|
|
72318
72540
|
const carried = foldThreadAttachments(folded);
|
|
72541
|
+
const existingPrCodingTarget = req.preset === "coding" && row2 !== void 0 && (row2.publication !== void 0 || row2.pr !== void 0 || row2.resume !== void 0 || parsePlanBranch(row2.branch) === void 0);
|
|
72542
|
+
if (existingPrCodingTarget) {
|
|
72543
|
+
if (row2.publication === void 0)
|
|
72544
|
+
return json3(409, {
|
|
72545
|
+
ok: false,
|
|
72546
|
+
error: "publication_binding_missing",
|
|
72547
|
+
message: "the existing pull request has no durable publication binding",
|
|
72548
|
+
at
|
|
72549
|
+
});
|
|
72550
|
+
try {
|
|
72551
|
+
const owner = deps.runnerOwnership?.owner(instance.repo, row2.publication.pr);
|
|
72552
|
+
if (owner === void 0 || owner.instanceId !== row2.publication.owner.instanceId || owner.unit !== row2.publication.owner.unit)
|
|
72553
|
+
return json3(409, {
|
|
72554
|
+
ok: false,
|
|
72555
|
+
error: "publication_ownership_changed",
|
|
72556
|
+
message: "the durable existing-PR publication owner is no longer the runner's sole owner",
|
|
72557
|
+
at
|
|
72558
|
+
});
|
|
72559
|
+
} catch (err2) {
|
|
72560
|
+
return json3(409, {
|
|
72561
|
+
ok: false,
|
|
72562
|
+
error: "publication_ownership_unknown",
|
|
72563
|
+
message: describe12(err2),
|
|
72564
|
+
at
|
|
72565
|
+
});
|
|
72566
|
+
}
|
|
72567
|
+
}
|
|
72319
72568
|
const msg = {
|
|
72320
72569
|
channelId: instance.channelId,
|
|
72321
72570
|
userId: instance.userId,
|
|
@@ -72357,10 +72606,16 @@ ${foldThreadEvents(folded)}` };
|
|
|
72357
72606
|
lastReply = text;
|
|
72358
72607
|
}
|
|
72359
72608
|
});
|
|
72609
|
+
const roundExpectedHead = req.brief?.kind === "findings" || req.brief?.kind === "rebase" ? req.brief.headSha : req.brief?.kind === "contract" ? req.brief.continue?.from : void 0;
|
|
72610
|
+
const publication = req.preset === "coding" && row2?.publication !== void 0 ? {
|
|
72611
|
+
...row2.publication,
|
|
72612
|
+
...roundExpectedHead !== void 0 ? { expectedHeadSha: roundExpectedHead } : {}
|
|
72613
|
+
} : void 0;
|
|
72360
72614
|
const tag = {
|
|
72361
72615
|
parentInstanceId: instance.id,
|
|
72362
72616
|
idempotencyKey: key,
|
|
72363
|
-
...instance.base !== void 0 ? { base: instance.base } : {}
|
|
72617
|
+
...instance.base !== void 0 ? { base: instance.base } : {},
|
|
72618
|
+
...publication !== void 0 ? { publication } : {}
|
|
72364
72619
|
};
|
|
72365
72620
|
let dispatching;
|
|
72366
72621
|
try {
|
|
@@ -72851,9 +73106,19 @@ async function prCheck(body, deps) {
|
|
|
72851
73106
|
const unit = await unitRowOf(deps, instance, body.unit);
|
|
72852
73107
|
if (!unit.ok) return unit.response;
|
|
72853
73108
|
const branch2 = unit.row?.branch ?? instance.branch;
|
|
72854
|
-
const remember = async (pr) => {
|
|
72855
|
-
if (unit.row
|
|
72856
|
-
|
|
73109
|
+
const remember = async (pr, facts2) => {
|
|
73110
|
+
if (!unit.row) return;
|
|
73111
|
+
const publication = unit.row.publication ?? (facts2?.state === "open" && facts2.sameRepoHead === true && facts2.headBranchExists === true && typeof facts2.headRef === "string" && typeof facts2.baseRef === "string" && typeof facts2.headSha === "string" && /^[0-9a-f]{40}$/i.test(facts2.headSha) ? {
|
|
73112
|
+
repo: instance.repo,
|
|
73113
|
+
pr: pr.number,
|
|
73114
|
+
headRef: facts2.headRef,
|
|
73115
|
+
baseRef: facts2.baseRef,
|
|
73116
|
+
expectedHeadSha: facts2.headSha,
|
|
73117
|
+
publicationRef: facts2.headRef,
|
|
73118
|
+
owner: { instanceId: instance.id, unit: unit.row.unit }
|
|
73119
|
+
} : void 0);
|
|
73120
|
+
if (unit.row.pr?.number !== pr.number || unit.row.pr.url !== pr.url || unit.row.publication === void 0 && publication !== void 0)
|
|
73121
|
+
await deps.instances.putUnits([{ ...unit.row, pr, ...publication !== void 0 ? { publication } : {} }]);
|
|
72857
73122
|
};
|
|
72858
73123
|
try {
|
|
72859
73124
|
if (follow !== void 0) {
|
|
@@ -72862,8 +73127,12 @@ async function prCheck(body, deps) {
|
|
|
72862
73127
|
const fallbackUrl = unit.row?.pr?.url ?? `https://github.com/${instance.repo}/pull/${follow}`;
|
|
72863
73128
|
const state2 = pullRequestState(follow, fallbackUrl, facts2);
|
|
72864
73129
|
if (state2 === void 0) throw new Error(headBranchStateError(instance.repo, follow));
|
|
72865
|
-
await remember({ number: follow, url: String(state2.url) });
|
|
73130
|
+
await remember({ number: follow, url: String(state2.url) }, facts2);
|
|
72866
73131
|
if (state2.state !== "open") return json3(200, { ok: true, ...state2, at });
|
|
73132
|
+
deps.runnerOwnership?.claim(instance.repo, follow, {
|
|
73133
|
+
instanceId: instance.id,
|
|
73134
|
+
unit: unit.row?.unit ?? body.unit
|
|
73135
|
+
});
|
|
72867
73136
|
const prRef = { repo: instance.repo, number: follow };
|
|
72868
73137
|
const headSha = facts2.headSha;
|
|
72869
73138
|
const approved = entry && headSha !== void 0 ? await reviewPostedAt(deps, prRef, "approve", headSha) : void 0;
|
|
@@ -72894,13 +73163,13 @@ async function prCheck(body, deps) {
|
|
|
72894
73163
|
}
|
|
72895
73164
|
const open2 = await deps.findOpenPrByHead(instance.repo, branch2);
|
|
72896
73165
|
if (open2) {
|
|
72897
|
-
await remember({ number: open2.number, url: open2.htmlUrl });
|
|
72898
73166
|
deps.runnerOwnership?.claim(instance.repo, open2.number, {
|
|
72899
73167
|
instanceId: instance.id,
|
|
72900
73168
|
unit: unit.row?.unit ?? body.unit
|
|
72901
73169
|
});
|
|
72902
73170
|
const liveFacts = await deps.fetchPrFacts({ repo: instance.repo, number: open2.number });
|
|
72903
73171
|
if (liveFacts === void 0) throw new Error(`could not read ${instance.repo}#${open2.number}`);
|
|
73172
|
+
await remember({ number: open2.number, url: open2.htmlUrl }, liveFacts);
|
|
72904
73173
|
const current = pullRequestState(open2.number, open2.htmlUrl, liveFacts);
|
|
72905
73174
|
if (current === void 0) throw new Error(headBranchStateError(instance.repo, open2.number));
|
|
72906
73175
|
if (current.state !== "open") return json3(200, { ok: true, ...current, at });
|
|
@@ -73209,12 +73478,13 @@ async function drawCard(deps, instance, units, close) {
|
|
|
73209
73478
|
source: instance.addressSeveritySource ?? "org"
|
|
73210
73479
|
} : void 0
|
|
73211
73480
|
);
|
|
73481
|
+
const display = { verbosity: instance.verbosity ?? DEFAULT_VERBOSITY };
|
|
73212
73482
|
if (!close) {
|
|
73213
|
-
await io.status(shell.live({ detail }));
|
|
73483
|
+
await io.status(shell.live({ detail }), display);
|
|
73214
73484
|
return;
|
|
73215
73485
|
}
|
|
73216
73486
|
shell.freeze(clock4());
|
|
73217
|
-
const handle2 = await io.status(shell.live({ detail }));
|
|
73487
|
+
const handle2 = await io.status(shell.live({ detail }), display);
|
|
73218
73488
|
await handle2.done(shell.close({ kind: "done", icon: close.icon, detail: detail.join("\n") }));
|
|
73219
73489
|
}
|
|
73220
73490
|
function parseGate(raw) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreplane/switchboard",
|
|
3
|
-
"version": "1.261.
|
|
3
|
+
"version": "1.261.2",
|
|
4
4
|
"description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://openswitchboard.dev",
|