@coreplane/switchboard 1.246.0 → 1.247.0
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/config/config.example.yaml +1 -1
- package/dist/assets/deploy/cloudflare-memory/worker.ts +14 -4
- package/dist/assets/deploy/cloudflare-resident/worker.ts +1 -1
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +5 -3
- package/dist/assets/project.json +10 -2
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +1 -1
- package/dist/assets/src/core/budgets.ts +19 -7
- package/dist/assets/src/core/runEvents.ts +5 -1
- package/dist/assets/src/core/ship/contract.ts +7 -0
- package/dist/assets/src/core/ship/coordinator.ts +1 -1
- package/dist/assets/src/core/trace/attrs.ts +9 -0
- package/dist/assets/web/dist/.vite/manifest.json +474 -485
- package/dist/assets/web/dist/assets/AppShell-DAtHiuI6.js +1 -0
- package/dist/assets/web/dist/assets/CostsPage-BoKzYa4B.js +2 -0
- package/dist/assets/web/dist/assets/DeliveryPage-NP4g6bQd.js +1 -0
- package/dist/assets/web/dist/assets/HomePage-DYxC0izY.js +2 -0
- package/dist/assets/web/dist/assets/InputMenu-C6aPpe30.js +1 -0
- package/dist/assets/web/dist/assets/NotFoundPage-DtE-GgTk.js +1 -0
- package/dist/assets/web/dist/assets/PendingTurnRow-BuRre8it.js +1 -0
- package/dist/assets/web/dist/assets/ResidentDetailPage-DLIpWYOc.js +1 -0
- package/dist/assets/web/dist/assets/ResidentsIndexPage-6LipuDjR.js +1 -0
- package/dist/assets/web/dist/assets/RunFoldRow-V-iSy64e.js +1 -0
- package/dist/assets/web/dist/assets/RunRoutePage-DUalB1u2.js +9 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-B9Ba1KdD.js +1 -0
- package/dist/assets/web/dist/assets/RunsTabs-YSbUu5py.js +1 -0
- package/dist/assets/web/dist/assets/ScheduledPage-KdjLtD_7.js +1 -0
- package/dist/assets/web/dist/assets/SettingSelect-BIzAsLk1.js +1 -0
- package/dist/assets/web/dist/assets/SettingsPage-IT5l_NaL.js +1 -0
- package/dist/assets/web/dist/assets/StatusDot-DBHAl4Il.js +1 -0
- package/dist/assets/web/dist/assets/Tooltip-_LEjptLV.js +1 -0
- package/dist/assets/web/dist/assets/UnitRoutePage-DPBsvGPR.js +1 -0
- package/dist/assets/web/dist/assets/{angular-html-D_3slZqf.js → angular-html-BjeQZdCq.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-D1Fv2cpN.js → angular-ts-ZGdORrL2.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-60Nm_trN.js → apl-DhvV_X93.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-CvyaM3Q9.js → astro-DiRSE4Ug.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-yan3VBls.js → blade-D8Fr5Wvq.js} +1 -1
- package/dist/assets/web/dist/assets/{seed-BhxEVasY.js → browser-B6T5hjfR.js} +1 -1
- package/dist/assets/web/dist/assets/{c-Ddgepap2.js → c-BwbC64D4.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-CgNY6dxC.js → chapel-DgmqRK97.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-C7q82cdP.js → cobol-DJod2RNK.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-DgRMF_ni.js → coffee-D1Y2CgBY.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-B8Lf-aeo.js → cpp-CsO97YOM.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-CkA_dhEO.js → crystal-CJ7m2tOc.js} +1 -1
- package/dist/assets/web/dist/assets/{css-Cyu-C_oA.js → css-B2M-NKoj.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-DS-gMjLZ.js → dist-BcYPGOBL.js} +2 -2
- package/dist/assets/web/dist/assets/durationTone-U_rOqo6r.js +1 -0
- package/dist/assets/web/dist/assets/{edge-BxV3rKNI.js → edge-CFVW-m9B.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-pNSEK39d.js → elixir-B2mtRgCr.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-zgwWE33W.js → elm-2teVAwCi.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-yzcYAHp_.js → erb-LAbeNqen.js} +1 -1
- package/dist/assets/web/dist/assets/eventSource-Bj_dZf6M.js +1 -0
- package/dist/assets/web/dist/assets/{git-rebase-Dg7Ous1-.js → git-rebase-DnhzH_sp.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-CbZEwX27.js → glimmer-js-DPKjXf1-.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-D3NIqALy.js → glimmer-ts-CABXnt5z.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-y4-3WrI2.js → glsl-Dz4-3-gJ.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-D5d0al7K.js → graphql-ByWUD1DL.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-D8fD6kEl.js → hack-CdmOK2K-.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-BWeFFc-V.js → haml-DzegMpd2.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-gIm5FeE_.js → handlebars-B5NeiP0e.js} +1 -1
- package/dist/assets/web/dist/assets/{html-fXJTVLSM.js → html-Zg_NHLv9.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-B3ySSBrQ.js → html-derivative-DZf9JNzh.js} +1 -1
- package/dist/assets/web/dist/assets/{http-DrJfQUmw.js → http-CVFrQZeN.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-Bvbnsf55.js → hurl-L5brWAJe.js} +1 -1
- package/dist/assets/web/dist/assets/{java-CUSpb-i-.js → java-hFphyZ4R.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-BWRH5fbY.js → javascript-CAJO1WKX.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-D3Ymci8y.js → jinja-N4A4srXI.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-Gt9SO7ZC.js → jison-D3BZa_iU.js} +1 -1
- package/dist/assets/web/dist/assets/{json-Cfcblhgn.js → json-DxD1Qh8W.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-C0366fFM.js → jsx-CHhag69S.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-CyBOTbxS.js → julia-CrhkZ6Tt.js} +1 -1
- package/dist/assets/web/dist/assets/{just-FPXZcIOd.js → just-D0LFOuhq.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-Bd-c88v0.js → latex-BHAlwCXK.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-5tu9Hc0k.js → liquid-CkaLfCWh.js} +1 -1
- package/dist/assets/web/dist/assets/{lua-BZ5fdPP8.js → lua-B1h4aAzP.js} +1 -1
- package/dist/assets/web/dist/assets/main-DUfSE0dj.js +28 -0
- package/dist/assets/web/dist/assets/main-zbP_dTjR.css +1 -0
- package/dist/assets/web/dist/assets/{marko-C_mmj4p0.js → marko-D6CG4hvX.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-CtaNqWjY.js → mdc-CVQzimAU.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-Ddm5Mb8J.js → nginx-h-9Ir54h.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-COhfroYB.js → nim-DHVI30sp.js} +1 -1
- package/dist/assets/web/dist/assets/{org-CCHFgmTb.js → org-AasAXfMF.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-CXzOE-Z-.js → perl-BVi5aTEy.js} +1 -1
- package/dist/assets/web/dist/assets/{php-1V6jAIhU.js → php-qOkCeUKr.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-uS37efts.js → pug-8TnUGvEX.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-Bj1A0Ben.js → qml-CpJbhoxn.js} +1 -1
- package/dist/assets/web/dist/assets/{r-CMbKbLsS.js → r-JkUNcAJW.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-DVRq_Ndk.js → razor-CtU4ws5a.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-AbaOr4oB.js → regexp-J2zA6ceN.js} +1 -1
- package/dist/assets/web/dist/assets/{rst-Dq_QWgOF.js → rst-CMCvf-yZ.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-CVSsAj7T.js → ruby-ChLBY_iR.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-C-dGfxUi.js → sas-cwn4x2VY.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-DxALxYPA.js → scss-jSrJlzLI.js} +1 -1
- package/dist/assets/web/dist/assets/{shellscript--qXcvQ5L.js → shellscript-D5nAd0gs.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-DeqnW8Ux.js → shellsession-BZY4-Tdh.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-DreoUIMt.js → soy-COppPOKr.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-o3i2VTG6.js → sql-Blc_6dOA.js} +1 -1
- package/dist/assets/web/dist/assets/{sseReplay-qF-gpbAY.js → sseReplay-DXC7kGbN.js} +3 -3
- package/dist/assets/web/dist/assets/{stata-CGAT41H7.js → stata-DRv3OY-h.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-CBU0QuGV.js → surrealql-qMv_eeJ6.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-CfHaLZiG.js → svelte-d1Edb6Rz.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-D02MUcma.js → templ-CUMSIOXG.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-DjprHYXl.js → tex-BGZQA-Ei.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-DftDOzP-.js → ts-tags-Z9ulxVsq.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-BOdvPohJ.js → tsx-oI4bI3Vf.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-B_yLS56B.js → twig-D_RXwBIX.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-CdPCa4nU.js → typescript-BgqQDYoN.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-C1oIFsUl.js → typst-5MW-KRDd.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-CcszJEX8.js → vue-CZhHHsPm.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-DcpxPcuT.js → vue-html-DlYy2d8v.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-D26bIKGj.js → vue-vine-CJDnx-qn.js} +1 -1
- package/dist/assets/web/dist/assets/wallClock-DmaC1sKA.js +1 -0
- package/dist/assets/web/dist/assets/{xml-D8zl6W5P.js → xml-BRBDk65Q.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-D86Bii28.js → xsl-BlCykPxo.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-g6Px4KFU.js → yaml-BqW48szf.js} +1 -1
- package/dist/cli.js +1120 -672
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/AppShell-B4EUiSwP.js +0 -1
- package/dist/assets/web/dist/assets/CostsPage-D8VjAtuH.js +0 -2
- package/dist/assets/web/dist/assets/DeliveryPage-DpQi5XUI.js +0 -1
- package/dist/assets/web/dist/assets/HomePage-CvKuIGqg.js +0 -2
- package/dist/assets/web/dist/assets/InputMenu-DmqAxkn4.js +0 -1
- package/dist/assets/web/dist/assets/NotFoundPage-DDogyiXA.js +0 -1
- package/dist/assets/web/dist/assets/PendingTurnRow-BoSLlR6j.js +0 -1
- package/dist/assets/web/dist/assets/ResidentDetailPage-CiBdbzkE.js +0 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-DgtHtd9K.js +0 -1
- package/dist/assets/web/dist/assets/RunFoldRow-IF4xbz-3.js +0 -1
- package/dist/assets/web/dist/assets/RunRoutePage-DHYa7rHK.js +0 -9
- package/dist/assets/web/dist/assets/RunsIndexPage-aWhNTv8r.js +0 -1
- package/dist/assets/web/dist/assets/RunsTabs-CY_tdGTU.js +0 -1
- package/dist/assets/web/dist/assets/ScheduledPage-gPEBZjcx.js +0 -1
- package/dist/assets/web/dist/assets/SettingSelect-VDanrTZi.js +0 -1
- package/dist/assets/web/dist/assets/SettingsPage-CFoltSRD.js +0 -1
- package/dist/assets/web/dist/assets/StatusDot-DA3eTVrT.js +0 -1
- package/dist/assets/web/dist/assets/Tooltip-BgbCkM12.js +0 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-DibegaJJ.js +0 -1
- package/dist/assets/web/dist/assets/durationTone-l6aVrHb8.js +0 -1
- package/dist/assets/web/dist/assets/eventSource-Ce94ho6M.js +0 -1
- package/dist/assets/web/dist/assets/favicon-D1hkyC5j.js +0 -1
- package/dist/assets/web/dist/assets/main-B4A6x82d.js +0 -28
- package/dist/assets/web/dist/assets/main-CpkwzHX6.css +0 -1
- package/dist/assets/web/dist/assets/residentsModel-By1fThvk.js +0 -1
- package/dist/assets/web/dist/assets/wallClock-DPTuP2FD.js +0 -1
package/dist/cli.js
CHANGED
|
@@ -1204,6 +1204,22 @@ var init_grants = __esm({
|
|
|
1204
1204
|
}
|
|
1205
1205
|
});
|
|
1206
1206
|
|
|
1207
|
+
// ../../src/core/authz/viewAs.ts
|
|
1208
|
+
function holdsAll(actor) {
|
|
1209
|
+
return actor.grants.actions === "all" && actor.grants.channels === "all" && actor.grants.repos === "all";
|
|
1210
|
+
}
|
|
1211
|
+
function isViewablePerson(id) {
|
|
1212
|
+
return /^slack:U[A-Z0-9]+$/.test(id);
|
|
1213
|
+
}
|
|
1214
|
+
function viewingRefusal(person) {
|
|
1215
|
+
return `You are viewing as ${person.name ?? person.id}; writes are your own to make \u2014 exit view-as to write.`;
|
|
1216
|
+
}
|
|
1217
|
+
var init_viewAs = __esm({
|
|
1218
|
+
"../../src/core/authz/viewAs.ts"() {
|
|
1219
|
+
"use strict";
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1207
1223
|
// ../../src/configDocument.ts
|
|
1208
1224
|
import { createHash } from "node:crypto";
|
|
1209
1225
|
function parseConfigLocation(value) {
|
|
@@ -1563,16 +1579,16 @@ var init_budgets = __esm({
|
|
|
1563
1579
|
LOOP_PRESETS = ["general", "coding", "review", "research", "explore", "conductor"];
|
|
1564
1580
|
ASKS = {
|
|
1565
1581
|
general: 5,
|
|
1566
|
-
coding:
|
|
1582
|
+
coding: 90,
|
|
1567
1583
|
review: 25,
|
|
1568
|
-
ship:
|
|
1584
|
+
ship: 240,
|
|
1569
1585
|
research: 8,
|
|
1570
1586
|
explore: 120,
|
|
1571
1587
|
conductor: 120
|
|
1572
1588
|
};
|
|
1573
1589
|
PRESET_FLOORS = {
|
|
1574
1590
|
general: 2,
|
|
1575
|
-
coding:
|
|
1591
|
+
coding: 15,
|
|
1576
1592
|
review: 5,
|
|
1577
1593
|
research: 3,
|
|
1578
1594
|
explore: 15,
|
|
@@ -1923,7 +1939,7 @@ ${renderGuards(contract.guards)}`
|
|
|
1923
1939
|
}
|
|
1924
1940
|
return { text, chars: text.length, dropped, overBudget: text.length > maxChars };
|
|
1925
1941
|
}
|
|
1926
|
-
var PR_TITLE_GUARD, GUARDS, CONTRACT_HEADING, CONTRACT_SECTION_HEADINGS, DEFAULT_CONTRACT_MAX_CHARS, UNIT_HEADING, ANY_HEADING, BULLET, SPEC_MENTION, ITEM_LIST, NAMED_REF, ANY_MD_MENTION, SPEC_ITEM, TITLE_MAX, APPROACH_DROPPED, DROP_NOTE;
|
|
1942
|
+
var PR_TITLE_GUARD, GUARDS, CONTRACT_HEADING, CONTRACT_SECTION_HEADINGS, DEFAULT_CONTRACT_MAX_CHARS, PLAN_MAX_CHARS, UNIT_HEADING, ANY_HEADING, BULLET, SPEC_MENTION, ITEM_LIST, NAMED_REF, ANY_MD_MENTION, SPEC_ITEM, TITLE_MAX, APPROACH_DROPPED, DROP_NOTE;
|
|
1927
1943
|
var init_contract = __esm({
|
|
1928
1944
|
"../../src/core/ship/contract.ts"() {
|
|
1929
1945
|
"use strict";
|
|
@@ -1959,6 +1975,7 @@ var init_contract = __esm({
|
|
|
1959
1975
|
guards: "### Guards"
|
|
1960
1976
|
};
|
|
1961
1977
|
DEFAULT_CONTRACT_MAX_CHARS = 48e3;
|
|
1978
|
+
PLAN_MAX_CHARS = 2e6;
|
|
1962
1979
|
UNIT_HEADING = /^### (U\d+)\. (.+?)\s*$/;
|
|
1963
1980
|
ANY_HEADING = /^#{2,3} /;
|
|
1964
1981
|
BULLET = /^- \*\*([^*]+)\*\*:(?:\s?(.*))?$/;
|
|
@@ -2038,7 +2055,7 @@ var init_registry = __esm({
|
|
|
2038
2055
|
init_contract();
|
|
2039
2056
|
MACHINE_CLASSES = ["none", "blank", "repo-cold", "repo-resident"];
|
|
2040
2057
|
IDENTITIES = ["none", "read", "write"];
|
|
2041
|
-
PR_DESCRIPTION_TEMPLATE = `PR description \u2014 submit it with the submit_pr_description tool for EVERY PR (this is the default, not something to wait to be asked for). Switchboard renders the GitHub body from the object you submit, so never author PR-body markdown yourself. Before submitting, judge your title with the ${PR_TITLE_GUARD} gate \u2014 \`npm run check:pr-title -- "<title>"\` \u2014 and submit only a title it accepts; the same gate refuses the PR in CI. The body is a fixed-size MAP for the reader with everything for agents collapsed under it; every field is capped in visible characters (a link's URL is not counted) and the tool refuses an object over a cap naming the field and the count \u2014 cut and resubmit. Prose is unwrapped \u2014 no hard line breaks inside a paragraph. Always hyperlink the triggering issue/request. Never fabricate validation \u2014 state exactly what you ran and the real result. BEFORE authoring the pointers, load the \`pr-description\` skill with use_skill \u2014 it defines how to choose at most seven pointers, the mechanical anchor rules and what goes below the fold; follow it for every PR.
|
|
2058
|
+
PR_DESCRIPTION_TEMPLATE = `PR description \u2014 submit it with the submit_pr_description tool for EVERY PR (this is the default, not something to wait to be asked for). Switchboard renders the GitHub body from the object you submit, so never author PR-body markdown yourself. Before submitting, judge your title with the ${PR_TITLE_GUARD} gate \u2014 \`npm run check:pr-title -- "<title>"\` \u2014 and submit only a title it accepts; the same gate refuses the PR in CI, and on Switchboard's own repository the tool refuses the same titles with the gate's own sentence, so fix the title and resubmit rather than pushing on. The body is a fixed-size MAP for the reader with everything for agents collapsed under it; every field is capped in visible characters (a link's URL is not counted) and the tool refuses an object over a cap naming the field and the count \u2014 cut and resubmit. Prose is unwrapped \u2014 no hard line breaks inside a paragraph. Always hyperlink the triggering issue/request. Never fabricate validation \u2014 state exactly what you ran and the real result. BEFORE authoring the pointers, load the \`pr-description\` skill with use_skill \u2014 it defines how to choose at most seven pointers, the mechanical anchor rules and what goes below the fold; follow it for every PR.
|
|
2042
2059
|
EVERY PR includes one that already exists when you push \u2014 opened by a person, by dependabot, or by an earlier run. After EVERY push to such a PR: read its current title and body (\`github_issue_get\` with the PR number works for pull requests; \`gh pr view\` where gh exists), judge them against the change as it now stands at the pushed head, and submit the object that describes the PR as it is NOW \u2014 carry forward what the existing body says that is still true (a dependency bump's release notes belong in why), add what you changed, and anchor the pointers at the new head. Switchboard replaces the PR's title and body with your rendering. A description that describes an earlier state of its branch is a bug; "it is someone else's PR" is never a reason to leave it.
|
|
2043
2060
|
- **title** (\u226472 characters in all): the changelog line \u2014 \`type(scope): what a reader can now do or expect\`, one change, one clause, present tense. The type and the scope spend the same budget as the description, so the description is short; the body carries the rest. The tool refuses a longer title naming the count, and so does the gate in CI.
|
|
2044
2061
|
- **TL;DR** (\`tldr\`, rendered first, \u2264300): two sentences for a reader with zero context \u2014 what this PR does and why it matters.
|
|
@@ -4451,6 +4468,7 @@ var init_config2 = __esm({
|
|
|
4451
4468
|
"use strict";
|
|
4452
4469
|
init_authorize();
|
|
4453
4470
|
init_grants();
|
|
4471
|
+
init_viewAs();
|
|
4454
4472
|
init_configDocument();
|
|
4455
4473
|
init_schedules();
|
|
4456
4474
|
init_registry();
|
|
@@ -4829,6 +4847,11 @@ var init_config2 = __esm({
|
|
|
4829
4847
|
grantsFor(actorId) {
|
|
4830
4848
|
return grantsIn(this.grants, actorId);
|
|
4831
4849
|
}
|
|
4850
|
+
/** The Slack people the grants table names, in its order — whoever holds anything by name (the
|
|
4851
|
+
* people an admin may want to view the dashboard as, record 0053). Never a credential or a surface. */
|
|
4852
|
+
grantedPeople() {
|
|
4853
|
+
return [...this.grants.grants.keys()].filter(isViewablePerson);
|
|
4854
|
+
}
|
|
4832
4855
|
/** Who to ask when denied — for actionable error messages: the Slack users
|
|
4833
4856
|
* who hold everything, in the table's order. Slack only because
|
|
4834
4857
|
* the hint is a `<@…>` mention in a chat reply; a credential granted
|
|
@@ -14525,14 +14548,14 @@ function partition(spans, input) {
|
|
|
14525
14548
|
}
|
|
14526
14549
|
if (backgroundIntervals.some(([s, e]) => s <= a && e >= b)) backgroundOnly += len;
|
|
14527
14550
|
}
|
|
14528
|
-
const
|
|
14551
|
+
const counted2 = buckets.getting_ready + buckets.thinking + buckets.tools + buckets.finishing_up;
|
|
14529
14552
|
return {
|
|
14530
14553
|
windowMs,
|
|
14531
14554
|
gettingReadyMs: buckets.getting_ready,
|
|
14532
14555
|
thinkingMs: buckets.thinking,
|
|
14533
14556
|
toolsMs: buckets.tools,
|
|
14534
14557
|
finishingUpMs: buckets.finishing_up,
|
|
14535
|
-
overheadMs: Math.max(0, windowMs -
|
|
14558
|
+
overheadMs: Math.max(0, windowMs - counted2 - notRecorded - notLoaded),
|
|
14536
14559
|
notRecordedMs: notRecorded,
|
|
14537
14560
|
notLoadedMs: notLoaded,
|
|
14538
14561
|
backgroundOnlyMs: backgroundOnly
|
|
@@ -16805,22 +16828,6 @@ var init_runDuration = __esm({
|
|
|
16805
16828
|
}
|
|
16806
16829
|
});
|
|
16807
16830
|
|
|
16808
|
-
// ../../src/core/authz/viewAs.ts
|
|
16809
|
-
function holdsAll(actor) {
|
|
16810
|
-
return actor.grants.actions === "all" && actor.grants.channels === "all" && actor.grants.repos === "all";
|
|
16811
|
-
}
|
|
16812
|
-
function isViewablePerson(id) {
|
|
16813
|
-
return /^slack:U[A-Z0-9]+$/.test(id);
|
|
16814
|
-
}
|
|
16815
|
-
function viewingRefusal(person) {
|
|
16816
|
-
return `You are viewing as ${person.name ?? person.id}; writes are your own to make \u2014 exit view-as to write.`;
|
|
16817
|
-
}
|
|
16818
|
-
var init_viewAs = __esm({
|
|
16819
|
-
"../../src/core/authz/viewAs.ts"() {
|
|
16820
|
-
"use strict";
|
|
16821
|
-
}
|
|
16822
|
-
});
|
|
16823
|
-
|
|
16824
16831
|
// ../../src/mcp/config.ts
|
|
16825
16832
|
function parseMcpSettings(raw) {
|
|
16826
16833
|
if (raw === void 0 || raw === null) return void 0;
|
|
@@ -23961,7 +23968,8 @@ _(clipped by Switchboard: body exceeded ${MAX_BODY_CHARS2} chars)_` : body;
|
|
|
23961
23968
|
}
|
|
23962
23969
|
return out;
|
|
23963
23970
|
}
|
|
23964
|
-
async readFile(repo, path, ref) {
|
|
23971
|
+
async readFile(repo, path, ref, opts) {
|
|
23972
|
+
const maxChars = opts?.maxChars ?? MAX_FILE_CHARS;
|
|
23965
23973
|
const q = ref ? `?ref=${encodeURIComponent(ref)}` : "";
|
|
23966
23974
|
const res = await this.request("read", "GET", "contents", `/repos/${repo}/contents/${encodePath(path)}${q}`);
|
|
23967
23975
|
const body = await res.json();
|
|
@@ -23981,13 +23989,13 @@ _(clipped by Switchboard: body exceeded ${MAX_BODY_CHARS2} chars)_` : body;
|
|
|
23981
23989
|
void 0,
|
|
23982
23990
|
"application/vnd.github.raw+json"
|
|
23983
23991
|
);
|
|
23984
|
-
content = await readTextCapped(raw,
|
|
23992
|
+
content = await readTextCapped(raw, maxChars + 1);
|
|
23985
23993
|
} else content = String(body.content ?? "");
|
|
23986
23994
|
if (content.includes("\0")) content = `(binary file, ${size} bytes \u2014 not shown)`;
|
|
23987
|
-
const truncated = content.length >
|
|
23995
|
+
const truncated = content.length > maxChars;
|
|
23988
23996
|
return {
|
|
23989
23997
|
path,
|
|
23990
|
-
content: truncated ? content.slice(0,
|
|
23998
|
+
content: truncated ? content.slice(0, maxChars) : content,
|
|
23991
23999
|
size,
|
|
23992
24000
|
truncated,
|
|
23993
24001
|
sha: String(body.sha ?? ""),
|
|
@@ -27535,6 +27543,8 @@ var init_attrs = __esm({
|
|
|
27535
27543
|
dedupe: "string",
|
|
27536
27544
|
requester: "string",
|
|
27537
27545
|
outcome: "string",
|
|
27546
|
+
refusal: "string",
|
|
27547
|
+
cause: "string",
|
|
27538
27548
|
count: "number",
|
|
27539
27549
|
backend: "string",
|
|
27540
27550
|
command: "string",
|
|
@@ -27612,6 +27622,189 @@ var init_trace = __esm({
|
|
|
27612
27622
|
}
|
|
27613
27623
|
});
|
|
27614
27624
|
|
|
27625
|
+
// ../../src/core/confirmations.ts
|
|
27626
|
+
import { randomUUID } from "node:crypto";
|
|
27627
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
|
|
27628
|
+
import { dirname as dirname9, resolve as resolve10 } from "node:path";
|
|
27629
|
+
function newConfirmationId() {
|
|
27630
|
+
return randomUUID();
|
|
27631
|
+
}
|
|
27632
|
+
function confirmationFooter(scope) {
|
|
27633
|
+
switch (scope) {
|
|
27634
|
+
case "channel":
|
|
27635
|
+
return "confirmation required by this channel's boundary";
|
|
27636
|
+
case "user":
|
|
27637
|
+
return "confirmation required by your boundary";
|
|
27638
|
+
case "defaults":
|
|
27639
|
+
return "confirmation required by the defaults' boundary";
|
|
27640
|
+
case "built-in":
|
|
27641
|
+
return "confirmation required by the built-in default";
|
|
27642
|
+
default:
|
|
27643
|
+
return `confirmation required by the ${scope} boundary`;
|
|
27644
|
+
}
|
|
27645
|
+
}
|
|
27646
|
+
function renderOffer(offer) {
|
|
27647
|
+
return [offer.line, ...offer.risk ? [offer.risk] : [], offer.footer].join("\n");
|
|
27648
|
+
}
|
|
27649
|
+
function confirmationMessageOf(msg) {
|
|
27650
|
+
const { images: _images, documents: _documents, staged: _staged, ...kept } = msg;
|
|
27651
|
+
return kept;
|
|
27652
|
+
}
|
|
27653
|
+
function isRecord3(v) {
|
|
27654
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
27655
|
+
}
|
|
27656
|
+
function isMessage(v) {
|
|
27657
|
+
return isRecord3(v) && typeof v.channelId === "string" && typeof v.userId === "string" && typeof v.threadKey === "string" && typeof v.text === "string";
|
|
27658
|
+
}
|
|
27659
|
+
function isPendingConfirmation(v) {
|
|
27660
|
+
return isRecord3(v) && typeof v.id === "string" && isMessage(v.message) && typeof v.command === "string" && isRecord3(v.input) && typeof v.receipt === "string" && typeof v.risk === "string" && typeof v.footer === "string" && typeof v.model === "string";
|
|
27661
|
+
}
|
|
27662
|
+
function isConfirmation(v) {
|
|
27663
|
+
return isPendingConfirmation(v) && typeof v.expiresAt === "number";
|
|
27664
|
+
}
|
|
27665
|
+
function isRefusal(v) {
|
|
27666
|
+
return typeof v === "string" && CONFIRMATION_REFUSALS.includes(v);
|
|
27667
|
+
}
|
|
27668
|
+
function judge2(rows, id, actorIds, now, kind) {
|
|
27669
|
+
const row2 = rows.get(id);
|
|
27670
|
+
if (!row2) return { ok: false, refused: "used" };
|
|
27671
|
+
if (kind === "consume" && row2.expiresAt <= now) {
|
|
27672
|
+
rows.delete(id);
|
|
27673
|
+
return { ok: false, refused: "expired", row: row2 };
|
|
27674
|
+
}
|
|
27675
|
+
if (!actorIds.includes(row2.message.userId))
|
|
27676
|
+
return kind === "consume" ? { ok: false, refused: "foreign", row: row2 } : { ok: false, refused: "foreign" };
|
|
27677
|
+
rows.delete(id);
|
|
27678
|
+
return kind === "consume" ? { ok: true, row: row2 } : { ok: true };
|
|
27679
|
+
}
|
|
27680
|
+
function replaceThreadRow(rows, row2) {
|
|
27681
|
+
for (const [id, r] of rows) if (r.message.threadKey === row2.message.threadKey) rows.delete(id);
|
|
27682
|
+
rows.set(row2.id, row2);
|
|
27683
|
+
}
|
|
27684
|
+
function buildConfirmationStore(config, secrets, opts) {
|
|
27685
|
+
const worker = config.config.runtimeOverrides?.worker;
|
|
27686
|
+
if (!worker) return new FileConfirmationStore(opts.path);
|
|
27687
|
+
const tokenEnv = worker.tokenEnv ?? "MEMORY_TOKEN";
|
|
27688
|
+
const token2 = secrets.named(tokenEnv);
|
|
27689
|
+
if (!token2) throw new Error(`runtimeOverrides.worker is configured but ${tokenEnv} is not set`);
|
|
27690
|
+
return new WorkerConfirmationStore({
|
|
27691
|
+
baseUrl: worker.baseUrl,
|
|
27692
|
+
token: token2.reveal(),
|
|
27693
|
+
...opts.fetch ? { fetch: opts.fetch } : {}
|
|
27694
|
+
});
|
|
27695
|
+
}
|
|
27696
|
+
var CONFIRMATION_REFUSALS, UNSHOWABLE_LINE, STORE_UNREACHABLE_NOTE, FileConfirmationStore, CONFIRMATION_WORKER_TIMEOUT_MS, WorkerConfirmationStore;
|
|
27697
|
+
var init_confirmations = __esm({
|
|
27698
|
+
"../../src/core/confirmations.ts"() {
|
|
27699
|
+
"use strict";
|
|
27700
|
+
init_clock();
|
|
27701
|
+
CONFIRMATION_REFUSALS = ["used", "expired", "foreign"];
|
|
27702
|
+
UNSHOWABLE_LINE = "this command carries a value that cannot be shown; type the line yourself";
|
|
27703
|
+
STORE_UNREACHABLE_NOTE = "(the confirmation store could not be reached, so there is no button to press)";
|
|
27704
|
+
FileConfirmationStore = class {
|
|
27705
|
+
path;
|
|
27706
|
+
clock;
|
|
27707
|
+
constructor(path, opts = {}) {
|
|
27708
|
+
this.path = resolve10(path);
|
|
27709
|
+
this.clock = opts.clock ?? systemClock;
|
|
27710
|
+
}
|
|
27711
|
+
read() {
|
|
27712
|
+
const rows = /* @__PURE__ */ new Map();
|
|
27713
|
+
if (!existsSync12(this.path)) return rows;
|
|
27714
|
+
const raw = JSON.parse(readFileSync14(this.path, "utf8"));
|
|
27715
|
+
for (const v of Array.isArray(raw.confirmations) ? raw.confirmations : []) if (isConfirmation(v)) rows.set(v.id, v);
|
|
27716
|
+
return rows;
|
|
27717
|
+
}
|
|
27718
|
+
write(rows) {
|
|
27719
|
+
mkdirSync9(dirname9(this.path), { recursive: true });
|
|
27720
|
+
writeFileSync8(this.path, JSON.stringify({ confirmations: [...rows.values()] }, null, 2));
|
|
27721
|
+
}
|
|
27722
|
+
async put(row2, ttlMs) {
|
|
27723
|
+
const rows = this.read();
|
|
27724
|
+
const stored = { ...row2, expiresAt: this.clock() + ttlMs };
|
|
27725
|
+
replaceThreadRow(rows, stored);
|
|
27726
|
+
this.write(rows);
|
|
27727
|
+
return stored;
|
|
27728
|
+
}
|
|
27729
|
+
async consume(id, actorIds) {
|
|
27730
|
+
const rows = this.read();
|
|
27731
|
+
const out = judge2(rows, id, actorIds, this.clock(), "consume");
|
|
27732
|
+
this.write(rows);
|
|
27733
|
+
return out;
|
|
27734
|
+
}
|
|
27735
|
+
async cancel(id, actorIds) {
|
|
27736
|
+
const rows = this.read();
|
|
27737
|
+
const out = judge2(rows, id, actorIds, this.clock(), "cancel");
|
|
27738
|
+
this.write(rows);
|
|
27739
|
+
return out;
|
|
27740
|
+
}
|
|
27741
|
+
describe() {
|
|
27742
|
+
return `file ${this.path}`;
|
|
27743
|
+
}
|
|
27744
|
+
};
|
|
27745
|
+
CONFIRMATION_WORKER_TIMEOUT_MS = 8e3;
|
|
27746
|
+
WorkerConfirmationStore = class {
|
|
27747
|
+
constructor(opts) {
|
|
27748
|
+
this.opts = opts;
|
|
27749
|
+
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
27750
|
+
this.fetchImpl = opts.fetch ?? fetch;
|
|
27751
|
+
}
|
|
27752
|
+
opts;
|
|
27753
|
+
baseUrl;
|
|
27754
|
+
fetchImpl;
|
|
27755
|
+
async put(row2, ttlMs) {
|
|
27756
|
+
const body = await this.post("/config/confirmations/put", {
|
|
27757
|
+
id: row2.id,
|
|
27758
|
+
threadKey: row2.message.threadKey,
|
|
27759
|
+
requester: row2.message.userId,
|
|
27760
|
+
body: row2,
|
|
27761
|
+
ttlMs
|
|
27762
|
+
});
|
|
27763
|
+
if (typeof body.expiresAt !== "number")
|
|
27764
|
+
throw new Error("confirmation store answered a put without the expiry it stamped");
|
|
27765
|
+
return { ...row2, expiresAt: body.expiresAt };
|
|
27766
|
+
}
|
|
27767
|
+
async consume(id, actorIds) {
|
|
27768
|
+
const body = await this.post("/config/confirmations/consume", { id, actorIds });
|
|
27769
|
+
const stored = isRecord3(body.row) ? body.row : void 0;
|
|
27770
|
+
const row2 = stored && isRecord3(stored.body) ? { ...stored.body, expiresAt: stored.expiresAt } : void 0;
|
|
27771
|
+
if (isRefusal(body.refused))
|
|
27772
|
+
return { ok: false, refused: body.refused, ...isConfirmation(row2) ? { row: row2 } : {} };
|
|
27773
|
+
if (!isConfirmation(row2)) throw new Error("confirmation store answered a consume outside its contract");
|
|
27774
|
+
return { ok: true, row: row2 };
|
|
27775
|
+
}
|
|
27776
|
+
async cancel(id, actorIds) {
|
|
27777
|
+
const body = await this.post("/config/confirmations/cancel", { id, actorIds });
|
|
27778
|
+
if (body.ok === true) return { ok: true };
|
|
27779
|
+
if (body.refused === "used" || body.refused === "foreign") return { ok: false, refused: body.refused };
|
|
27780
|
+
throw new Error("confirmation store answered a cancel outside its contract");
|
|
27781
|
+
}
|
|
27782
|
+
describe() {
|
|
27783
|
+
return `state Worker ${this.baseUrl} (ConfigDO confirmations)`;
|
|
27784
|
+
}
|
|
27785
|
+
async post(path, payload) {
|
|
27786
|
+
let res;
|
|
27787
|
+
try {
|
|
27788
|
+
res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
27789
|
+
method: "POST",
|
|
27790
|
+
headers: { authorization: `Bearer ${this.opts.token}`, "content-type": "application/json" },
|
|
27791
|
+
body: JSON.stringify(payload),
|
|
27792
|
+
signal: AbortSignal.timeout(CONFIRMATION_WORKER_TIMEOUT_MS)
|
|
27793
|
+
});
|
|
27794
|
+
} catch (err2) {
|
|
27795
|
+
throw new Error(`confirmation store unreachable: ${err2 instanceof Error ? err2.message : String(err2)}`, {
|
|
27796
|
+
cause: err2
|
|
27797
|
+
});
|
|
27798
|
+
}
|
|
27799
|
+
if (!res.ok) throw new Error(`confirmation store answered HTTP ${res.status} on ${path}`);
|
|
27800
|
+
const body = await res.json().catch(() => void 0);
|
|
27801
|
+
if (!isRecord3(body)) throw new Error(`confirmation store returned a non-JSON body on ${path}`);
|
|
27802
|
+
return body;
|
|
27803
|
+
}
|
|
27804
|
+
};
|
|
27805
|
+
}
|
|
27806
|
+
});
|
|
27807
|
+
|
|
27615
27808
|
// ../../src/core/markdownDocument.ts
|
|
27616
27809
|
function toMarkdownDocument(text) {
|
|
27617
27810
|
return text.replace(/[\uE000\uE001]/g, "").split(/(```[\s\S]*?```)/g).map((seg, i) => i % 2 === 1 ? seg : hardBreaks(convertProse(seg))).join("");
|
|
@@ -27802,6 +27995,17 @@ function channelSegment(channelId, channelName) {
|
|
|
27802
27995
|
function errorReply(err2) {
|
|
27803
27996
|
return `\u26A0\uFE0F ${err2 instanceof Error ? err2.message : String(err2)}`;
|
|
27804
27997
|
}
|
|
27998
|
+
async function renderRefusal(refusal, io) {
|
|
27999
|
+
if (refusal.cause === "policy" && refusal.wayForward) return io.reply(`${refusal.text} ${refusal.wayForward}`);
|
|
28000
|
+
return io.reply(refusal.text);
|
|
28001
|
+
}
|
|
28002
|
+
async function renderConfirmationOffer(io, offer) {
|
|
28003
|
+
if (io.offer) return io.offer(offer);
|
|
28004
|
+
return io.reply(renderOffer(offer));
|
|
28005
|
+
}
|
|
28006
|
+
function aRun(agentName) {
|
|
28007
|
+
return `${/^[aeiou]/i.test(agentName) ? "an" : "a"} *${agentName}* run`;
|
|
28008
|
+
}
|
|
27805
28009
|
function cardLines(trace, opts) {
|
|
27806
28010
|
const shape = cardShapeLine(trace.spansSoFar(), {
|
|
27807
28011
|
window: { start: trace.receivedAt, end: opts.end },
|
|
@@ -27910,12 +28114,13 @@ function afterReply(deps, ctx) {
|
|
|
27910
28114
|
answer
|
|
27911
28115
|
});
|
|
27912
28116
|
}
|
|
27913
|
-
var SNIPPET_MAX, RUN_LABEL_MAX, ASSISTANT_TRACE_CAP, STATUS_PREFIXES, LONG_COMMAND_REPLY_CHARS;
|
|
28117
|
+
var SNIPPET_MAX, RUN_LABEL_MAX, ASSISTANT_TRACE_CAP, STATUS_PREFIXES, REFUSAL_SENTENCES, LONG_COMMAND_REPLY_CHARS;
|
|
27914
28118
|
var init_reply = __esm({
|
|
27915
28119
|
"../../src/core/dispatch/reply.ts"() {
|
|
27916
28120
|
"use strict";
|
|
27917
28121
|
init_reviewVerdict();
|
|
27918
28122
|
init_channelDirectory();
|
|
28123
|
+
init_confirmations();
|
|
27919
28124
|
init_commandSurface();
|
|
27920
28125
|
init_markdownDocument();
|
|
27921
28126
|
init_runShape();
|
|
@@ -27926,10 +28131,73 @@ var init_reply = __esm({
|
|
|
27926
28131
|
RUN_LABEL_MAX = 160;
|
|
27927
28132
|
ASSISTANT_TRACE_CAP = 80;
|
|
27928
28133
|
STATUS_PREFIXES = ["\u23F3", "\u2705", "\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
28134
|
+
REFUSAL_SENTENCES = {
|
|
28135
|
+
agent_allowlist: (p) => `\u{1F6AB} You're not on the allowlist for the \`${p.agent}\` agent. Ask ${p.adminsHint} for access.`,
|
|
28136
|
+
live_agent_allowlist: (p) => `\u{1F6AB} You're not on the allowlist for the \`${p.agent}\` agent, whose run is in flight in this thread. Ask ${p.adminsHint} for access.`,
|
|
28137
|
+
elsewhere_agent_allowlist: (p) => `\u{1F6AB} You're not on the allowlist for the \`${p.agent}\` agent, whose run is in flight in this thread. Ask ${p.adminsHint} for access.`,
|
|
28138
|
+
repo_not_visible: (p) => `\u{1F4E6} \`${p.slug}\` is not a repository this installation can see \u2014 GitHub answered 404 \u2014 so I did not start ${aRun(p.agent)} for it. The repository is outside the Switchboard GitHub App installation (\`github_repos\` lists the reachable ones), or the name is wrong.`,
|
|
28139
|
+
repo_unverified: (p) => p.via === "github" ? `\u26A0\uFE0F I couldn't verify \`${p.slug}\` against GitHub \u2014 it didn't answer \u2014 so I did not start ${aRun(p.agent)} rather than guess which repository you meant. Try again in a minute.` : `\u26A0\uFE0F I couldn't verify that \`${p.slug}\` is an onboarded repo \u2014 the resident registry didn't answer \u2014 so I did not start a *${p.agent}* run rather than guess which repo you meant. Try again in a minute, or name the repository by URL (https://github.com/${p.slug}) to run in a cold per-thread sandbox.`,
|
|
28140
|
+
repo_not_onboarded: (p) => `\u{1F4E6} \`${p.slug}\` is not onboarded as a resident, so I did not start a *${p.agent}* run for it. ${p.onboardHint} for a warm, deps-ready environment, or name the repository by URL (https://github.com/${p.slug}) to run in a cold per-thread sandbox.`,
|
|
28141
|
+
repo_access: (p) => `\u{1F6AB} You're not on the allowlist for the \`${p.repo}\` repo environment. Ask ${p.adminsHint} for access.`,
|
|
28142
|
+
which_branch: (p) => `\u{1F33F} Which branch of \`${p.repo}\` should this thread work on? No branch is bound yet \u2014 reply naming one (e.g. "on main" or "on branch fix/login") and I'll pick it up from there.`,
|
|
28143
|
+
ship_budget: (p) => `\u{1F6AB} Ship cannot start under a ${p.maxMinutes}-minute budget: the loop it allows (${p.maxRounds} review rounds) needs ${p.need} minutes \u2014 ${p.provision} to provision, the coding child's ${p.coding}, and the reserve for the rounds after it at their floors. Widen the budget or the boundary that clipped it, or run \`agent:coding\` for a single pass without the review loop.`
|
|
28144
|
+
};
|
|
27929
28145
|
LONG_COMMAND_REPLY_CHARS = 3e3;
|
|
27930
28146
|
}
|
|
27931
28147
|
});
|
|
27932
28148
|
|
|
28149
|
+
// ../../src/core/refusal.ts
|
|
28150
|
+
function causeOf(code) {
|
|
28151
|
+
return CAUSE_OF[code];
|
|
28152
|
+
}
|
|
28153
|
+
function refusalOf(code, text, extra) {
|
|
28154
|
+
return { cause: causeOf(code), code, text, ...extra ?? {} };
|
|
28155
|
+
}
|
|
28156
|
+
var CAUSE_OF, REFUSAL_CODES;
|
|
28157
|
+
var init_refusal = __esm({
|
|
28158
|
+
"../../src/core/refusal.ts"() {
|
|
28159
|
+
"use strict";
|
|
28160
|
+
CAUSE_OF = {
|
|
28161
|
+
// the dispatch gates (dispatcher.ts `refuse(code)`)
|
|
28162
|
+
agent_allowlist: "policy",
|
|
28163
|
+
profile_bounded: "policy",
|
|
28164
|
+
repo_not_visible: "policy",
|
|
28165
|
+
repo_unverified: "system",
|
|
28166
|
+
repo_not_onboarded: "request",
|
|
28167
|
+
repo_access: "policy",
|
|
28168
|
+
pr_head_unknown: "system",
|
|
28169
|
+
branch_moved: "system",
|
|
28170
|
+
coordinator_thread_live: "system",
|
|
28171
|
+
live_agent_allowlist: "policy",
|
|
28172
|
+
follow_up_refused: "request",
|
|
28173
|
+
elsewhere_agent_allowlist: "policy",
|
|
28174
|
+
elsewhere_follow_up_refused: "request",
|
|
28175
|
+
which_branch: "request",
|
|
28176
|
+
workspace_lost: "system",
|
|
28177
|
+
ship_preflight: "system",
|
|
28178
|
+
ship_budget: "request",
|
|
28179
|
+
setup_failed: "system",
|
|
28180
|
+
// the click on a confirmation (confirm.ts)
|
|
28181
|
+
confirmation_used: "request",
|
|
28182
|
+
confirmation_expired: "request",
|
|
28183
|
+
confirmation_foreign: "policy",
|
|
28184
|
+
confirmation_unreadable: "system",
|
|
28185
|
+
// the references step's eight reasons (record 0037: one sentence, eight codes)
|
|
28186
|
+
reference_over_cap: "request",
|
|
28187
|
+
reference_rate_limited: "request",
|
|
28188
|
+
reference_guest: "policy",
|
|
28189
|
+
reference_not_a_member: "policy",
|
|
28190
|
+
reference_denied: "policy",
|
|
28191
|
+
reference_timed_out: "system",
|
|
28192
|
+
reference_never: "system",
|
|
28193
|
+
reference_fetch_failed: "system",
|
|
28194
|
+
// the catch-all: an uncaught throw in dispatch()
|
|
28195
|
+
uncaught: "system"
|
|
28196
|
+
};
|
|
28197
|
+
REFUSAL_CODES = Object.keys(CAUSE_OF);
|
|
28198
|
+
}
|
|
28199
|
+
});
|
|
28200
|
+
|
|
27933
28201
|
// ../../src/core/runHistoryWriter.ts
|
|
27934
28202
|
async function putOnce(sink, record2) {
|
|
27935
28203
|
try {
|
|
@@ -28344,7 +28612,20 @@ async function closeResumedRow(adopted, resume, why, status3 = "interrupted") {
|
|
|
28344
28612
|
}
|
|
28345
28613
|
}
|
|
28346
28614
|
async function admit(deps, ctx) {
|
|
28347
|
-
const {
|
|
28615
|
+
const {
|
|
28616
|
+
msg,
|
|
28617
|
+
io,
|
|
28618
|
+
directives,
|
|
28619
|
+
agentName,
|
|
28620
|
+
resume,
|
|
28621
|
+
restart,
|
|
28622
|
+
carriedRow,
|
|
28623
|
+
clock: clock4,
|
|
28624
|
+
root,
|
|
28625
|
+
refuse: refuse3,
|
|
28626
|
+
refuseSilently,
|
|
28627
|
+
admission
|
|
28628
|
+
} = ctx;
|
|
28348
28629
|
let claim = admission.claim(msg.threadKey, {
|
|
28349
28630
|
agent: agentName,
|
|
28350
28631
|
...carriedRow ? { now: carriedRow.startedAt } : {}
|
|
@@ -28391,16 +28672,16 @@ async function admit(deps, ctx) {
|
|
|
28391
28672
|
console.log(
|
|
28392
28673
|
`[dispatch] ${msg.threadKey} coordinator spawn (${ctx.coordinator.idempotencyKey}) refused: the ${claim.live.agent} run ${claim.live.runId ?? "(unnamed)"} is in flight`
|
|
28393
28674
|
);
|
|
28394
|
-
await
|
|
28675
|
+
await refuseSilently("coordinator_thread_live", async () => {
|
|
28395
28676
|
});
|
|
28396
28677
|
return { kind: "refused", reason: "coordinator_thread_live" };
|
|
28397
28678
|
}
|
|
28398
28679
|
if (claim.kind === "live") {
|
|
28399
28680
|
if (!deps.config.canRunAgent(chatActorOf(deps.config, msg), claim.live.agent)) {
|
|
28400
28681
|
await refuse3(
|
|
28401
|
-
|
|
28402
|
-
|
|
28403
|
-
|
|
28682
|
+
refusalOf(
|
|
28683
|
+
"live_agent_allowlist",
|
|
28684
|
+
REFUSAL_SENTENCES.live_agent_allowlist({ agent: claim.live.agent, adminsHint: deps.config.adminsHint() })
|
|
28404
28685
|
)
|
|
28405
28686
|
);
|
|
28406
28687
|
return { kind: "refused", reason: "live_agent_allowlist" };
|
|
@@ -28410,7 +28691,7 @@ async function admit(deps, ctx) {
|
|
|
28410
28691
|
console.log(
|
|
28411
28692
|
`[dispatch] ${msg.threadKey} follow-up refused (${decision.reason}): ${claim.live.agent} run in flight`
|
|
28412
28693
|
);
|
|
28413
|
-
await refuse3("follow_up_refused",
|
|
28694
|
+
await refuse3(refusalOf("follow_up_refused", refusalReply(claim.live, decision, clock4())));
|
|
28414
28695
|
return { kind: "refused", reason: "follow_up_refused" };
|
|
28415
28696
|
}
|
|
28416
28697
|
const at = clock4();
|
|
@@ -28442,7 +28723,7 @@ async function admit(deps, ctx) {
|
|
|
28442
28723
|
console.log(
|
|
28443
28724
|
`[dispatch] ${msg.threadKey} coordinator spawn (${ctx.coordinator.idempotencyKey}) refused: run ${elsewhere.runId} is in flight on another generation`
|
|
28444
28725
|
);
|
|
28445
|
-
await
|
|
28726
|
+
await refuseSilently("coordinator_thread_live", async () => {
|
|
28446
28727
|
});
|
|
28447
28728
|
return { kind: "refused", reason: "coordinator_thread_live" };
|
|
28448
28729
|
}
|
|
@@ -28457,8 +28738,11 @@ async function admit(deps, ctx) {
|
|
|
28457
28738
|
runId: elsewhere.runId
|
|
28458
28739
|
};
|
|
28459
28740
|
if (!deps.config.canRunAgent(chatActorOf(deps.config, msg), far.agent)) {
|
|
28460
|
-
await
|
|
28461
|
-
|
|
28741
|
+
await refuse3(
|
|
28742
|
+
refusalOf(
|
|
28743
|
+
"elsewhere_agent_allowlist",
|
|
28744
|
+
REFUSAL_SENTENCES.elsewhere_agent_allowlist({ agent: far.agent, adminsHint: deps.config.adminsHint() })
|
|
28745
|
+
)
|
|
28462
28746
|
);
|
|
28463
28747
|
return { kind: "refused", reason: "elsewhere_agent_allowlist" };
|
|
28464
28748
|
}
|
|
@@ -28468,7 +28752,7 @@ async function admit(deps, ctx) {
|
|
|
28468
28752
|
console.log(
|
|
28469
28753
|
`[dispatch] ${msg.threadKey} follow-up refused (${decision.reason}): ${far.agent} run ${far.runId} live on another generation`
|
|
28470
28754
|
);
|
|
28471
|
-
await
|
|
28755
|
+
await refuse3(refusalOf("elsewhere_follow_up_refused", refusalReply(far, decision, now)));
|
|
28472
28756
|
return { kind: "refused", reason: "elsewhere_follow_up_refused" };
|
|
28473
28757
|
}
|
|
28474
28758
|
const seq = await deps.runLedger.pushInbox(elsewhere.runId, durableInboxMessage(msg, directives.text, now));
|
|
@@ -28595,6 +28879,8 @@ var init_admission = __esm({
|
|
|
28595
28879
|
init_trace();
|
|
28596
28880
|
init_threadAdmission();
|
|
28597
28881
|
init_record();
|
|
28882
|
+
init_refusal();
|
|
28883
|
+
init_reply();
|
|
28598
28884
|
init_inboxMessage();
|
|
28599
28885
|
defaultAdmission = new ThreadAdmission();
|
|
28600
28886
|
}
|
|
@@ -29105,10 +29391,12 @@ function extractSignals(rawText) {
|
|
|
29105
29391
|
if (!t || t.includes("://") || t.toLowerCase().startsWith("github.com/")) continue;
|
|
29106
29392
|
const prev = i > 0 ? stripPunct(tokens[i - 1]).toLowerCase() : "";
|
|
29107
29393
|
if (prev === "branch") continue;
|
|
29108
|
-
|
|
29394
|
+
const prev2 = i > 1 ? stripPunct(tokens[i - 2]).toLowerCase() : "";
|
|
29395
|
+
const inAt = prev === "in" ? i - 1 : DETERMINERS.has(prev) && prev2 === "in" ? i - 2 : -1;
|
|
29396
|
+
if (inAt >= 0 && !inCode[i] && !out.addressed && !DETERMINERS.has(t.toLowerCase())) {
|
|
29109
29397
|
const slug = slugOf(t);
|
|
29110
29398
|
if (slug) out.addressed = { slug };
|
|
29111
|
-
else if (!t.includes("/") && NAME_RE.test(t) && tokens.slice(0,
|
|
29399
|
+
else if (!t.includes("/") && NAME_RE.test(t) && tokens.slice(0, inAt).every(isDirectiveOrMention))
|
|
29112
29400
|
out.addressed = { name: t.toLowerCase() };
|
|
29113
29401
|
}
|
|
29114
29402
|
if (prev === "on") {
|
|
@@ -29420,7 +29708,7 @@ function prFacts(title, body) {
|
|
|
29420
29708
|
if (last >= 55296 && last <= 56319) end--;
|
|
29421
29709
|
return { title, body: body.slice(0, end), truncated: true };
|
|
29422
29710
|
}
|
|
29423
|
-
var PR_BODY_CAP, OWNER_RE, NAME_RE, WELL_KNOWN_REFS, CODE_SPAN, threadSignalsCache, COMPARE_FILES_CAP;
|
|
29711
|
+
var PR_BODY_CAP, OWNER_RE, NAME_RE, WELL_KNOWN_REFS, DETERMINERS, CODE_SPAN, threadSignalsCache, COMPARE_FILES_CAP;
|
|
29424
29712
|
var init_repoContext = __esm({
|
|
29425
29713
|
"../../src/core/repoContext.ts"() {
|
|
29426
29714
|
"use strict";
|
|
@@ -29432,6 +29720,7 @@ var init_repoContext = __esm({
|
|
|
29432
29720
|
OWNER_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
|
|
29433
29721
|
NAME_RE = /^[A-Za-z0-9._-]{1,100}$/;
|
|
29434
29722
|
WELL_KNOWN_REFS = /* @__PURE__ */ new Set(["main", "master", "develop", "trunk"]);
|
|
29723
|
+
DETERMINERS = /* @__PURE__ */ new Set(["the", "a", "an", "our", "my", "this"]);
|
|
29435
29724
|
CODE_SPAN = /```[\s\S]*?```|`[^`\n]*`/g;
|
|
29436
29725
|
threadSignalsCache = /* @__PURE__ */ new WeakMap();
|
|
29437
29726
|
COMPARE_FILES_CAP = 300;
|
|
@@ -29646,7 +29935,7 @@ var init_indexFeed = __esm({
|
|
|
29646
29935
|
});
|
|
29647
29936
|
|
|
29648
29937
|
// ../../src/core/runRegistry.ts
|
|
29649
|
-
import { randomBytes as randomBytes3, randomUUID, timingSafeEqual } from "node:crypto";
|
|
29938
|
+
import { randomBytes as randomBytes3, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
|
|
29650
29939
|
function safeEqual(a, b) {
|
|
29651
29940
|
const ab = Buffer.from(a, "utf8");
|
|
29652
29941
|
const bb = Buffer.from(b, "utf8");
|
|
@@ -29685,7 +29974,7 @@ var init_runRegistry = __esm({
|
|
|
29685
29974
|
bytes: opts.backlogBytes ?? DEFAULT_BACKLOG_BYTES
|
|
29686
29975
|
};
|
|
29687
29976
|
this.ttlMs = opts.ttlMs ?? 6e4;
|
|
29688
|
-
this.genId = opts.genId ?? (() =>
|
|
29977
|
+
this.genId = opts.genId ?? (() => randomUUID2());
|
|
29689
29978
|
this.genToken = opts.genToken ?? (() => randomBytes3(32).toString("hex"));
|
|
29690
29979
|
this.now = opts.now ?? Date.now;
|
|
29691
29980
|
}
|
|
@@ -32485,6 +32774,162 @@ var init_status2 = __esm({
|
|
|
32485
32774
|
}
|
|
32486
32775
|
});
|
|
32487
32776
|
|
|
32777
|
+
// ../../src/core/prTitle.mjs
|
|
32778
|
+
function checkPrTitle(rawTitle, vocabulary) {
|
|
32779
|
+
const { types, scopes } = vocabulary;
|
|
32780
|
+
const title = (rawTitle ?? "").trim();
|
|
32781
|
+
if (title === "") return { ok: false, problems: ["the title is empty"] };
|
|
32782
|
+
const m = TITLE_GRAMMAR.exec(title);
|
|
32783
|
+
if (!m || !m.groups) return { ok: false, problems: [diagnose(title, types)] };
|
|
32784
|
+
const { type, scope, breaking, description } = m.groups;
|
|
32785
|
+
const problems = [];
|
|
32786
|
+
if (!types.includes(type)) problems.push(`unknown type "${type}" \u2014 use one of: ${types.join(", ")}`);
|
|
32787
|
+
if (scope !== void 0 && !scopes.includes(scope)) {
|
|
32788
|
+
problems.push(
|
|
32789
|
+
`unknown scope "${scope}" \u2014 use one of: ${scopes.join(", ")} (the Areas in ${CODE_MAP_PATH}), or no scope for a tree-wide change`
|
|
32790
|
+
);
|
|
32791
|
+
}
|
|
32792
|
+
if (/\.$/.test(description)) problems.push("the description ends with a period; drop it (it is a commit subject)");
|
|
32793
|
+
const exemptFromCap = type === "revert" || scope !== void 0 && BOT_SCOPES.includes(scope);
|
|
32794
|
+
if (!exemptFromCap && title.length > TITLE_MAX_LENGTH) {
|
|
32795
|
+
problems.push(
|
|
32796
|
+
`the title is ${title.length} characters; at most ${TITLE_MAX_LENGTH} \u2014 one change, one clause, present tense; the PR body carries the rest`
|
|
32797
|
+
);
|
|
32798
|
+
}
|
|
32799
|
+
if (problems.length > 0) return { ok: false, problems };
|
|
32800
|
+
return { ok: true, type, scope: scope ?? null, breaking: breaking === "!", description };
|
|
32801
|
+
}
|
|
32802
|
+
function diagnose(title, types) {
|
|
32803
|
+
const list = types.join(", ");
|
|
32804
|
+
if (/^\[?(wip|draft)\b/i.test(title))
|
|
32805
|
+
return `drop the "${title.split(/[\s\]:]/)[0]}" marker; mark the PR as a draft instead`;
|
|
32806
|
+
if (/^Revert "/.test(title))
|
|
32807
|
+
return 'a revert is titled `revert: <the original title>` (GitHub\'s Revert button writes `Revert "\u2026"`)';
|
|
32808
|
+
if (!/^[a-z]/.test(title)) return `the type must be lowercase, one of: ${list}`;
|
|
32809
|
+
if (!/^[a-z]+(\([^)]*\))?!?:/.test(title))
|
|
32810
|
+
return `start with a type and a colon \u2014 \`type: description\` or \`type(scope): description\` \u2014 where type is one of: ${list}`;
|
|
32811
|
+
if (/^[a-z]+\([^)]*[^a-z0-9._/)-][^)]*\)/.test(title) || /^[a-z]+\([^a-z0-9]/.test(title)) {
|
|
32812
|
+
return "the scope is lowercase letters, digits and . _ / - inside the parentheses, like `feat(slack): \u2026`";
|
|
32813
|
+
}
|
|
32814
|
+
if (/^[a-z]+(\([^)]*\))?!?:\s*$/.test(title)) return "add a description after the colon";
|
|
32815
|
+
if (/^[a-z]+(\([^)]*\))?!?:\S/.test(title)) return "put exactly one space after the colon, then the description";
|
|
32816
|
+
return `the title does not match \`type(scope)!: description\` with type one of: ${list}`;
|
|
32817
|
+
}
|
|
32818
|
+
var TITLE_GRAMMAR, CODE_MAP_PATH, RELEASE_CONFIG_PATH, TITLE_MAX_LENGTH, BOT_SCOPES, VOCABULARY_NOTE;
|
|
32819
|
+
var init_prTitle = __esm({
|
|
32820
|
+
"../../src/core/prTitle.mjs"() {
|
|
32821
|
+
"use strict";
|
|
32822
|
+
TITLE_GRAMMAR = /^(?<type>[a-z]+)(?:\((?<scope>[a-z0-9][a-z0-9._/-]*)\))?(?<breaking>!)?: (?<description>\S.*)$/;
|
|
32823
|
+
CODE_MAP_PATH = "docs/reference/code-map.md";
|
|
32824
|
+
RELEASE_CONFIG_PATH = "release-please-config.json";
|
|
32825
|
+
TITLE_MAX_LENGTH = 72;
|
|
32826
|
+
BOT_SCOPES = ["deps", "main"];
|
|
32827
|
+
VOCABULARY_NOTE = `generated by \`npm run pr-title:gen\` from ${RELEASE_CONFIG_PATH} (types) and the Areas table in ${CODE_MAP_PATH} (scopes) \u2014 do not edit by hand`;
|
|
32828
|
+
}
|
|
32829
|
+
});
|
|
32830
|
+
|
|
32831
|
+
// ../../src/core/prTitleVocabulary.json
|
|
32832
|
+
var prTitleVocabulary_default;
|
|
32833
|
+
var init_prTitleVocabulary = __esm({
|
|
32834
|
+
"../../src/core/prTitleVocabulary.json"() {
|
|
32835
|
+
prTitleVocabulary_default = {
|
|
32836
|
+
$generated: "generated by `npm run pr-title:gen` from release-please-config.json (types) and the Areas table in docs/reference/code-map.md (scopes) \u2014 do not edit by hand",
|
|
32837
|
+
types: [
|
|
32838
|
+
"feat",
|
|
32839
|
+
"fix",
|
|
32840
|
+
"perf",
|
|
32841
|
+
"revert",
|
|
32842
|
+
"docs",
|
|
32843
|
+
"style",
|
|
32844
|
+
"refactor",
|
|
32845
|
+
"test",
|
|
32846
|
+
"build",
|
|
32847
|
+
"ci",
|
|
32848
|
+
"chore"
|
|
32849
|
+
],
|
|
32850
|
+
scopes: [
|
|
32851
|
+
"dispatcher",
|
|
32852
|
+
"core",
|
|
32853
|
+
"config",
|
|
32854
|
+
"commands",
|
|
32855
|
+
"cli",
|
|
32856
|
+
"init",
|
|
32857
|
+
"setup",
|
|
32858
|
+
"authz",
|
|
32859
|
+
"runs",
|
|
32860
|
+
"tracing",
|
|
32861
|
+
"costs",
|
|
32862
|
+
"delivery",
|
|
32863
|
+
"slack",
|
|
32864
|
+
"http",
|
|
32865
|
+
"mcp",
|
|
32866
|
+
"agents",
|
|
32867
|
+
"review",
|
|
32868
|
+
"coding",
|
|
32869
|
+
"ship",
|
|
32870
|
+
"research",
|
|
32871
|
+
"explore",
|
|
32872
|
+
"conductor",
|
|
32873
|
+
"general",
|
|
32874
|
+
"providers",
|
|
32875
|
+
"resident",
|
|
32876
|
+
"sandbox",
|
|
32877
|
+
"harness",
|
|
32878
|
+
"memory",
|
|
32879
|
+
"skills",
|
|
32880
|
+
"tools",
|
|
32881
|
+
"web",
|
|
32882
|
+
"workers",
|
|
32883
|
+
"deploy",
|
|
32884
|
+
"docs",
|
|
32885
|
+
"load",
|
|
32886
|
+
"process",
|
|
32887
|
+
"release",
|
|
32888
|
+
"deps",
|
|
32889
|
+
"main"
|
|
32890
|
+
]
|
|
32891
|
+
};
|
|
32892
|
+
}
|
|
32893
|
+
});
|
|
32894
|
+
|
|
32895
|
+
// ../../src/core/selfDescription.ts
|
|
32896
|
+
function residentCapNote(cap) {
|
|
32897
|
+
return cap === void 0 ? RESIDENT_CAP_UNKNOWN_NOTE : `capped at ${cap} residents`;
|
|
32898
|
+
}
|
|
32899
|
+
function buildClause(build) {
|
|
32900
|
+
if (!build) return "";
|
|
32901
|
+
const commit = build.commit === "unknown" ? "an unstamped build" : `build ${build.commit.slice(0, 8)}`;
|
|
32902
|
+
return ` (version ${build.version}, ${commit} \u2014 \`status show\` reports it live)`;
|
|
32903
|
+
}
|
|
32904
|
+
function selfDescriptionBlock(agents, organization, caps, residentCap, build) {
|
|
32905
|
+
const agentLines = Object.values(agents).map((a) => `\`${a.name}\` \u2014 ${a.description}`).join("; ");
|
|
32906
|
+
const surfaces = caps.ingress ? "plus a CLI, an HTTP /api and an MCP surface" : "plus a CLI and an HTTP /api surface";
|
|
32907
|
+
const repos = caps.residents ? `Repos: the coding and review agents work in a "resident" \u2014 an always-warm per-repo environment (a mirror clone, installed deps, a built checkout, refreshed on a schedule and snapshotted) on the resident Worker; each run attaches a worktree on its branch in seconds. An admin onboards a repo with \`repo onboard <owner/name>\` (install/build/test commands are detected from the repo root, \`--install/--build/--test\` override), sees the fleet with \`repo list\`, changes it with \`repo reconfigure\` / \`repo rebuild\` / \`repo offboard\`; the fleet is ${residentCapNote(residentCap)} (\`--evict-coldest\` frees a slot). There is no separate "priority repos" setting: onboarded = warm. Repos that are not onboarded run in a cold per-thread workspace.` : `Repos: this installation has no resident (always-warm) repo environments \u2014 the coding and review agents clone the repository a request names (an https://github.com/owner/name URL or a PR link) into a per-thread workspace ${WORKSPACE_WHERE[caps.execution]} for the run. Nothing is kept warm between runs, and there is nothing to onboard.`;
|
|
32908
|
+
const history = caps.runHistory ? ", kept as run history" : " while it runs (this installation keeps no run history: a finished run's page goes away about a minute later)";
|
|
32909
|
+
const memory = caps.memory ? " Cross-session memory is per org/repo/channel/user (`memory list` / `memory forget`)." : " This installation has no cross-session memory: a run knows only its own thread.";
|
|
32910
|
+
return [
|
|
32911
|
+
`${SELF_DESCRIPTION_HEADER} you are Switchboard${buildClause(build)}, the agent gateway of the ${organization} organization. A message arrives on a channel (Slack \u2014 an @-mention, thread follow-ups stay on the same agent \u2014 ${surfaces}), is routed to an agent, runs on a configured provider/model, and executes tools through an executor.`,
|
|
32912
|
+
`Agents: ${agentLines}. Users pick one with an inline \`agent:<name>\` directive (default: general); \`model:\` / \`effort:\` ride the same way (see the runtime-config block).`,
|
|
32913
|
+
repos,
|
|
32914
|
+
`Runs${caps.memory ? " and memory" : ""}: every run (agent or operator command) has a live page under /runs with its events${history}; runs can be stopped from there.${memory} \`help\` lists every chat command.`,
|
|
32915
|
+
`Source of truth: the code lives in the GitHub repo ${SWITCHBOARD_REPO} \u2014 README.md (architecture + agents), AGENTS.md (invariants + map), and one behavioral spec per feature under docs/reference/specs/*.md (e.g. docs/reference/specs/resident-repos.md, docs/reference/specs/routing-and-config.md, docs/reference/specs/github-tools.md). For a question about how Switchboard works beyond this block, read those files with the GitHub tools when you have them (github_tree / github_file on ${SWITCHBOARD_REPO}) \u2014 they are private, so a public-web fetch 404s; if you have no GitHub tools, answer from this block and point at that path. Never describe yourself as stateless, tool-less by design, or unable to know your own workings.`
|
|
32916
|
+
].join("\n");
|
|
32917
|
+
}
|
|
32918
|
+
var SELF_DESCRIPTION_HEADER, SWITCHBOARD_REPO, RESIDENT_CAP_UNKNOWN_NOTE, WORKSPACE_WHERE;
|
|
32919
|
+
var init_selfDescription = __esm({
|
|
32920
|
+
"../../src/core/selfDescription.ts"() {
|
|
32921
|
+
"use strict";
|
|
32922
|
+
SELF_DESCRIPTION_HEADER = "About Switchboard (yourself):";
|
|
32923
|
+
SWITCHBOARD_REPO = "coreplanelabs/switchboard";
|
|
32924
|
+
RESIDENT_CAP_UNKNOWN_NOTE = "capped (`repo list` shows the cap)";
|
|
32925
|
+
WORKSPACE_WHERE = {
|
|
32926
|
+
local: "on the bot host",
|
|
32927
|
+
e2b: "in an E2B micro-VM",
|
|
32928
|
+
cloudflare: "in a Cloudflare sandbox"
|
|
32929
|
+
};
|
|
32930
|
+
}
|
|
32931
|
+
});
|
|
32932
|
+
|
|
32488
32933
|
// ../../src/core/prDescription.ts
|
|
32489
32934
|
import { z as z21 } from "zod";
|
|
32490
32935
|
function visibleLength(s) {
|
|
@@ -32502,6 +32947,9 @@ function cappedRaw(base, max) {
|
|
|
32502
32947
|
if (n2 > max) ctx.addIssue({ code: z21.ZodIssueCode.custom, message: `at most ${max} characters (got ${n2})` });
|
|
32503
32948
|
});
|
|
32504
32949
|
}
|
|
32950
|
+
function titleGateApplies(repo) {
|
|
32951
|
+
return repo !== void 0 && repo.toLowerCase() === TITLE_GATE_REPOSITORY;
|
|
32952
|
+
}
|
|
32505
32953
|
function boundedArray(item, min, max, what) {
|
|
32506
32954
|
return z21.array(item).superRefine((arr, ctx) => {
|
|
32507
32955
|
if (arr.length < min)
|
|
@@ -32510,8 +32958,11 @@ function boundedArray(item, min, max, what) {
|
|
|
32510
32958
|
ctx.addIssue({ code: z21.ZodIssueCode.custom, message: `at most ${max} ${what} (got ${arr.length})` });
|
|
32511
32959
|
});
|
|
32512
32960
|
}
|
|
32513
|
-
function
|
|
32514
|
-
return PrDescriptionSchema.
|
|
32961
|
+
function prDescriptionSchemaFor(repo) {
|
|
32962
|
+
return titleGateApplies(repo) ? PrDescriptionSchema.extend({ title: gatedTitle }) : PrDescriptionSchema;
|
|
32963
|
+
}
|
|
32964
|
+
function parsePrDescription(input, target = {}) {
|
|
32965
|
+
return prDescriptionSchemaFor(target.repo).parse(input);
|
|
32515
32966
|
}
|
|
32516
32967
|
function redactPrDescription(d) {
|
|
32517
32968
|
return mapStringLeaves(d, redactSecrets);
|
|
@@ -32902,16 +33353,19 @@ function parseLegacySteps(lines, problems) {
|
|
|
32902
33353
|
}
|
|
32903
33354
|
return out;
|
|
32904
33355
|
}
|
|
32905
|
-
var PR_DESCRIPTION_CAPS, line2, prose, RISK_SEPARATOR, PrAnchorSchema, PointerSchema, PrDescriptionSchema, REPO_RE2, SHA_RE2, GENERATED_FOOTER, MAP_LABELS, FOLD_SUMMARIES, PERMALINK_SRC, PERMALINK_RE, POINTER_ROW_RE, HEADING_RE, LABEL_RE, SUMMARY_RE, DECISION_RE, TABLE_HEADER_RE, TABLE_RULE_RE, STEP_RE, LOOK_FOR_RE, LABEL_KEYS, WHERE_TO_LOOK, LEGACY_SECTIONS;
|
|
33356
|
+
var PR_DESCRIPTION_CAPS, line2, prose, TITLE_GATE_REPOSITORY, gatedTitle, RISK_SEPARATOR, PrAnchorSchema, PointerSchema, PrDescriptionSchema, REPO_RE2, SHA_RE2, GENERATED_FOOTER, MAP_LABELS, FOLD_SUMMARIES, PERMALINK_SRC, PERMALINK_RE, POINTER_ROW_RE, HEADING_RE, LABEL_RE, SUMMARY_RE, DECISION_RE, TABLE_HEADER_RE, TABLE_RULE_RE, STEP_RE, LOOK_FOR_RE, LABEL_KEYS, WHERE_TO_LOOK, LEGACY_SECTIONS;
|
|
32906
33357
|
var init_prDescription = __esm({
|
|
32907
33358
|
"../../src/core/prDescription.ts"() {
|
|
32908
33359
|
"use strict";
|
|
33360
|
+
init_prTitle();
|
|
33361
|
+
init_prTitleVocabulary();
|
|
32909
33362
|
init_redact();
|
|
33363
|
+
init_selfDescription();
|
|
32910
33364
|
PR_DESCRIPTION_CAPS = {
|
|
32911
33365
|
/** The squash subject and the changelog line, the whole line counted: the
|
|
32912
|
-
*
|
|
32913
|
-
*
|
|
32914
|
-
title:
|
|
33366
|
+
* title gate's own number (`src/core/prTitle.mjs`), read from it so the
|
|
33367
|
+
* two cannot drift. */
|
|
33368
|
+
title: TITLE_MAX_LENGTH,
|
|
32915
33369
|
tldr: 300,
|
|
32916
33370
|
why: 400,
|
|
32917
33371
|
pointers: 7,
|
|
@@ -32930,6 +33384,13 @@ var init_prDescription = __esm({
|
|
|
32930
33384
|
};
|
|
32931
33385
|
line2 = z21.string().trim().min(1).refine((s) => !/[\r\n]/.test(s), "must be a single line (no embedded newlines)");
|
|
32932
33386
|
prose = z21.string().trim().min(1);
|
|
33387
|
+
TITLE_GATE_REPOSITORY = SWITCHBOARD_REPO;
|
|
33388
|
+
gatedTitle = line2.superRefine((s, ctx) => {
|
|
33389
|
+
if (s === "") return;
|
|
33390
|
+
const verdict = checkPrTitle(s, prTitleVocabulary_default);
|
|
33391
|
+
if (verdict.ok) return;
|
|
33392
|
+
for (const message4 of verdict.problems) ctx.addIssue({ code: z21.ZodIssueCode.custom, message: message4 });
|
|
33393
|
+
});
|
|
32933
33394
|
RISK_SEPARATOR = " \u26A0 ";
|
|
32934
33395
|
PrAnchorSchema = z21.object({
|
|
32935
33396
|
// Repo-relative: no leading `/`, no `..` SEGMENT (a filename like `a..b.ts` is fine).
|
|
@@ -32951,7 +33412,9 @@ var init_prDescription = __esm({
|
|
|
32951
33412
|
PrDescriptionSchema = z21.object({
|
|
32952
33413
|
/** The PR title's single source. Metadata for the PR's own title field —
|
|
32953
33414
|
* never rendered into the body (GitHub shows the title itself) — and the
|
|
32954
|
-
* squash subject, so it is capped like every other field
|
|
33415
|
+
* squash subject, so it is capped like every other field; on the
|
|
33416
|
+
* repository that carries the title gate, `prDescriptionSchemaFor` judges
|
|
33417
|
+
* it as the gate does. */
|
|
32955
33418
|
title: cappedRaw(line2, PR_DESCRIPTION_CAPS.title),
|
|
32956
33419
|
tldr: capped2(prose, PR_DESCRIPTION_CAPS.tldr),
|
|
32957
33420
|
why: capped2(prose, PR_DESCRIPTION_CAPS.why),
|
|
@@ -33120,13 +33583,13 @@ var init_submit = __esm({
|
|
|
33120
33583
|
submitPrDescriptionTool = {
|
|
33121
33584
|
name: "submit_pr_description",
|
|
33122
33585
|
failsInText: true,
|
|
33123
|
-
description: "Submit the PR description as a typed object. REQUIRED after pushing your branch: Switchboard renders the GitHub PR body from this object at the pushed head and opens (or updates) the pull request itself \u2014 never open a PR yourself. The body is a fixed-size MAP for the reader (tldr, why, at most 7 pointers, feedbackWanted, risk, verified) with decisions, validation and agentNotes collapsed under it; every field is capped in visible characters (a link's URL is not counted) and the tool refuses an object over a cap naming the field and the count, so cut and resubmit. `title` becomes the PR's title; pointer anchors are (path, from, to) line ranges at your pushed head, rendered as links. Call it after your last push; if you push again afterwards, call it again \u2014 the last valid call wins.",
|
|
33586
|
+
description: "Submit the PR description as a typed object. REQUIRED after pushing your branch: Switchboard renders the GitHub PR body from this object at the pushed head and opens (or updates) the pull request itself \u2014 never open a PR yourself. The body is a fixed-size MAP for the reader (tldr, why, at most 7 pointers, feedbackWanted, risk, verified) with decisions, validation and agentNotes collapsed under it; every field is capped in visible characters (a link's URL is not counted) and the tool refuses an object over a cap naming the field and the count, so cut and resubmit. `title` becomes the PR's title (at most 72 characters); on Switchboard's own repository it is also judged as that repository's CI `title` check judges it \u2014 `type(scope): description`, type from its release config, scope one of its code map's Areas or none \u2014 and refused with that check's own sentence, so fix the title and resubmit; pointer anchors are (path, from, to) line ranges at your pushed head, rendered as links. Call it after your last push; if you push again afterwards, call it again \u2014 the last valid call wins.",
|
|
33124
33587
|
inputSchema: {
|
|
33125
33588
|
type: "object",
|
|
33126
33589
|
properties: {
|
|
33127
33590
|
title: {
|
|
33128
33591
|
type: "string",
|
|
33129
|
-
description: "The PR title \u2014 one line naming the change, `type(scope): what a reader can now do or expect`, at most 72 characters in all"
|
|
33592
|
+
description: "The PR title \u2014 one line naming the change, `type(scope): what a reader can now do or expect`, at most 72 characters in all; on Switchboard's own repository a title its CI title check would refuse (type, scope, grammar) is refused here with the same sentence"
|
|
33130
33593
|
},
|
|
33131
33594
|
tldr: {
|
|
33132
33595
|
type: "string",
|
|
@@ -33205,7 +33668,7 @@ var init_submit = __esm({
|
|
|
33205
33668
|
async run(input, ctx) {
|
|
33206
33669
|
let desc;
|
|
33207
33670
|
try {
|
|
33208
|
-
desc = parsePrDescription(input);
|
|
33671
|
+
desc = parsePrDescription(input, ctx.repo === void 0 ? {} : { repo: ctx.repo });
|
|
33209
33672
|
} catch (err2) {
|
|
33210
33673
|
const detail = err2 instanceof z22.ZodError ? err2.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ") : String(err2);
|
|
33211
33674
|
return `error: invalid PR description \u2014 ${detail}`;
|
|
@@ -33416,187 +33879,6 @@ var init_statusCardFrame = __esm({
|
|
|
33416
33879
|
}
|
|
33417
33880
|
});
|
|
33418
33881
|
|
|
33419
|
-
// ../../src/core/confirmations.ts
|
|
33420
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
33421
|
-
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
|
|
33422
|
-
import { dirname as dirname9, resolve as resolve10 } from "node:path";
|
|
33423
|
-
function newConfirmationId() {
|
|
33424
|
-
return randomUUID2();
|
|
33425
|
-
}
|
|
33426
|
-
function confirmationFooter(scope) {
|
|
33427
|
-
switch (scope) {
|
|
33428
|
-
case "channel":
|
|
33429
|
-
return "confirmation required by this channel's boundary";
|
|
33430
|
-
case "user":
|
|
33431
|
-
return "confirmation required by your boundary";
|
|
33432
|
-
case "defaults":
|
|
33433
|
-
return "confirmation required by the defaults' boundary";
|
|
33434
|
-
case "built-in":
|
|
33435
|
-
return "confirmation required by the built-in default";
|
|
33436
|
-
default:
|
|
33437
|
-
return `confirmation required by the ${scope} boundary`;
|
|
33438
|
-
}
|
|
33439
|
-
}
|
|
33440
|
-
function renderOffer(offer) {
|
|
33441
|
-
return [offer.line, ...offer.risk ? [offer.risk] : [], offer.footer].join("\n");
|
|
33442
|
-
}
|
|
33443
|
-
function confirmationMessageOf(msg) {
|
|
33444
|
-
const { images: _images, documents: _documents, staged: _staged, ...kept } = msg;
|
|
33445
|
-
return kept;
|
|
33446
|
-
}
|
|
33447
|
-
function isRecord3(v) {
|
|
33448
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
33449
|
-
}
|
|
33450
|
-
function isMessage(v) {
|
|
33451
|
-
return isRecord3(v) && typeof v.channelId === "string" && typeof v.userId === "string" && typeof v.threadKey === "string" && typeof v.text === "string";
|
|
33452
|
-
}
|
|
33453
|
-
function isPendingConfirmation(v) {
|
|
33454
|
-
return isRecord3(v) && typeof v.id === "string" && isMessage(v.message) && typeof v.command === "string" && isRecord3(v.input) && typeof v.receipt === "string" && typeof v.risk === "string" && typeof v.footer === "string" && typeof v.model === "string";
|
|
33455
|
-
}
|
|
33456
|
-
function isConfirmation(v) {
|
|
33457
|
-
return isPendingConfirmation(v) && typeof v.expiresAt === "number";
|
|
33458
|
-
}
|
|
33459
|
-
function isRefusal(v) {
|
|
33460
|
-
return typeof v === "string" && CONFIRMATION_REFUSALS.includes(v);
|
|
33461
|
-
}
|
|
33462
|
-
function judge2(rows, id, actorIds, now, kind) {
|
|
33463
|
-
const row2 = rows.get(id);
|
|
33464
|
-
if (!row2) return { ok: false, refused: "used" };
|
|
33465
|
-
if (kind === "consume" && row2.expiresAt <= now) {
|
|
33466
|
-
rows.delete(id);
|
|
33467
|
-
return { ok: false, refused: "expired" };
|
|
33468
|
-
}
|
|
33469
|
-
if (!actorIds.includes(row2.message.userId)) return { ok: false, refused: "foreign" };
|
|
33470
|
-
rows.delete(id);
|
|
33471
|
-
return kind === "consume" ? { ok: true, row: row2 } : { ok: true };
|
|
33472
|
-
}
|
|
33473
|
-
function replaceThreadRow(rows, row2) {
|
|
33474
|
-
for (const [id, r] of rows) if (r.message.threadKey === row2.message.threadKey) rows.delete(id);
|
|
33475
|
-
rows.set(row2.id, row2);
|
|
33476
|
-
}
|
|
33477
|
-
function buildConfirmationStore(config, secrets, opts) {
|
|
33478
|
-
const worker = config.config.runtimeOverrides?.worker;
|
|
33479
|
-
if (!worker) return new FileConfirmationStore(opts.path);
|
|
33480
|
-
const tokenEnv = worker.tokenEnv ?? "MEMORY_TOKEN";
|
|
33481
|
-
const token2 = secrets.named(tokenEnv);
|
|
33482
|
-
if (!token2) throw new Error(`runtimeOverrides.worker is configured but ${tokenEnv} is not set`);
|
|
33483
|
-
return new WorkerConfirmationStore({
|
|
33484
|
-
baseUrl: worker.baseUrl,
|
|
33485
|
-
token: token2.reveal(),
|
|
33486
|
-
...opts.fetch ? { fetch: opts.fetch } : {}
|
|
33487
|
-
});
|
|
33488
|
-
}
|
|
33489
|
-
var CONFIRMATION_REFUSALS, UNSHOWABLE_LINE, STORE_UNREACHABLE_NOTE, FileConfirmationStore, CONFIRMATION_WORKER_TIMEOUT_MS, WorkerConfirmationStore;
|
|
33490
|
-
var init_confirmations = __esm({
|
|
33491
|
-
"../../src/core/confirmations.ts"() {
|
|
33492
|
-
"use strict";
|
|
33493
|
-
init_clock();
|
|
33494
|
-
CONFIRMATION_REFUSALS = ["used", "expired", "foreign"];
|
|
33495
|
-
UNSHOWABLE_LINE = "this command carries a value that cannot be shown; type the line yourself";
|
|
33496
|
-
STORE_UNREACHABLE_NOTE = "(the confirmation store could not be reached, so there is no button to press)";
|
|
33497
|
-
FileConfirmationStore = class {
|
|
33498
|
-
path;
|
|
33499
|
-
clock;
|
|
33500
|
-
constructor(path, opts = {}) {
|
|
33501
|
-
this.path = resolve10(path);
|
|
33502
|
-
this.clock = opts.clock ?? systemClock;
|
|
33503
|
-
}
|
|
33504
|
-
read() {
|
|
33505
|
-
const rows = /* @__PURE__ */ new Map();
|
|
33506
|
-
if (!existsSync12(this.path)) return rows;
|
|
33507
|
-
const raw = JSON.parse(readFileSync14(this.path, "utf8"));
|
|
33508
|
-
for (const v of Array.isArray(raw.confirmations) ? raw.confirmations : []) if (isConfirmation(v)) rows.set(v.id, v);
|
|
33509
|
-
return rows;
|
|
33510
|
-
}
|
|
33511
|
-
write(rows) {
|
|
33512
|
-
mkdirSync9(dirname9(this.path), { recursive: true });
|
|
33513
|
-
writeFileSync8(this.path, JSON.stringify({ confirmations: [...rows.values()] }, null, 2));
|
|
33514
|
-
}
|
|
33515
|
-
async put(row2, ttlMs) {
|
|
33516
|
-
const rows = this.read();
|
|
33517
|
-
const stored = { ...row2, expiresAt: this.clock() + ttlMs };
|
|
33518
|
-
replaceThreadRow(rows, stored);
|
|
33519
|
-
this.write(rows);
|
|
33520
|
-
return stored;
|
|
33521
|
-
}
|
|
33522
|
-
async consume(id, actorIds) {
|
|
33523
|
-
const rows = this.read();
|
|
33524
|
-
const out = judge2(rows, id, actorIds, this.clock(), "consume");
|
|
33525
|
-
this.write(rows);
|
|
33526
|
-
return out;
|
|
33527
|
-
}
|
|
33528
|
-
async cancel(id, actorIds) {
|
|
33529
|
-
const rows = this.read();
|
|
33530
|
-
const out = judge2(rows, id, actorIds, this.clock(), "cancel");
|
|
33531
|
-
this.write(rows);
|
|
33532
|
-
return out;
|
|
33533
|
-
}
|
|
33534
|
-
describe() {
|
|
33535
|
-
return `file ${this.path}`;
|
|
33536
|
-
}
|
|
33537
|
-
};
|
|
33538
|
-
CONFIRMATION_WORKER_TIMEOUT_MS = 8e3;
|
|
33539
|
-
WorkerConfirmationStore = class {
|
|
33540
|
-
constructor(opts) {
|
|
33541
|
-
this.opts = opts;
|
|
33542
|
-
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
33543
|
-
this.fetchImpl = opts.fetch ?? fetch;
|
|
33544
|
-
}
|
|
33545
|
-
opts;
|
|
33546
|
-
baseUrl;
|
|
33547
|
-
fetchImpl;
|
|
33548
|
-
async put(row2, ttlMs) {
|
|
33549
|
-
const body = await this.post("/config/confirmations/put", {
|
|
33550
|
-
id: row2.id,
|
|
33551
|
-
threadKey: row2.message.threadKey,
|
|
33552
|
-
requester: row2.message.userId,
|
|
33553
|
-
body: row2,
|
|
33554
|
-
ttlMs
|
|
33555
|
-
});
|
|
33556
|
-
if (typeof body.expiresAt !== "number")
|
|
33557
|
-
throw new Error("confirmation store answered a put without the expiry it stamped");
|
|
33558
|
-
return { ...row2, expiresAt: body.expiresAt };
|
|
33559
|
-
}
|
|
33560
|
-
async consume(id, actorIds) {
|
|
33561
|
-
const body = await this.post("/config/confirmations/consume", { id, actorIds });
|
|
33562
|
-
if (isRefusal(body.refused)) return { ok: false, refused: body.refused };
|
|
33563
|
-
const stored = isRecord3(body.row) ? body.row : void 0;
|
|
33564
|
-
const row2 = stored && isRecord3(stored.body) ? { ...stored.body, expiresAt: stored.expiresAt } : void 0;
|
|
33565
|
-
if (!isConfirmation(row2)) throw new Error("confirmation store answered a consume outside its contract");
|
|
33566
|
-
return { ok: true, row: row2 };
|
|
33567
|
-
}
|
|
33568
|
-
async cancel(id, actorIds) {
|
|
33569
|
-
const body = await this.post("/config/confirmations/cancel", { id, actorIds });
|
|
33570
|
-
if (body.ok === true) return { ok: true };
|
|
33571
|
-
if (body.refused === "used" || body.refused === "foreign") return { ok: false, refused: body.refused };
|
|
33572
|
-
throw new Error("confirmation store answered a cancel outside its contract");
|
|
33573
|
-
}
|
|
33574
|
-
describe() {
|
|
33575
|
-
return `state Worker ${this.baseUrl} (ConfigDO confirmations)`;
|
|
33576
|
-
}
|
|
33577
|
-
async post(path, payload) {
|
|
33578
|
-
let res;
|
|
33579
|
-
try {
|
|
33580
|
-
res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
33581
|
-
method: "POST",
|
|
33582
|
-
headers: { authorization: `Bearer ${this.opts.token}`, "content-type": "application/json" },
|
|
33583
|
-
body: JSON.stringify(payload),
|
|
33584
|
-
signal: AbortSignal.timeout(CONFIRMATION_WORKER_TIMEOUT_MS)
|
|
33585
|
-
});
|
|
33586
|
-
} catch (err2) {
|
|
33587
|
-
throw new Error(`confirmation store unreachable: ${err2 instanceof Error ? err2.message : String(err2)}`, {
|
|
33588
|
-
cause: err2
|
|
33589
|
-
});
|
|
33590
|
-
}
|
|
33591
|
-
if (!res.ok) throw new Error(`confirmation store answered HTTP ${res.status} on ${path}`);
|
|
33592
|
-
const body = await res.json().catch(() => void 0);
|
|
33593
|
-
if (!isRecord3(body)) throw new Error(`confirmation store returned a non-JSON body on ${path}`);
|
|
33594
|
-
return body;
|
|
33595
|
-
}
|
|
33596
|
-
};
|
|
33597
|
-
}
|
|
33598
|
-
});
|
|
33599
|
-
|
|
33600
33882
|
// ../../src/core/dispatch/route.ts
|
|
33601
33883
|
function routedRunsAtOnce(def, confirm) {
|
|
33602
33884
|
const radius = blastRadius(def);
|
|
@@ -34080,9 +34362,8 @@ async function answerHandBack(branch2, msg, def, input, route3, receipt, confirm
|
|
|
34080
34362
|
return { kind: "command", command: def.id, outcome };
|
|
34081
34363
|
};
|
|
34082
34364
|
const handBack = `${HAND_BACK_PREFIX} ${receipt}`;
|
|
34083
|
-
const offer = io.offer?.bind(io);
|
|
34084
34365
|
const store = deps.confirmations;
|
|
34085
|
-
if (!offer || !store) return answer(handBack, "hand_back", () => io.reply(handBack));
|
|
34366
|
+
if (!io.offer || !store) return answer(handBack, "hand_back", () => io.reply(handBack));
|
|
34086
34367
|
const line3 = chatInvocation(def, input);
|
|
34087
34368
|
if (redactSecrets(line3) !== line3) return answer(UNSHOWABLE_LINE, "hand_back", () => io.reply(UNSHOWABLE_LINE));
|
|
34088
34369
|
const risk = def.annotations?.risk?.(input) ?? "";
|
|
@@ -34112,7 +34393,7 @@ ${STORE_UNREACHABLE_NOTE}`;
|
|
|
34112
34393
|
}
|
|
34113
34394
|
const shown = { id: row2.id, line: line3, risk, footer, expiresAt: row2.expiresAt };
|
|
34114
34395
|
console.log(`[route] ${msg.threadKey} offered ${def.id} as confirmation ${row2.id} (${footer})`);
|
|
34115
|
-
return answer(renderOffer(shown), "offered", () =>
|
|
34396
|
+
return answer(renderOffer(shown), "offered", () => renderConfirmationOffer(io, shown));
|
|
34116
34397
|
}
|
|
34117
34398
|
function redactedInput(input) {
|
|
34118
34399
|
const text = (s) => redactAndCap(redactSecrets(s), ROUTE_COMMAND_VALUE_CAP);
|
|
@@ -34172,6 +34453,7 @@ var init_route = __esm({
|
|
|
34172
34453
|
init_confirmations();
|
|
34173
34454
|
init_repoContext();
|
|
34174
34455
|
init_commandRun();
|
|
34456
|
+
init_reply();
|
|
34175
34457
|
init_spawn();
|
|
34176
34458
|
init_statusCardFrame();
|
|
34177
34459
|
ROUTE_TEXT_CAP = 2e3;
|
|
@@ -34282,8 +34564,8 @@ function refusalLine(refused7) {
|
|
|
34282
34564
|
function actorIdsOf(actor) {
|
|
34283
34565
|
return [.../* @__PURE__ */ new Set([actor.id, ...actor.self ?? []])];
|
|
34284
34566
|
}
|
|
34285
|
-
function refused3(r) {
|
|
34286
|
-
return { kind: "refused", refusal: `confirmation_${r}`, text: refusalLine(r) };
|
|
34567
|
+
function refused3(r, row2) {
|
|
34568
|
+
return { kind: "refused", refusal: `confirmation_${r}`, text: refusalLine(r), ...row2 ? { row: row2 } : {} };
|
|
34287
34569
|
}
|
|
34288
34570
|
async function consumeAndRun(deps, click, io, ending, trace) {
|
|
34289
34571
|
const store = deps.confirmations;
|
|
@@ -34297,7 +34579,7 @@ async function consumeAndRun(deps, click, io, ending, trace) {
|
|
|
34297
34579
|
);
|
|
34298
34580
|
return UNREADABLE;
|
|
34299
34581
|
}
|
|
34300
|
-
if (!consumed.ok) return refused3(consumed.refused);
|
|
34582
|
+
if (!consumed.ok) return refused3(consumed.refused, consumed.row);
|
|
34301
34583
|
const row2 = consumed.row;
|
|
34302
34584
|
const result = await runChatCommand(
|
|
34303
34585
|
deps,
|
|
@@ -34337,7 +34619,7 @@ async function cancelPending(deps, click) {
|
|
|
34337
34619
|
if (!cancelled.ok) return refused3(cancelled.refused);
|
|
34338
34620
|
return { kind: "cancelled", text: OFFER_CANCELLED_LINE };
|
|
34339
34621
|
}
|
|
34340
|
-
var OFFER_EXPIRED_LINE, OFFER_FOREIGN_LINE, OFFER_USED_LINE, OFFER_UNREADABLE_LINE, OFFER_CANCELLED_LINE, CONFIRMED_REASON, UNREADABLE;
|
|
34622
|
+
var OFFER_EXPIRED_LINE, OFFER_FOREIGN_LINE, OFFER_USED_LINE, OFFER_UNREADABLE_LINE, OFFER_CANCELLED_LINE, CONFIRMED_REASON, REFUSED_REASON, UNREADABLE;
|
|
34341
34623
|
var init_confirm = __esm({
|
|
34342
34624
|
"../../src/core/dispatch/confirm.ts"() {
|
|
34343
34625
|
"use strict";
|
|
@@ -34350,6 +34632,7 @@ var init_confirm = __esm({
|
|
|
34350
34632
|
OFFER_UNREADABLE_LINE = "the confirmation could not be read; type the line to run it";
|
|
34351
34633
|
OFFER_CANCELLED_LINE = "Cancelled; nothing ran";
|
|
34352
34634
|
CONFIRMED_REASON = "confirmed after offer";
|
|
34635
|
+
REFUSED_REASON = "refused after offer";
|
|
34353
34636
|
UNREADABLE = { kind: "refused", refusal: "confirmation_unreadable", text: OFFER_UNREADABLE_LINE };
|
|
34354
34637
|
}
|
|
34355
34638
|
});
|
|
@@ -35288,12 +35571,12 @@ var init_reviewRound = __esm({
|
|
|
35288
35571
|
|
|
35289
35572
|
// ../../src/core/dispatch/authorize.ts
|
|
35290
35573
|
async function authorizeAgent(deps, ctx) {
|
|
35291
|
-
const { msg,
|
|
35574
|
+
const { msg, refuse: refuse3, agentName } = ctx;
|
|
35292
35575
|
if (!deps.config.canRunAgent(chatActorOf(deps.config, msg), agentName)) {
|
|
35293
35576
|
await refuse3(
|
|
35294
|
-
|
|
35295
|
-
|
|
35296
|
-
|
|
35577
|
+
refusalOf(
|
|
35578
|
+
"agent_allowlist",
|
|
35579
|
+
REFUSAL_SENTENCES.agent_allowlist({ agent: agentName, adminsHint: deps.config.adminsHint() })
|
|
35297
35580
|
)
|
|
35298
35581
|
);
|
|
35299
35582
|
return { kind: "refused", reason: "agent_allowlist" };
|
|
@@ -35301,11 +35584,11 @@ async function authorizeAgent(deps, ctx) {
|
|
|
35301
35584
|
return { kind: "allowed" };
|
|
35302
35585
|
}
|
|
35303
35586
|
async function authorizeProfile(deps, ctx) {
|
|
35304
|
-
const { msg,
|
|
35587
|
+
const { msg, refuse: refuse3, agent, resolution } = ctx;
|
|
35305
35588
|
if (resolution.kind === "profile") return { kind: "allowed", profile: resolution.profile };
|
|
35306
35589
|
const { refusal } = resolution;
|
|
35307
35590
|
console.log(`[dispatch] ${msg.threadKey} not started: profile bounded (${agent.name}: ${refusalSummary(refusal)})`);
|
|
35308
|
-
await refuse3("profile_bounded",
|
|
35591
|
+
await refuse3(refusalOf("profile_bounded", profileRefusalReply(agent.name, refusal, deps.config.adminsHint())));
|
|
35309
35592
|
return { kind: "refused", reason: "profile_bounded" };
|
|
35310
35593
|
}
|
|
35311
35594
|
function refusalSummary(refusal) {
|
|
@@ -35384,19 +35667,17 @@ function profileRefusalReply(agentName, refusal, adminsHint) {
|
|
|
35384
35667
|
return `\u{1F6AB} \`${agentName}\` runs on a \`${refusal.needs}\` machine; ${who2} only ${allowed2}. ${capitalize(how)}.`;
|
|
35385
35668
|
}
|
|
35386
35669
|
async function authorizeRepo(deps, ctx) {
|
|
35387
|
-
const { msg,
|
|
35670
|
+
const { msg, refuse: refuse3, card, shell, closeLines, clock: clock4, agent, profile, needsRepo, repoCtx } = ctx;
|
|
35388
35671
|
if (profile.machine === "repo-cold" && needsRepo && !repoCtx.repo) {
|
|
35389
35672
|
if (repoCtx.rejectedRepo) {
|
|
35390
35673
|
const slug = repoCtx.rejectedRepo;
|
|
35391
35674
|
console.log(`[dispatch] ${msg.threadKey} not started: repo not visible (${slug}: GitHub answered 404)`);
|
|
35392
|
-
await refuse3(
|
|
35393
|
-
|
|
35675
|
+
await refuse3(
|
|
35676
|
+
refusalOf("repo_not_visible", REFUSAL_SENTENCES.repo_not_visible({ slug, agent: agent.name })),
|
|
35677
|
+
() => card.done(
|
|
35394
35678
|
shell.close({ kind: "not_started", icon: "\u{1F4E6}", reason: "repo not visible", ...closeLines(clock4(), false) })
|
|
35395
|
-
)
|
|
35396
|
-
|
|
35397
|
-
`\u{1F4E6} \`${slug}\` is not a repository this installation can see \u2014 GitHub answered 404 \u2014 so I did not start ${aRun(agent.name)} for it. The repository is outside the Switchboard GitHub App installation (\`github_repos\` lists the reachable ones), or the name is wrong.`
|
|
35398
|
-
);
|
|
35399
|
-
});
|
|
35679
|
+
)
|
|
35680
|
+
);
|
|
35400
35681
|
return { kind: "refused", reason: "repo_not_visible" };
|
|
35401
35682
|
}
|
|
35402
35683
|
if (repoCtx.unverifiedRepo) {
|
|
@@ -35404,19 +35685,17 @@ async function authorizeRepo(deps, ctx) {
|
|
|
35404
35685
|
console.log(
|
|
35405
35686
|
`[dispatch] ${msg.threadKey} not started: repo could not be verified (${slug}: GitHub did not answer)`
|
|
35406
35687
|
);
|
|
35407
|
-
await refuse3(
|
|
35408
|
-
|
|
35688
|
+
await refuse3(
|
|
35689
|
+
refusalOf("repo_unverified", REFUSAL_SENTENCES.repo_unverified({ slug, agent: agent.name, via: "github" })),
|
|
35690
|
+
() => card.done(
|
|
35409
35691
|
shell.close({
|
|
35410
35692
|
kind: "not_started",
|
|
35411
35693
|
icon: "\u{1F4E6}",
|
|
35412
35694
|
reason: "repo could not be verified",
|
|
35413
35695
|
...closeLines(clock4(), false)
|
|
35414
35696
|
})
|
|
35415
|
-
)
|
|
35416
|
-
|
|
35417
|
-
`\u26A0\uFE0F I couldn't verify \`${slug}\` against GitHub \u2014 it didn't answer \u2014 so I did not start ${aRun(agent.name)} rather than guess which repository you meant. Try again in a minute.`
|
|
35418
|
-
);
|
|
35419
|
-
});
|
|
35697
|
+
)
|
|
35698
|
+
);
|
|
35420
35699
|
return { kind: "refused", reason: "repo_unverified" };
|
|
35421
35700
|
}
|
|
35422
35701
|
}
|
|
@@ -35424,14 +35703,12 @@ async function authorizeRepo(deps, ctx) {
|
|
|
35424
35703
|
const slug = repoCtx.rejectedRepo;
|
|
35425
35704
|
console.log(`[dispatch] ${msg.threadKey} not started: repo not onboarded (${slug})`);
|
|
35426
35705
|
const onboardHint = deps.config.canManageRepos(chatActorOf(deps.config, msg)) ? `Onboard it (\`repo onboard ${slug}\`)` : `Ask ${deps.config.adminsHint()} to onboard it (\`repo onboard ${slug}\`)`;
|
|
35427
|
-
await refuse3(
|
|
35428
|
-
|
|
35706
|
+
await refuse3(
|
|
35707
|
+
refusalOf("repo_not_onboarded", REFUSAL_SENTENCES.repo_not_onboarded({ slug, agent: agent.name, onboardHint })),
|
|
35708
|
+
() => card.done(
|
|
35429
35709
|
shell.close({ kind: "not_started", icon: "\u{1F4E6}", reason: "repo not onboarded", ...closeLines(clock4(), false) })
|
|
35430
|
-
)
|
|
35431
|
-
|
|
35432
|
-
`\u{1F4E6} \`${slug}\` is not onboarded as a resident, so I did not start a *${agent.name}* run for it. ${onboardHint} for a warm, deps-ready environment, or name the repository by URL (https://github.com/${slug}) to run in a cold per-thread sandbox.`
|
|
35433
|
-
);
|
|
35434
|
-
});
|
|
35710
|
+
)
|
|
35711
|
+
);
|
|
35435
35712
|
return { kind: "refused", reason: "repo_not_onboarded" };
|
|
35436
35713
|
}
|
|
35437
35714
|
if (needsRepo && !repoCtx.repo && repoCtx.unverifiedRepo) {
|
|
@@ -35439,55 +35716,48 @@ async function authorizeRepo(deps, ctx) {
|
|
|
35439
35716
|
console.log(
|
|
35440
35717
|
`[dispatch] ${msg.threadKey} not started: repo could not be verified (${slug}: resident registry unreachable)`
|
|
35441
35718
|
);
|
|
35442
|
-
await refuse3(
|
|
35443
|
-
|
|
35719
|
+
await refuse3(
|
|
35720
|
+
refusalOf("repo_unverified", REFUSAL_SENTENCES.repo_unverified({ slug, agent: agent.name, via: "registry" })),
|
|
35721
|
+
() => card.done(
|
|
35444
35722
|
shell.close({
|
|
35445
35723
|
kind: "not_started",
|
|
35446
35724
|
icon: "\u{1F4E6}",
|
|
35447
35725
|
reason: "repo could not be verified",
|
|
35448
35726
|
...closeLines(clock4(), false)
|
|
35449
35727
|
})
|
|
35450
|
-
)
|
|
35451
|
-
|
|
35452
|
-
`\u26A0\uFE0F I couldn't verify that \`${slug}\` is an onboarded repo \u2014 the resident registry didn't answer \u2014 so I did not start a *${agent.name}* run rather than guess which repo you meant. Try again in a minute, or name the repository by URL (https://github.com/${slug}) to run in a cold per-thread sandbox.`
|
|
35453
|
-
);
|
|
35454
|
-
});
|
|
35728
|
+
)
|
|
35729
|
+
);
|
|
35455
35730
|
return { kind: "refused", reason: "repo_unverified" };
|
|
35456
35731
|
}
|
|
35457
35732
|
if (needsRepo && repoCtx.repo && !deps.config.canUseRepo(chatActorOf(deps.config, msg), repoCtx.repo)) {
|
|
35458
35733
|
const repo = repoCtx.repo;
|
|
35459
|
-
await refuse3(
|
|
35460
|
-
|
|
35734
|
+
await refuse3(
|
|
35735
|
+
refusalOf("repo_access", REFUSAL_SENTENCES.repo_access({ repo, adminsHint: deps.config.adminsHint() })),
|
|
35736
|
+
() => card.done(
|
|
35461
35737
|
shell.close({ kind: "not_started", icon: "\u{1F6AB}", reason: "repo access", ...closeLines(clock4(), false) })
|
|
35462
|
-
)
|
|
35463
|
-
|
|
35464
|
-
`\u{1F6AB} You're not on the allowlist for the \`${repo}\` repo environment. Ask ${deps.config.adminsHint()} for access.`
|
|
35465
|
-
);
|
|
35466
|
-
});
|
|
35738
|
+
)
|
|
35739
|
+
);
|
|
35467
35740
|
return { kind: "refused", reason: "repo_access" };
|
|
35468
35741
|
}
|
|
35469
35742
|
return { kind: "allowed" };
|
|
35470
35743
|
}
|
|
35471
|
-
function aRun(agentName) {
|
|
35472
|
-
return `${/^[aeiou]/i.test(agentName) ? "an" : "a"} *${agentName}* run`;
|
|
35473
|
-
}
|
|
35474
35744
|
async function authorizePrHead(ctx) {
|
|
35475
|
-
const { msg,
|
|
35745
|
+
const { msg, refuse: refuse3, card, shell, closeLines, clock: clock4, agent, directives, repoCtx } = ctx;
|
|
35476
35746
|
const preflight = checkPrHeadPreflight({ agent, requestText: directives.text, repoCtx });
|
|
35477
35747
|
if (!preflight.ok) {
|
|
35478
35748
|
console.log(`[review] ${msg.threadKey} not started: PR head unknown (${preflight.where})`);
|
|
35479
|
-
await refuse3(
|
|
35480
|
-
|
|
35749
|
+
await refuse3(
|
|
35750
|
+
refusalOf("pr_head_unknown", preflight.reply),
|
|
35751
|
+
() => card.done(
|
|
35481
35752
|
shell.close({ kind: "not_started", icon: "\u{1F500}", reason: "PR head unknown", ...closeLines(clock4(), false) })
|
|
35482
|
-
)
|
|
35483
|
-
|
|
35484
|
-
});
|
|
35753
|
+
)
|
|
35754
|
+
);
|
|
35485
35755
|
return { kind: "refused", reason: "pr_head_unknown" };
|
|
35486
35756
|
}
|
|
35487
35757
|
return { kind: "allowed" };
|
|
35488
35758
|
}
|
|
35489
35759
|
async function authorizeAttachedHead(deps, ctx) {
|
|
35490
|
-
const { msg,
|
|
35760
|
+
const { msg, refuse: refuse3, card, shell, closeLines, clock: clock4, agent, resume, selection, root } = ctx;
|
|
35491
35761
|
const { executor, resident, binding } = selection;
|
|
35492
35762
|
let repoCtx = ctx.repoCtx;
|
|
35493
35763
|
let verifiedAtAttach = false;
|
|
@@ -35513,14 +35783,12 @@ async function authorizeAttachedHead(deps, ctx) {
|
|
|
35513
35783
|
verifiedAtAttach = true;
|
|
35514
35784
|
headAdopted = true;
|
|
35515
35785
|
} else if (guard.outcome === "refused") {
|
|
35516
|
-
|
|
35517
|
-
await refuse3("branch_moved", async () => {
|
|
35786
|
+
await refuse3(refusalOf("branch_moved", guard.reply), async () => {
|
|
35518
35787
|
if (executor.release) await executor.release("always").catch(() => {
|
|
35519
35788
|
});
|
|
35520
35789
|
await card.done(
|
|
35521
35790
|
shell.close({ kind: "not_started", icon: "\u{1F500}", reason: "branch moved", ...closeLines(clock4(), false) })
|
|
35522
35791
|
);
|
|
35523
|
-
await io.reply(reply);
|
|
35524
35792
|
});
|
|
35525
35793
|
return { kind: "refused", reason: "branch_moved" };
|
|
35526
35794
|
}
|
|
@@ -35533,6 +35801,8 @@ var init_authorize2 = __esm({
|
|
|
35533
35801
|
init_actor();
|
|
35534
35802
|
init_repoContext();
|
|
35535
35803
|
init_reviewRound();
|
|
35804
|
+
init_refusal();
|
|
35805
|
+
init_reply();
|
|
35536
35806
|
}
|
|
35537
35807
|
});
|
|
35538
35808
|
|
|
@@ -35887,44 +36157,6 @@ var init_configAwareness = __esm({
|
|
|
35887
36157
|
}
|
|
35888
36158
|
});
|
|
35889
36159
|
|
|
35890
|
-
// ../../src/core/selfDescription.ts
|
|
35891
|
-
function residentCapNote(cap) {
|
|
35892
|
-
return cap === void 0 ? RESIDENT_CAP_UNKNOWN_NOTE : `capped at ${cap} residents`;
|
|
35893
|
-
}
|
|
35894
|
-
function buildClause(build) {
|
|
35895
|
-
if (!build) return "";
|
|
35896
|
-
const commit = build.commit === "unknown" ? "an unstamped build" : `build ${build.commit.slice(0, 8)}`;
|
|
35897
|
-
return ` (version ${build.version}, ${commit} \u2014 \`status show\` reports it live)`;
|
|
35898
|
-
}
|
|
35899
|
-
function selfDescriptionBlock(agents, organization, caps, residentCap, build) {
|
|
35900
|
-
const agentLines = Object.values(agents).map((a) => `\`${a.name}\` \u2014 ${a.description}`).join("; ");
|
|
35901
|
-
const surfaces = caps.ingress ? "plus a CLI, an HTTP /api and an MCP surface" : "plus a CLI and an HTTP /api surface";
|
|
35902
|
-
const repos = caps.residents ? `Repos: the coding and review agents work in a "resident" \u2014 an always-warm per-repo environment (a mirror clone, installed deps, a built checkout, refreshed on a schedule and snapshotted) on the resident Worker; each run attaches a worktree on its branch in seconds. An admin onboards a repo with \`repo onboard <owner/name>\` (install/build/test commands are detected from the repo root, \`--install/--build/--test\` override), sees the fleet with \`repo list\`, changes it with \`repo reconfigure\` / \`repo rebuild\` / \`repo offboard\`; the fleet is ${residentCapNote(residentCap)} (\`--evict-coldest\` frees a slot). There is no separate "priority repos" setting: onboarded = warm. Repos that are not onboarded run in a cold per-thread workspace.` : `Repos: this installation has no resident (always-warm) repo environments \u2014 the coding and review agents clone the repository a request names (an https://github.com/owner/name URL or a PR link) into a per-thread workspace ${WORKSPACE_WHERE[caps.execution]} for the run. Nothing is kept warm between runs, and there is nothing to onboard.`;
|
|
35903
|
-
const history = caps.runHistory ? ", kept as run history" : " while it runs (this installation keeps no run history: a finished run's page goes away about a minute later)";
|
|
35904
|
-
const memory = caps.memory ? " Cross-session memory is per org/repo/channel/user (`memory list` / `memory forget`)." : " This installation has no cross-session memory: a run knows only its own thread.";
|
|
35905
|
-
return [
|
|
35906
|
-
`${SELF_DESCRIPTION_HEADER} you are Switchboard${buildClause(build)}, the agent gateway of the ${organization} organization. A message arrives on a channel (Slack \u2014 an @-mention, thread follow-ups stay on the same agent \u2014 ${surfaces}), is routed to an agent, runs on a configured provider/model, and executes tools through an executor.`,
|
|
35907
|
-
`Agents: ${agentLines}. Users pick one with an inline \`agent:<name>\` directive (default: general); \`model:\` / \`effort:\` ride the same way (see the runtime-config block).`,
|
|
35908
|
-
repos,
|
|
35909
|
-
`Runs${caps.memory ? " and memory" : ""}: every run (agent or operator command) has a live page under /runs with its events${history}; runs can be stopped from there.${memory} \`help\` lists every chat command.`,
|
|
35910
|
-
`Source of truth: the code lives in the GitHub repo ${SWITCHBOARD_REPO} \u2014 README.md (architecture + agents), AGENTS.md (invariants + map), and one behavioral spec per feature under docs/reference/specs/*.md (e.g. docs/reference/specs/resident-repos.md, docs/reference/specs/routing-and-config.md, docs/reference/specs/github-tools.md). For a question about how Switchboard works beyond this block, read those files with the GitHub tools when you have them (github_tree / github_file on ${SWITCHBOARD_REPO}) \u2014 they are private, so a public-web fetch 404s; if you have no GitHub tools, answer from this block and point at that path. Never describe yourself as stateless, tool-less by design, or unable to know your own workings.`
|
|
35911
|
-
].join("\n");
|
|
35912
|
-
}
|
|
35913
|
-
var SELF_DESCRIPTION_HEADER, SWITCHBOARD_REPO, RESIDENT_CAP_UNKNOWN_NOTE, WORKSPACE_WHERE;
|
|
35914
|
-
var init_selfDescription = __esm({
|
|
35915
|
-
"../../src/core/selfDescription.ts"() {
|
|
35916
|
-
"use strict";
|
|
35917
|
-
SELF_DESCRIPTION_HEADER = "About Switchboard (yourself):";
|
|
35918
|
-
SWITCHBOARD_REPO = "coreplanelabs/switchboard";
|
|
35919
|
-
RESIDENT_CAP_UNKNOWN_NOTE = "capped (`repo list` shows the cap)";
|
|
35920
|
-
WORKSPACE_WHERE = {
|
|
35921
|
-
local: "on the bot host",
|
|
35922
|
-
e2b: "in an E2B micro-VM",
|
|
35923
|
-
cloudflare: "in a Cloudflare sandbox"
|
|
35924
|
-
};
|
|
35925
|
-
}
|
|
35926
|
-
});
|
|
35927
|
-
|
|
35928
36160
|
// ../../src/core/customInstructions.ts
|
|
35929
36161
|
function customInstructionsBlock(scopes) {
|
|
35930
36162
|
const channel = scopes.channel.instructions?.trim();
|
|
@@ -36307,7 +36539,7 @@ async function reattachWorkspace2(deps, ctx) {
|
|
|
36307
36539
|
}
|
|
36308
36540
|
}
|
|
36309
36541
|
async function attachWorkspace(deps, ctx) {
|
|
36310
|
-
const { msg,
|
|
36542
|
+
const { msg, refuse: refuse3, card, shell, closeLines, clock: clock4, agent, profile, repoCtx, root, reattach, stopSignal } = ctx;
|
|
36311
36543
|
const { remainingMs } = ctx;
|
|
36312
36544
|
let round2;
|
|
36313
36545
|
try {
|
|
@@ -36325,14 +36557,12 @@ async function attachWorkspace(deps, ctx) {
|
|
|
36325
36557
|
} catch (err2) {
|
|
36326
36558
|
if (err2 instanceof ResidentNeedsRefError) {
|
|
36327
36559
|
const repo = repoCtx.repo;
|
|
36328
|
-
await refuse3(
|
|
36329
|
-
|
|
36560
|
+
await refuse3(
|
|
36561
|
+
refusalOf("which_branch", REFUSAL_SENTENCES.which_branch({ repo })),
|
|
36562
|
+
() => card.done(
|
|
36330
36563
|
shell.close({ kind: "not_started", icon: "\u{1F33F}", reason: "which branch?", ...closeLines(clock4(), false) })
|
|
36331
|
-
)
|
|
36332
|
-
|
|
36333
|
-
`\u{1F33F} Which branch of \`${repo}\` should this thread work on? No branch is bound yet \u2014 reply naming one (e.g. "on main" or "on branch fix/login") and I'll pick it up from there.`
|
|
36334
|
-
);
|
|
36335
|
-
});
|
|
36564
|
+
)
|
|
36565
|
+
);
|
|
36336
36566
|
return { kind: "refused", reason: "which_branch" };
|
|
36337
36567
|
}
|
|
36338
36568
|
if (err2 instanceof WorkspaceReattachRefusedError) return { kind: "reattach_refused", why: err2.why };
|
|
@@ -36504,6 +36734,7 @@ var init_provision = __esm({
|
|
|
36504
36734
|
init_types5();
|
|
36505
36735
|
init_record();
|
|
36506
36736
|
init_reply();
|
|
36737
|
+
init_refusal();
|
|
36507
36738
|
init_messages();
|
|
36508
36739
|
init_route();
|
|
36509
36740
|
init_roster();
|
|
@@ -36827,12 +37058,12 @@ async function prepareRelaunch(deps, ctx) {
|
|
|
36827
37058
|
);
|
|
36828
37059
|
round2 = reattached.round;
|
|
36829
37060
|
}
|
|
36830
|
-
const
|
|
36831
|
-
let written =
|
|
37061
|
+
const counted2 = { ...facts2, relaunches: relaunches + 1 };
|
|
37062
|
+
let written = counted2;
|
|
36832
37063
|
let bearer;
|
|
36833
37064
|
if (deps.runBearers) {
|
|
36834
37065
|
const rotated = deps.runBearers.rotate(ctx.runId, (secretHash) => {
|
|
36835
|
-
written = { ...
|
|
37066
|
+
written = { ...counted2, bearerHash: secretHash };
|
|
36836
37067
|
ctx.saveFacts(written);
|
|
36837
37068
|
});
|
|
36838
37069
|
if (!rotated.ok)
|
|
@@ -37746,12 +37977,56 @@ var init_botHostContainer = __esm({
|
|
|
37746
37977
|
});
|
|
37747
37978
|
|
|
37748
37979
|
// ../../src/core/harness/windDown.ts
|
|
37980
|
+
function windDownEndingOf(writeUp, text, writeUpFailed, ended = "failed") {
|
|
37981
|
+
const failed = writeUpFailed !== void 0 ? { writeUpFailed } : {};
|
|
37982
|
+
if (writeUp?.kind === "time") return { kind: "time", text, ...failed };
|
|
37983
|
+
if (writeUp?.kind === "turns") return { kind: "turns", pace: writeUp.pace, text, ...failed };
|
|
37984
|
+
if (writeUp?.kind === "soft") return { kind: "soft", text, ...failed };
|
|
37985
|
+
if (writeUpFailed !== void 0) return { kind: "unlabelled", text, writeUpFailed, ended };
|
|
37986
|
+
return void 0;
|
|
37987
|
+
}
|
|
37988
|
+
function windDownAnswer(ending, maxMinutes, facts2) {
|
|
37989
|
+
switch (ending.kind) {
|
|
37990
|
+
case "time":
|
|
37991
|
+
return timeBudgetAnswer(ending.text, maxMinutes, ending.writeUpFailed, facts2);
|
|
37992
|
+
case "turns":
|
|
37993
|
+
return turnGuardAnswer(ending.text, ending.pace, ending.writeUpFailed, facts2);
|
|
37994
|
+
case "soft":
|
|
37995
|
+
return softStopAnswer(ending.text, ending.writeUpFailed, facts2);
|
|
37996
|
+
case "unlabelled":
|
|
37997
|
+
return unlabelledAnswer(ending.text, ending.writeUpFailed, ending.ended, facts2);
|
|
37998
|
+
}
|
|
37999
|
+
}
|
|
38000
|
+
function established(facts2, advice) {
|
|
38001
|
+
const then = advice ? ` \u2014 ${advice}` : "";
|
|
38002
|
+
const w = facts2?.workspace;
|
|
38003
|
+
let tree = "";
|
|
38004
|
+
if (w?.kind === "unmeasured") tree = `The workspace could not be measured, so work may sit unpushed there${then}.`;
|
|
38005
|
+
else if (w?.kind === "clean")
|
|
38006
|
+
tree = `The tree was clean${w.branch ? ` and \`${w.branch}\` held no unpushed commits` : " with no unpushed commits"}${w.head ? ` \u2014 its head \`${shortSha(w.head)}\` is on the remote` : ""}.`;
|
|
38007
|
+
else if (w?.kind === "salvaged")
|
|
38008
|
+
tree = `What the tree held was pushed to \`${w.branch}\`${w.head ? ` at \`${shortSha(w.head)}\`` : ""} by the budget salvage, unreviewed \u2014 a follow-up starts from it.`;
|
|
38009
|
+
else if (w?.kind === "left")
|
|
38010
|
+
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${then}.` : `${counted(w)} were left in the tree, which is torn down since a command may still be running in it${then}.`;
|
|
38011
|
+
const description = facts2?.description === "submitted" ? "The PR description was submitted." : facts2?.description === "not_submitted" ? "No PR description was submitted." : "";
|
|
38012
|
+
return [tree, description].filter((s) => s !== "").join(" ");
|
|
38013
|
+
}
|
|
38014
|
+
function nothingWritten(facts2, advice) {
|
|
38015
|
+
const known = established(facts2, advice);
|
|
38016
|
+
if (known) return ` ${known}`;
|
|
38017
|
+
if (facts2?.workspace?.kind === "none") return advice ? ` ${advice.charAt(0).toUpperCase()}${advice.slice(1)}.` : "";
|
|
38018
|
+
return ` Partial work may exist in the workspace${advice ? ` \u2014 ${advice}` : ""}.`;
|
|
38019
|
+
}
|
|
38020
|
+
function beforeFindings(facts2) {
|
|
38021
|
+
const known = established(facts2, void 0);
|
|
38022
|
+
return known ? `. ${known} Findings so far:` : " \u2014 findings so far:";
|
|
38023
|
+
}
|
|
37749
38024
|
function elapsedMinutes(ms) {
|
|
37750
38025
|
const minutes = Math.round(ms / 6e4);
|
|
37751
38026
|
if (minutes < 1) return "under a minute";
|
|
37752
38027
|
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
37753
38028
|
}
|
|
37754
|
-
var HARD_STOP_MESSAGE, CONTINUE_PROMPT, wrapUpNote, wrapUpInstruction, WRITE_UP_REQUEST, timeBudgetInstruction, turnGuardInstruction, SOFT_STOP_INSTRUCTION, turnGuardPace, MODEL_CALL_IN_FLIGHT, timeBudgetNote, toolCutNote, turnGuardNote, softStopNote, hardStopNote, windDownFailureNote, finaleTimedOutNote, wrapUpUndeliveredNote, wrapUpNeverPostedNote, wrapUpWriteFailedNote, ABORT_DROPPED_NOTE, abortWriteFailedNote, abortReaskedNote, abortFailedAfterEndNote, abortUnheardAtEndNote, timesWord, wrapUpName, finaleAbortReason, noWriteUp, timeBudgetAnswer, turnGuardAnswer, unlabelledAnswer, softStopAnswer;
|
|
38029
|
+
var HARD_STOP_MESSAGE, CONTINUE_PROMPT, wrapUpNote, wrapUpInstruction, WRITE_UP_REQUEST, timeBudgetInstruction, turnGuardInstruction, SOFT_STOP_INSTRUCTION, turnGuardPace, MODEL_CALL_IN_FLIGHT, timeBudgetNote, toolCutNote, turnGuardNote, softStopNote, hardStopNote, windDownFailureNote, finaleWaitNote, finaleTimedOutNote, wrapUpUndeliveredNote, wrapUpNeverPostedNote, wrapUpWriteFailedNote, ABORT_DROPPED_NOTE, abortWriteFailedNote, abortReaskedNote, abortFailedAfterEndNote, abortUnheardAtEndNote, timesWord, wrapUpName, finaleAbortReason, noWriteUp, TIME_ADVICE, TURN_ADVICE, shortSha, counted, timeBudgetAnswer, turnGuardAnswer, unlabelledAnswer, softStopAnswer;
|
|
37755
38030
|
var init_windDown = __esm({
|
|
37756
38031
|
"../../src/core/harness/windDown.ts"() {
|
|
37757
38032
|
"use strict";
|
|
@@ -37771,6 +38046,7 @@ var init_windDown = __esm({
|
|
|
37771
38046
|
softStopNote = () => "soft stop \u2014 no further steps, writing up findings so far";
|
|
37772
38047
|
hardStopNote = () => "hard stop \u2014 run aborted, no summary written";
|
|
37773
38048
|
windDownFailureNote = (error, closes = "run") => `the model call failed during the wind-down (${error}); the ${closes} closes with its findings so far`;
|
|
38049
|
+
finaleWaitNote = (reason2, doing, interruptUnanswered, closes = "run") => `the finale bound ended the wait${doing ? ` while ${doing}` : ""}${interruptUnanswered ? ", the loop-end interrupt unanswered" : ""} (${reason2}); the ${closes} closes with its findings so far`;
|
|
37774
38050
|
finaleTimedOutNote = (closes = "run") => `finale timed out \u2014 closing the ${closes} without a write-up`;
|
|
37775
38051
|
wrapUpUndeliveredNote = (kind, closes = "run") => `the ${wrapUpName(kind)} wrap-up instruction never reached pi \u2014 held behind a prompt in doubt when pi settled \u2014 so the ${closes} closes on pi's own answer, unlabelled`;
|
|
37776
38052
|
wrapUpNeverPostedNote = (kind, closes = "run", ended = "finished") => `the ${wrapUpName(kind)} wrap-up instruction was never posted \u2014 ${ended === "finale" ? "the loop-end interrupt was never answered, the finale bound ending the wait" : `the loop-end interrupt found the session idle, the execution having ${ended === "failed" ? "failed on the provider" : "finished on its own"}`} \u2014 so the ${closes} closes on OpenCode's own answer, unlabelled`;
|
|
@@ -37792,25 +38068,29 @@ var init_windDown = __esm({
|
|
|
37792
38068
|
wrapUpName = (kind) => kind === "time" ? "time-budget" : kind === "turns" ? "turn-guard" : "soft-stop";
|
|
37793
38069
|
finaleAbortReason = (boundMs) => `aborted at the finale bound (${elapsedMinutes(boundMs)})`;
|
|
37794
38070
|
noWriteUp = (failed) => failed ? `; the model call failed during the wind-down (${failed}), so no write-up came` : "";
|
|
37795
|
-
|
|
38071
|
+
TIME_ADVICE = "narrow the task and try again";
|
|
38072
|
+
TURN_ADVICE = "look for a retry loop in the run's events before trying again";
|
|
38073
|
+
shortSha = (sha) => sha.slice(0, 7);
|
|
38074
|
+
counted = (w) => `${w.uncommitted} uncommitted change(s) and ${w.unpushed} unpushed commit(s)`;
|
|
38075
|
+
timeBudgetAnswer = (text, maxMinutes, writeUpFailed, facts2) => text ? `\u26A0\uFE0F _Hit the ${maxMinutes}-minute budget before finishing${beforeFindings(facts2)}_
|
|
37796
38076
|
|
|
37797
|
-
${text}` : `Stopped at the ${maxMinutes}-minute budget without finishing${noWriteUp(writeUpFailed)}
|
|
37798
|
-
turnGuardAnswer = (text, pace, writeUpFailed) => text ? `\u26A0\uFE0F _Stopped after ${pace} \u2014 that pace looks like a loop; findings so far:_
|
|
38077
|
+
${text}` : `Stopped at the ${maxMinutes}-minute budget without finishing${noWriteUp(writeUpFailed)}.${nothingWritten(facts2, TIME_ADVICE)}`;
|
|
38078
|
+
turnGuardAnswer = (text, pace, writeUpFailed, facts2) => text ? `\u26A0\uFE0F _Stopped after ${pace} \u2014 that pace looks like a loop${established(facts2, void 0) ? beforeFindings(facts2) : "; findings so far:"}_
|
|
37799
38079
|
|
|
37800
|
-
${text}` : `Stopped after ${pace} \u2014 that pace looks like a loop \u2014 without finishing${noWriteUp(writeUpFailed)}
|
|
37801
|
-
unlabelledAnswer = (text, writeUpFailed, ended = "failed") => {
|
|
38080
|
+
${text}` : `Stopped after ${pace} \u2014 that pace looks like a loop \u2014 without finishing${noWriteUp(writeUpFailed)}.${nothingWritten(facts2, TURN_ADVICE)}`;
|
|
38081
|
+
unlabelledAnswer = (text, writeUpFailed, ended = "failed", facts2) => {
|
|
37802
38082
|
if (!writeUpFailed) return text || "_(no response)_";
|
|
37803
38083
|
if (ended === "finale")
|
|
37804
38084
|
return text ? `${text}
|
|
37805
38085
|
|
|
37806
|
-
\u26A0\uFE0F _The write-up never started: the loop-end interrupt went unanswered and the finale bound ended the wait (${writeUpFailed}); this is the last answer before it._` : `\u26A0\uFE0F The write-up never started: the loop-end interrupt went unanswered and the finale bound ended the wait (${writeUpFailed})
|
|
38086
|
+
\u26A0\uFE0F _The write-up never started: the loop-end interrupt went unanswered and the finale bound ended the wait (${writeUpFailed}); this is the last answer before it._` : `\u26A0\uFE0F The write-up never started: the loop-end interrupt went unanswered and the finale bound ended the wait (${writeUpFailed}).${nothingWritten(facts2, void 0)}`;
|
|
37807
38087
|
return text ? `${text}
|
|
37808
38088
|
|
|
37809
|
-
\u26A0\uFE0F _The model call that followed failed (${writeUpFailed}); this is the last answer before it._` : `\u26A0\uFE0F The model call failed (${writeUpFailed}) and no answer came
|
|
38089
|
+
\u26A0\uFE0F _The model call that followed failed (${writeUpFailed}); this is the last answer before it._` : `\u26A0\uFE0F The model call failed (${writeUpFailed}) and no answer came.${nothingWritten(facts2, void 0)}`;
|
|
37810
38090
|
};
|
|
37811
|
-
softStopAnswer = (text, writeUpFailed) => text ? `\u23F9 _Stopped early by an operator (soft stop)
|
|
38091
|
+
softStopAnswer = (text, writeUpFailed, facts2) => text ? `\u23F9 _Stopped early by an operator (soft stop)${beforeFindings(facts2)}_
|
|
37812
38092
|
|
|
37813
|
-
${text}` : `\u23F9 Stopped early by an operator (soft stop) before any findings were written${noWriteUp(writeUpFailed)}
|
|
38093
|
+
${text}` : `\u23F9 Stopped early by an operator (soft stop) before any findings were written${noWriteUp(writeUpFailed)}.${nothingWritten(facts2, void 0)}`;
|
|
37814
38094
|
}
|
|
37815
38095
|
});
|
|
37816
38096
|
|
|
@@ -40777,6 +41057,7 @@ ${stagedLine}` : prompt;
|
|
|
40777
41057
|
throw new Error(`pi exited before the run settled${tail2.trim() ? `: ${redactAndCap(tail2.trim(), 400)}` : ""}`);
|
|
40778
41058
|
};
|
|
40779
41059
|
let answer;
|
|
41060
|
+
let ending;
|
|
40780
41061
|
if (finaleAborted && !settled && (containerSaid ?? transportLost) !== void 0) {
|
|
40781
41062
|
const met = containerSaid ?? transportLost;
|
|
40782
41063
|
note(
|
|
@@ -40800,7 +41081,8 @@ ${stagedLine}` : prompt;
|
|
|
40800
41081
|
);
|
|
40801
41082
|
}
|
|
40802
41083
|
const text = bridge.answer() ?? "";
|
|
40803
|
-
|
|
41084
|
+
ending = windDownEndingOf(writeUp ?? (stopMode === "soft" ? { kind: "soft" } : void 0), text, writeUpFailed);
|
|
41085
|
+
answer = ending ? windDownAnswer(ending, run2.agent.maxMinutes) : unlabelledAnswer(text, void 0);
|
|
40804
41086
|
}
|
|
40805
41087
|
agentSpan?.end(hardStopped ? "error" : "ok");
|
|
40806
41088
|
const root = paths;
|
|
@@ -40977,10 +41259,8 @@ ${stagedLine}` : prompt;
|
|
|
40977
41259
|
throw new Error(`the model call failed: ${turnError}`);
|
|
40978
41260
|
}
|
|
40979
41261
|
const text = bridge.answer() ?? "";
|
|
40980
|
-
|
|
40981
|
-
|
|
40982
|
-
if (writeUp?.kind === "soft") return softStopAnswer(text, writeUpFailed);
|
|
40983
|
-
return text || "_(no response)_";
|
|
41262
|
+
const turnEnding = windDownEndingOf(writeUp, text, writeUpFailed);
|
|
41263
|
+
return turnEnding ? windDownAnswer(turnEnding, input.maxMinutes) : unlabelledAnswer(text, void 0);
|
|
40984
41264
|
} catch (err2) {
|
|
40985
41265
|
turnFailed = true;
|
|
40986
41266
|
if (err2 instanceof HarnessControlFileLostError) note("harness_error", err2.message);
|
|
@@ -40993,7 +41273,7 @@ ${stagedLine}` : prompt;
|
|
|
40993
41273
|
turnSpan?.end(hardStopped || bypass || turnFailed ? "error" : "ok");
|
|
40994
41274
|
}
|
|
40995
41275
|
};
|
|
40996
|
-
return { answer, followUp, remainingMs: () => deadline - now(), end };
|
|
41276
|
+
return { answer, ...ending ? { ending } : {}, followUp, remainingMs: () => deadline - now(), end };
|
|
40997
41277
|
} catch (err2) {
|
|
40998
41278
|
loopEnded = true;
|
|
40999
41279
|
requeueUnechoed();
|
|
@@ -42088,6 +42368,10 @@ async function runLoop(deps, ctx) {
|
|
|
42088
42368
|
};
|
|
42089
42369
|
const restoredDescription = PrDescriptionSchema.safeParse(restored.prDescription);
|
|
42090
42370
|
let prDescription = restoredDescription.success ? restoredDescription.data : void 0;
|
|
42371
|
+
if (!restoredDescription.success && restored.prDescription !== void 0) {
|
|
42372
|
+
const why = restoredDescription.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
|
|
42373
|
+
console.log(`[resume] ${msg.threadKey} the restored PR description no longer parses and is dropped \u2014 ${why}`);
|
|
42374
|
+
}
|
|
42091
42375
|
const onPrDescription = (d) => {
|
|
42092
42376
|
prDescription = d;
|
|
42093
42377
|
ledgerRun?.setState({ prDescription: d });
|
|
@@ -42151,6 +42435,8 @@ async function runLoop(deps, ctx) {
|
|
|
42151
42435
|
let runDiagnosis;
|
|
42152
42436
|
let harnessSession;
|
|
42153
42437
|
let leaseSpentDuringRelaunch;
|
|
42438
|
+
let windDownEnding;
|
|
42439
|
+
let salvagedTo;
|
|
42154
42440
|
let relaunchEndedRun = false;
|
|
42155
42441
|
const tailSkipped = () => run2.control.requested === "hard" || relaunchEndedRun;
|
|
42156
42442
|
const releaseWorkspace = (span) => {
|
|
@@ -42199,6 +42485,7 @@ async function runLoop(deps, ctx) {
|
|
|
42199
42485
|
skills: deps.skills,
|
|
42200
42486
|
github: githubCapabilityFor(deps, chatActorOf(deps.config, msg)),
|
|
42201
42487
|
agentName: agent.name,
|
|
42488
|
+
...repoCtx.repo ? { repo: repoCtx.repo } : {},
|
|
42202
42489
|
...spawn7 ? { spawn: spawn7 } : {},
|
|
42203
42490
|
...runs ? { runs } : {},
|
|
42204
42491
|
...steer ? { steer } : {},
|
|
@@ -42280,6 +42567,7 @@ async function runLoop(deps, ctx) {
|
|
|
42280
42567
|
await judge4.end(facts2, container);
|
|
42281
42568
|
}
|
|
42282
42569
|
} else noteUnknownWord("the run finished on the answer it already had");
|
|
42570
|
+
windDownEnding = windDownEndingUnder(finish2.answer, loopEnding);
|
|
42283
42571
|
answer = answerUnderEnding(finish2.answer, loopEnding, agent.maxMinutes);
|
|
42284
42572
|
} else {
|
|
42285
42573
|
if (!deps.harness)
|
|
@@ -42410,7 +42698,8 @@ async function runLoop(deps, ctx) {
|
|
|
42410
42698
|
harnessDeps.registry.forget(run2.id);
|
|
42411
42699
|
relaunchEndedRun = true;
|
|
42412
42700
|
onEvent({ type: "run_note", kind: "time_budget_exhausted", summary: decision.why });
|
|
42413
|
-
|
|
42701
|
+
windDownEnding = { kind: "time", text: "" };
|
|
42702
|
+
leaseSpentDuringRelaunch = windDownAnswer(windDownEnding, agent.maxMinutes);
|
|
42414
42703
|
break;
|
|
42415
42704
|
}
|
|
42416
42705
|
if (decision.round !== void 0) {
|
|
@@ -42427,6 +42716,7 @@ async function runLoop(deps, ctx) {
|
|
|
42427
42716
|
}
|
|
42428
42717
|
}
|
|
42429
42718
|
answer = harnessSession?.answer ?? leaseSpentDuringRelaunch ?? HARD_STOP_MESSAGE;
|
|
42719
|
+
if (harnessSession?.ending !== void 0) windDownEnding = harnessSession.ending;
|
|
42430
42720
|
}
|
|
42431
42721
|
if (isPrReview && repoCtx.repo && repoCtx.pr !== void 0 && !tailSkipped() && postedBefore === void 0) {
|
|
42432
42722
|
const settled = await settleReviewedHead({
|
|
@@ -42531,6 +42821,11 @@ async function runLoop(deps, ctx) {
|
|
|
42531
42821
|
onEvent({ type: "run_note", kind: "budget_salvage", summary: salvaged.summary });
|
|
42532
42822
|
if (salvaged.pushed && "head" in salvaged && salvaged.head !== void 0 && !("skipped" in target))
|
|
42533
42823
|
onEvent({ type: "pushed_head", ref: target.branch, sha: salvaged.head, by: "salvage" });
|
|
42824
|
+
if (salvaged.pushed && !("skipped" in target))
|
|
42825
|
+
salvagedTo = {
|
|
42826
|
+
branch: target.branch,
|
|
42827
|
+
..."head" in salvaged && salvaged.head !== void 0 ? { head: salvaged.head } : {}
|
|
42828
|
+
};
|
|
42534
42829
|
if (salvaged.pushed) await observeWorkspaceNow();
|
|
42535
42830
|
}
|
|
42536
42831
|
let descriptionTurnRan = false;
|
|
@@ -42570,6 +42865,28 @@ async function runLoop(deps, ctx) {
|
|
|
42570
42865
|
}
|
|
42571
42866
|
await endHarness();
|
|
42572
42867
|
const leftBehind = isCodingPrRun && !tailSkipped() ? workLeftBehindOf({ uncommittedChanges: observedUncommitted, unpushedCommits: observedUnpushed }) : void 0;
|
|
42868
|
+
const endingFacts = () => {
|
|
42869
|
+
if (profile.machine === "none") return { workspace: { kind: "none" } };
|
|
42870
|
+
if (!isCodingPrRun || tailSkipped()) return { workspace: { kind: "unread" } };
|
|
42871
|
+
const branch2 = observedBranch ?? observedCheckedOut;
|
|
42872
|
+
let workspace;
|
|
42873
|
+
if (salvagedTo !== void 0) workspace = { kind: "salvaged", ...salvagedTo };
|
|
42874
|
+
else if (observedUncommitted === void 0 || observedUnpushed === void 0) workspace = { kind: "unmeasured" };
|
|
42875
|
+
else if (observedUncommitted === 0 && observedUnpushed === 0)
|
|
42876
|
+
workspace = {
|
|
42877
|
+
kind: "clean",
|
|
42878
|
+
...branch2 !== void 0 ? { branch: branch2 } : {},
|
|
42879
|
+
...observedHead !== void 0 ? { head: observedHead } : {}
|
|
42880
|
+
};
|
|
42881
|
+
else
|
|
42882
|
+
workspace = {
|
|
42883
|
+
kind: "left",
|
|
42884
|
+
uncommitted: observedUncommitted,
|
|
42885
|
+
unpushed: observedUnpushed,
|
|
42886
|
+
fate: commandInFlight ? "torn_down" : profile.machine === "repo-resident" ? "discarded" : "kept"
|
|
42887
|
+
};
|
|
42888
|
+
return { workspace, description: prDescription !== void 0 ? "submitted" : "not_submitted" };
|
|
42889
|
+
};
|
|
42573
42890
|
if (leftBehind) {
|
|
42574
42891
|
registry.publish(run2.id, {
|
|
42575
42892
|
type: "run_note",
|
|
@@ -42613,6 +42930,8 @@ async function runLoop(deps, ctx) {
|
|
|
42613
42930
|
if (baseline) await root.span("run.reading_diff_join", () => baseline);
|
|
42614
42931
|
const description = descriptionArtifact;
|
|
42615
42932
|
if (description) await root.span("run.pr_description_join", () => description);
|
|
42933
|
+
if (windDownEnding !== void 0 && answer === windDownAnswer(windDownEnding, agent.maxMinutes))
|
|
42934
|
+
answer = windDownAnswer(windDownEnding, agent.maxMinutes, endingFacts());
|
|
42616
42935
|
const acceptedAnswer = markdownOutput.parse(answer);
|
|
42617
42936
|
const rawAnswer = acceptedAnswer.ok && acceptedAnswer.changed ? answer : void 0;
|
|
42618
42937
|
if (acceptedAnswer.ok) answer = acceptedAnswer.value;
|
|
@@ -42750,17 +43069,18 @@ function describeEnding(ending) {
|
|
|
42750
43069
|
return `a write-up: ${ending.summary}`;
|
|
42751
43070
|
}
|
|
42752
43071
|
}
|
|
43072
|
+
function windDownEndingUnder(text, ending) {
|
|
43073
|
+
if (ending.kind === "soft_stop") return { kind: "soft", text };
|
|
43074
|
+
if (ending.kind === "written_up" && ending.note === "time_budget_exhausted") return { kind: "time", text };
|
|
43075
|
+
return void 0;
|
|
43076
|
+
}
|
|
42753
43077
|
function answerUnderEnding(text, ending, maxMinutes) {
|
|
42754
|
-
|
|
42755
|
-
if (
|
|
42756
|
-
|
|
42757
|
-
|
|
42758
|
-
return timeBudgetAnswer(text, maxMinutes);
|
|
42759
|
-
default:
|
|
42760
|
-
return text ? `\u26A0\uFE0F _${ending.summary}_
|
|
43078
|
+
const windDown = windDownEndingUnder(text, ending);
|
|
43079
|
+
if (windDown !== void 0) return windDownAnswer(windDown, maxMinutes);
|
|
43080
|
+
if (ending.kind !== "written_up") return text || "_(no response)_";
|
|
43081
|
+
return text ? `\u26A0\uFE0F _${ending.summary}_
|
|
42761
43082
|
|
|
42762
43083
|
${text}` : `\u26A0\uFE0F ${ending.summary}`;
|
|
42763
|
-
}
|
|
42764
43084
|
}
|
|
42765
43085
|
function unknownHarnessWordOf(state) {
|
|
42766
43086
|
if (typeof state !== "object" || state === null) return void 0;
|
|
@@ -43034,7 +43354,13 @@ async function plan(deps, input) {
|
|
|
43034
43354
|
}
|
|
43035
43355
|
let text;
|
|
43036
43356
|
try {
|
|
43037
|
-
|
|
43357
|
+
const file = await deps.readFile(entry.repo, request2.planPath, base, { maxChars: PLAN_MAX_CHARS });
|
|
43358
|
+
if (file.truncated === true)
|
|
43359
|
+
return {
|
|
43360
|
+
ok: false,
|
|
43361
|
+
reply: `\u{1F6AB} The plan \`${request2.planPath}\` is longer than ${PLAN_MAX_CHARS.toLocaleString("en-US")} characters at \`${base}\` in ${entry.repo}; its later units would be lost, so nothing was run \u2014 split the plan.`
|
|
43362
|
+
};
|
|
43363
|
+
text = file.content;
|
|
43038
43364
|
} catch (err2) {
|
|
43039
43365
|
return {
|
|
43040
43366
|
ok: false,
|
|
@@ -43384,10 +43710,10 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
43384
43710
|
);
|
|
43385
43711
|
if (!pre.ok) {
|
|
43386
43712
|
console.log(`[ship] ${msg.threadKey} not started: ${pre.where}`);
|
|
43387
|
-
await refuse3(
|
|
43388
|
-
|
|
43389
|
-
|
|
43390
|
-
|
|
43713
|
+
await refuse3(
|
|
43714
|
+
refusalOf("ship_preflight", pre.reply),
|
|
43715
|
+
() => card.done(shell.close({ kind: "refused", icon: "\u{1F6AB}", reason: pre.card, ...closeLines(clock4(), false) }))
|
|
43716
|
+
);
|
|
43391
43717
|
return;
|
|
43392
43718
|
}
|
|
43393
43719
|
const entry = pre.entry;
|
|
@@ -43530,12 +43856,19 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
43530
43856
|
if (!held2.ok) {
|
|
43531
43857
|
const reason2 = `budget ${caps.maxMinutes} min cannot hold the ship loop (${caps.maxRounds} review rounds need ${held2.need} min)`;
|
|
43532
43858
|
console.log(`[ship] ${msg.threadKey} not started: ${reason2}`);
|
|
43533
|
-
await refuse3(
|
|
43534
|
-
|
|
43535
|
-
|
|
43536
|
-
|
|
43537
|
-
|
|
43538
|
-
|
|
43859
|
+
await refuse3(
|
|
43860
|
+
refusalOf(
|
|
43861
|
+
"ship_budget",
|
|
43862
|
+
REFUSAL_SENTENCES.ship_budget({
|
|
43863
|
+
maxMinutes: caps.maxMinutes,
|
|
43864
|
+
maxRounds: caps.maxRounds,
|
|
43865
|
+
need: held2.need,
|
|
43866
|
+
provision: ALLOWANCES.provision,
|
|
43867
|
+
coding: ASKS.coding
|
|
43868
|
+
})
|
|
43869
|
+
),
|
|
43870
|
+
() => card.done(shell.close({ kind: "refused", icon: "\u{1F6AB}", reason: reason2, ...closeLines(clock4(), false) }))
|
|
43871
|
+
);
|
|
43539
43872
|
return;
|
|
43540
43873
|
}
|
|
43541
43874
|
const scopes = deps.config.scopes(msg.channelId, msg.userId);
|
|
@@ -43560,7 +43893,7 @@ async function runShipBranch(deps, msg, io, ctx) {
|
|
|
43560
43893
|
"dispatch.ship_hand_off",
|
|
43561
43894
|
() => handOffToCoordinator(
|
|
43562
43895
|
{
|
|
43563
|
-
readFile: (repo, path, ref) => githubCapabilityFor(deps, chatActorOf(deps.config, msg)).api.readFile(repo, path, ref),
|
|
43896
|
+
readFile: (repo, path, ref, opts) => githubCapabilityFor(deps, chatActorOf(deps.config, msg)).api.readFile(repo, path, ref, opts),
|
|
43564
43897
|
instances: deps.coordinatorInstances ?? new NullCoordinatorInstanceStore(),
|
|
43565
43898
|
create: deps.createCoordinatorInstance ?? ((id) => createInstanceViaShim(shim(), id)),
|
|
43566
43899
|
status: deps.fetchCoordinatorInstanceStatus ?? ((id) => fetchInstanceStatusViaShim(shim(), id))
|
|
@@ -43657,6 +43990,8 @@ var init_ship = __esm({
|
|
|
43657
43990
|
init_runRegistry();
|
|
43658
43991
|
init_statusCardFrame();
|
|
43659
43992
|
init_types5();
|
|
43993
|
+
init_refusal();
|
|
43994
|
+
init_reply();
|
|
43660
43995
|
}
|
|
43661
43996
|
});
|
|
43662
43997
|
|
|
@@ -44312,10 +44647,28 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
44312
44647
|
const queuedBeforeMs = msg.originAt !== void 0 ? Math.max(0, receivedAt - msg.originAt) : void 0;
|
|
44313
44648
|
const queued = queuedCaption("behind", opts.queuedBehindMs) ?? queuedCaption("before", queuedBeforeMs);
|
|
44314
44649
|
let refused7 = false;
|
|
44315
|
-
const
|
|
44650
|
+
const stampRefusal = (outcome) => {
|
|
44316
44651
|
refused7 = true;
|
|
44652
|
+
const cause = causeOf(outcome);
|
|
44317
44653
|
ended.refusal ??= outcome;
|
|
44318
|
-
|
|
44654
|
+
ended.cause ??= cause;
|
|
44655
|
+
root.setAttrs({ refusal: outcome, cause });
|
|
44656
|
+
return cause;
|
|
44657
|
+
};
|
|
44658
|
+
const refuse3 = async (refusal, side) => {
|
|
44659
|
+
const cause = stampRefusal(refusal.code);
|
|
44660
|
+
await root.span(
|
|
44661
|
+
"dispatch.refuse",
|
|
44662
|
+
async () => {
|
|
44663
|
+
await side?.();
|
|
44664
|
+
await renderRefusal(refusal, io);
|
|
44665
|
+
},
|
|
44666
|
+
{ attrs: { outcome: refusal.code, refusal: refusal.code, cause } }
|
|
44667
|
+
);
|
|
44668
|
+
};
|
|
44669
|
+
const refuseSilently = (outcome, side) => {
|
|
44670
|
+
const cause = stampRefusal(outcome);
|
|
44671
|
+
return root.span("dispatch.refuse", side, { attrs: { outcome, refusal: outcome, cause } });
|
|
44319
44672
|
};
|
|
44320
44673
|
const closeLines = (end, finished, owner = "agent") => cardLines(trace, { end, finished, owner, queued });
|
|
44321
44674
|
const doneLines = (diagnosis) => {
|
|
@@ -44436,6 +44789,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
44436
44789
|
clock: clock4,
|
|
44437
44790
|
root,
|
|
44438
44791
|
refuse: refuse3,
|
|
44792
|
+
refuseSilently,
|
|
44439
44793
|
admission,
|
|
44440
44794
|
hooks: {
|
|
44441
44795
|
reservation: reservationHooks,
|
|
@@ -44701,7 +45055,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
44701
45055
|
const request2 = await abandonLostWorkspace({
|
|
44702
45056
|
msg,
|
|
44703
45057
|
io,
|
|
44704
|
-
refuse:
|
|
45058
|
+
refuse: refuseSilently,
|
|
44705
45059
|
card,
|
|
44706
45060
|
shell,
|
|
44707
45061
|
closeLines,
|
|
@@ -44992,10 +45346,13 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
44992
45346
|
return ended;
|
|
44993
45347
|
} catch (err2) {
|
|
44994
45348
|
caught = true;
|
|
45349
|
+
ended.refusal ??= "uncaught";
|
|
45350
|
+
ended.cause ??= "system";
|
|
45351
|
+
if (!refused7) root.setAttrs({ refusal: "uncaught", cause: "system" });
|
|
44995
45352
|
const errMsg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
44996
45353
|
if (setupCard && setupShell) {
|
|
44997
45354
|
const [failedCard, failedShell] = [setupCard, setupShell];
|
|
44998
|
-
await
|
|
45355
|
+
await refuseSilently(
|
|
44999
45356
|
"setup_failed",
|
|
45000
45357
|
() => failedCard.done(
|
|
45001
45358
|
failedShell.close({
|
|
@@ -45008,16 +45365,21 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
45008
45365
|
});
|
|
45009
45366
|
}
|
|
45010
45367
|
const replyName = root.record().attrs.runId !== void 0 ? "post.reply" : "dispatch.refuse";
|
|
45368
|
+
const replyAttrs = replyName === "dispatch.refuse" ? { attrs: { outcome: "uncaught", refusal: "uncaught", cause: "system" } } : void 0;
|
|
45011
45369
|
await ending.sealAfterReply(
|
|
45012
45370
|
async () => {
|
|
45013
45371
|
},
|
|
45014
|
-
() => root.span(
|
|
45015
|
-
|
|
45016
|
-
|
|
45017
|
-
|
|
45372
|
+
() => root.span(
|
|
45373
|
+
replyName,
|
|
45374
|
+
() => {
|
|
45375
|
+
const line3 = redactSecrets(stripAnsi(errorReply(err2)));
|
|
45376
|
+
const link = admitted?.runLink;
|
|
45377
|
+
return io.reply(link ? `${line3}
|
|
45018
45378
|
|
|
45019
45379
|
[Live run](${link})` : line3);
|
|
45020
|
-
|
|
45380
|
+
},
|
|
45381
|
+
replyAttrs
|
|
45382
|
+
)
|
|
45021
45383
|
).catch(() => {
|
|
45022
45384
|
});
|
|
45023
45385
|
} finally {
|
|
@@ -45089,10 +45451,14 @@ async function dispatchClick(deps, click) {
|
|
|
45089
45451
|
registry: deps.runRegistry ?? defaultRunRegistry,
|
|
45090
45452
|
onFinished: (id) => void deps.runBearers?.revoke(id)
|
|
45091
45453
|
});
|
|
45092
|
-
const refuse3 = (
|
|
45454
|
+
const refuse3 = (refusal) => {
|
|
45093
45455
|
refused7 = true;
|
|
45094
|
-
ended.refusal =
|
|
45095
|
-
|
|
45456
|
+
ended.refusal = refusal.code;
|
|
45457
|
+
ended.cause = refusal.cause;
|
|
45458
|
+
root.setAttrs({ refusal: refusal.code, cause: refusal.cause });
|
|
45459
|
+
return root.span("dispatch.refuse", () => renderRefusal(refusal, io), {
|
|
45460
|
+
attrs: { outcome: refusal.code, refusal: refusal.code, cause: refusal.cause }
|
|
45461
|
+
});
|
|
45096
45462
|
};
|
|
45097
45463
|
try {
|
|
45098
45464
|
if (click.kind === "cancel") {
|
|
@@ -45101,7 +45467,7 @@ async function dispatchClick(deps, click) {
|
|
|
45101
45467
|
await ending.sealAfterReply(
|
|
45102
45468
|
async () => {
|
|
45103
45469
|
},
|
|
45104
|
-
() => refuse3(res2.refusal, res2.text)
|
|
45470
|
+
() => refuse3(refusalOf(res2.refusal, res2.text))
|
|
45105
45471
|
);
|
|
45106
45472
|
else
|
|
45107
45473
|
await ending.sealAfterReply(
|
|
@@ -45113,10 +45479,33 @@ async function dispatchClick(deps, click) {
|
|
|
45113
45479
|
}
|
|
45114
45480
|
const res = await consumeAndRun(deps, { id: click.id, actorIds }, io, ending, trace);
|
|
45115
45481
|
if (res.kind === "refused") {
|
|
45482
|
+
const refusal = refusalOf(res.refusal, res.text);
|
|
45483
|
+
if (res.row) {
|
|
45484
|
+
const row2 = res.row;
|
|
45485
|
+
await recordRoutedDecision(
|
|
45486
|
+
deps,
|
|
45487
|
+
row2.message,
|
|
45488
|
+
io,
|
|
45489
|
+
{ id: row2.command },
|
|
45490
|
+
{
|
|
45491
|
+
preset: COMMAND_RUN_AGENT,
|
|
45492
|
+
reason: REFUSED_REASON,
|
|
45493
|
+
model: row2.model,
|
|
45494
|
+
command: row2.command,
|
|
45495
|
+
input: redactedInput(row2.input),
|
|
45496
|
+
receipt: row2.receipt,
|
|
45497
|
+
outcome: "refused",
|
|
45498
|
+
refusalCode: refusal.code
|
|
45499
|
+
},
|
|
45500
|
+
res.text,
|
|
45501
|
+
ending,
|
|
45502
|
+
trace
|
|
45503
|
+
);
|
|
45504
|
+
}
|
|
45116
45505
|
await ending.sealAfterReply(
|
|
45117
45506
|
async () => {
|
|
45118
45507
|
},
|
|
45119
|
-
() => refuse3(
|
|
45508
|
+
() => refuse3(refusal)
|
|
45120
45509
|
);
|
|
45121
45510
|
return ended;
|
|
45122
45511
|
}
|
|
@@ -45154,10 +45543,13 @@ var init_dispatcher = __esm({
|
|
|
45154
45543
|
init_runEvents();
|
|
45155
45544
|
init_redact();
|
|
45156
45545
|
init_reply();
|
|
45546
|
+
init_refusal();
|
|
45157
45547
|
init_admission();
|
|
45158
45548
|
init_fastPath();
|
|
45159
45549
|
init_confirm();
|
|
45160
45550
|
init_commandRun();
|
|
45551
|
+
init_runOwner();
|
|
45552
|
+
init_route();
|
|
45161
45553
|
init_references();
|
|
45162
45554
|
init_actor();
|
|
45163
45555
|
init_config2();
|
|
@@ -47929,7 +48321,12 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
47929
48321
|
const reason2 = finaleAbortReason(lease.finaleMs);
|
|
47930
48322
|
writeUpFailed ??= reason2;
|
|
47931
48323
|
run2.onProgress?.(finaleTimedOutNote());
|
|
47932
|
-
|
|
48324
|
+
const doing = bridge.doingNow();
|
|
48325
|
+
const onTool = doing !== void 0 && doing !== "model";
|
|
48326
|
+
note(
|
|
48327
|
+
"harness_error",
|
|
48328
|
+
cutInFlight || onTool ? finaleWaitNote(reason2, doingWords(doing), cutInFlight) : windDownFailureNote(reason2)
|
|
48329
|
+
);
|
|
47933
48330
|
void interrupt({ ending: "the write-up's finale" });
|
|
47934
48331
|
}
|
|
47935
48332
|
return;
|
|
@@ -48212,9 +48609,9 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
48212
48609
|
throw replaced;
|
|
48213
48610
|
}
|
|
48214
48611
|
if (interrupts.cut || interrupts.ending !== void 0) {
|
|
48215
|
-
const { cut, ending } = interrupts;
|
|
48612
|
+
const { cut, ending: ending2 } = interrupts;
|
|
48216
48613
|
bridge.closeOpenSpans(
|
|
48217
|
-
(open2) => cut &&
|
|
48614
|
+
(open2) => cut && ending2 !== void 0 ? `${open2.tool} was cut at the loop's end; the loop then left on its interrupt (${ending2}) before the call's outcome reached the record` : ending2 !== void 0 ? `${open2.tool} was still running when the loop left on its interrupt (${ending2}); its outcome never reached the record` : `${open2.tool} was cut at the loop's end and its outcome never reached the record before the write-up settled`,
|
|
48218
48615
|
{ cut: true }
|
|
48219
48616
|
);
|
|
48220
48617
|
}
|
|
@@ -48258,14 +48655,9 @@ async function driveOpenCode(deps, run2, conn, kind = "loop") {
|
|
|
48258
48655
|
note("wrap_up", wrapUpNeverPostedNote(writeUp.kind, "run", neverPostedEnd));
|
|
48259
48656
|
writeUp = void 0;
|
|
48260
48657
|
}
|
|
48261
|
-
const
|
|
48262
|
-
|
|
48263
|
-
}
|
|
48264
|
-
function writeUpAnswer(writeUp, text, maxMinutes, writeUpFailed, neverPostedEnd) {
|
|
48265
|
-
if (writeUp?.kind === "time") return timeBudgetAnswer(text, maxMinutes, writeUpFailed);
|
|
48266
|
-
if (writeUp?.kind === "turns") return turnGuardAnswer(text, writeUp.pace, writeUpFailed);
|
|
48267
|
-
if (writeUp?.kind === "soft") return softStopAnswer(text, writeUpFailed);
|
|
48268
|
-
return unlabelledAnswer(text, writeUpFailed, neverPostedEnd === "finale" ? "finale" : "failed");
|
|
48658
|
+
const ending = windDownEndingOf(writeUp, text, writeUpFailed, neverPostedEnd === "finale" ? "finale" : "failed");
|
|
48659
|
+
const answer = ending ? windDownAnswer(ending, run2.agent.maxMinutes) : unlabelledAnswer(text, void 0);
|
|
48660
|
+
return { answer, ...ending ? { ending } : {}, remainingMs: remaining, hardStopped: false, ...handOver() };
|
|
48269
48661
|
}
|
|
48270
48662
|
var OpenCodeGateBypassedError, OpenCodeReplyFailedError, OpenCodeRequestRefusedError, OpenCodeWriteUnresolvedError, FIRST_EVENT_BOUND_MS, SILENCE_TAIL_BYTES, OpenCodeSilentError, OpenCodeContainerReplacedError, TOOL_NAME_WORD, RESOURCE_PERMISSIONS, isRecord9, str7, TURN_BUDGET_CODE, TURN_BUDGET_REFUSAL, OpenCodeBridge, StepBoundaries;
|
|
48271
48663
|
var init_bridge3 = __esm({
|
|
@@ -49395,12 +49787,13 @@ async function openOpenCodeRun(deps, run2, settings) {
|
|
|
49395
49787
|
(err2) => err2
|
|
49396
49788
|
);
|
|
49397
49789
|
let answer;
|
|
49790
|
+
let ending;
|
|
49398
49791
|
let remainingMs;
|
|
49399
49792
|
let hardStopped;
|
|
49400
49793
|
let drained;
|
|
49401
49794
|
try {
|
|
49402
49795
|
let storeIds;
|
|
49403
|
-
({ answer, remainingMs, storeIds, hardStopped } = await driveOpenCode(deps, run2, conn));
|
|
49796
|
+
({ answer, ending, remainingMs, storeIds, hardStopped } = await driveOpenCode(deps, run2, conn));
|
|
49404
49797
|
for (const id of storeIds) known.add(id);
|
|
49405
49798
|
} finally {
|
|
49406
49799
|
draining = false;
|
|
@@ -49409,6 +49802,7 @@ async function openOpenCodeRun(deps, run2, settings) {
|
|
|
49409
49802
|
if (drained instanceof OpenCodeWriteUnresolvedError && !hardStopped) throw drained;
|
|
49410
49803
|
return {
|
|
49411
49804
|
answer,
|
|
49805
|
+
...ending ? { ending } : {},
|
|
49412
49806
|
followUp: async (input) => {
|
|
49413
49807
|
const turnEnd = await feedEndFrom(deps.container, conn.paths.feed, conn.feedOffset);
|
|
49414
49808
|
const previous = live.toolContext;
|
|
@@ -53884,14 +54278,54 @@ function viewAsFromCookie(header4) {
|
|
|
53884
54278
|
function isViewAsPath(pathname) {
|
|
53885
54279
|
return pathname === VIEW_AS_PATH || pathname === VIEW_AS_EXIT_PATH;
|
|
53886
54280
|
}
|
|
53887
|
-
function
|
|
54281
|
+
function peopleOf(rows, known) {
|
|
53888
54282
|
const seen = /* @__PURE__ */ new Map();
|
|
53889
|
-
|
|
53890
|
-
if (!
|
|
53891
|
-
seen.
|
|
53892
|
-
|
|
54283
|
+
const add = (id, name) => {
|
|
54284
|
+
if (!isViewablePerson(id)) return;
|
|
54285
|
+
const had = seen.get(id);
|
|
54286
|
+
if (!had) seen.set(id, { id, ...name ? { name } : {} });
|
|
54287
|
+
else if (!had.name && name) seen.set(id, { id, name });
|
|
54288
|
+
};
|
|
54289
|
+
for (const r of rows) if (r.userId) add(r.userId, r.userName);
|
|
54290
|
+
for (const p of known) add(p.id, p.name);
|
|
53893
54291
|
return [...seen.values()].sort((a, b) => (a.name ?? a.id).localeCompare(b.name ?? b.id));
|
|
53894
54292
|
}
|
|
54293
|
+
async function knownPeople(deps) {
|
|
54294
|
+
const rows = await deps.requesters().catch(() => []);
|
|
54295
|
+
const fromRuns = peopleOf(rows, []);
|
|
54296
|
+
const namedIds = new Set(fromRuns.filter((p) => p.name).map((p) => p.id));
|
|
54297
|
+
const granted = await Promise.all(
|
|
54298
|
+
deps.granted().map(async (id) => {
|
|
54299
|
+
if (namedIds.has(id)) return { id };
|
|
54300
|
+
const name = await deps.name(id).catch(() => void 0);
|
|
54301
|
+
return { id, ...name ? { name } : {} };
|
|
54302
|
+
})
|
|
54303
|
+
);
|
|
54304
|
+
return peopleOf(rows, granted);
|
|
54305
|
+
}
|
|
54306
|
+
function peopleSource(deps, opts = {}) {
|
|
54307
|
+
const ttlMs = opts.ttlMs ?? KNOWN_PEOPLE_TTL_MS;
|
|
54308
|
+
const now = opts.now ?? Date.now;
|
|
54309
|
+
let people = [];
|
|
54310
|
+
let refreshedAt = Number.NEGATIVE_INFINITY;
|
|
54311
|
+
let inFlight;
|
|
54312
|
+
const refresh = () => {
|
|
54313
|
+
inFlight ??= knownPeople(deps).then((next) => {
|
|
54314
|
+
people = next;
|
|
54315
|
+
refreshedAt = now();
|
|
54316
|
+
}).catch(() => void 0).finally(() => {
|
|
54317
|
+
inFlight = void 0;
|
|
54318
|
+
});
|
|
54319
|
+
return inFlight;
|
|
54320
|
+
};
|
|
54321
|
+
return {
|
|
54322
|
+
current() {
|
|
54323
|
+
if (now() - refreshedAt >= ttlMs) void refresh();
|
|
54324
|
+
return people;
|
|
54325
|
+
},
|
|
54326
|
+
refresh
|
|
54327
|
+
};
|
|
54328
|
+
}
|
|
53895
54329
|
function refuseWhileViewing(res, person) {
|
|
53896
54330
|
res.writeHead(403, JSON_NO_STORE);
|
|
53897
54331
|
res.end(JSON.stringify({ error: "unauthorized", message: viewingRefusal(person) }));
|
|
@@ -53955,10 +54389,11 @@ function createViewAsHandler(deps) {
|
|
|
53955
54389
|
return true;
|
|
53956
54390
|
};
|
|
53957
54391
|
}
|
|
53958
|
-
var VIEW_AS_COOKIE, VIEW_AS_PATH, VIEW_AS_EXIT_PATH, MAX_BODY_BYTES3, JSON_NO_STORE;
|
|
54392
|
+
var VIEW_AS_COOKIE, VIEW_AS_PATH, VIEW_AS_EXIT_PATH, MAX_BODY_BYTES3, KNOWN_PEOPLE_TTL_MS, JSON_NO_STORE;
|
|
53959
54393
|
var init_viewAs2 = __esm({
|
|
53960
54394
|
"../../src/channels/viewAs.ts"() {
|
|
53961
54395
|
"use strict";
|
|
54396
|
+
init_budgets();
|
|
53962
54397
|
init_viewAs();
|
|
53963
54398
|
init_commandHttp();
|
|
53964
54399
|
init_http();
|
|
@@ -53966,20 +54401,11 @@ var init_viewAs2 = __esm({
|
|
|
53966
54401
|
VIEW_AS_PATH = "/runs/view-as";
|
|
53967
54402
|
VIEW_AS_EXIT_PATH = "/runs/view-as/exit";
|
|
53968
54403
|
MAX_BODY_BYTES3 = 1024;
|
|
54404
|
+
KNOWN_PEOPLE_TTL_MS = MINUTE_MS;
|
|
53969
54405
|
JSON_NO_STORE = { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" };
|
|
53970
54406
|
}
|
|
53971
54407
|
});
|
|
53972
54408
|
|
|
53973
|
-
// ../../src/channels/liveView/html.ts
|
|
53974
|
-
function escapeHtml(s) {
|
|
53975
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
53976
|
-
}
|
|
53977
|
-
var init_html = __esm({
|
|
53978
|
-
"../../src/channels/liveView/html.ts"() {
|
|
53979
|
-
"use strict";
|
|
53980
|
-
}
|
|
53981
|
-
});
|
|
53982
|
-
|
|
53983
54409
|
// ../../src/channels/favicon.ts
|
|
53984
54410
|
function faviconSvg(fill) {
|
|
53985
54411
|
return `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><circle cx='8' cy='8' r='6' fill='${fill}'/></svg>`;
|
|
@@ -54005,121 +54431,6 @@ var init_favicon = __esm({
|
|
|
54005
54431
|
}
|
|
54006
54432
|
});
|
|
54007
54433
|
|
|
54008
|
-
// ../../src/channels/residentsModel.ts
|
|
54009
|
-
function residentStateTone(state) {
|
|
54010
|
-
switch (state) {
|
|
54011
|
-
case "warm":
|
|
54012
|
-
return "green";
|
|
54013
|
-
case "onboarding":
|
|
54014
|
-
case "refreshing":
|
|
54015
|
-
case "restoring":
|
|
54016
|
-
return "amber";
|
|
54017
|
-
case "degraded":
|
|
54018
|
-
case "down":
|
|
54019
|
-
return "red";
|
|
54020
|
-
default:
|
|
54021
|
-
return "grey";
|
|
54022
|
-
}
|
|
54023
|
-
}
|
|
54024
|
-
function residentsFleetTone(records) {
|
|
54025
|
-
if (records.length === 0) return "grey";
|
|
54026
|
-
const tones = new Set(records.map((record2) => residentStateTone(residentLive(rec(record2)).state)));
|
|
54027
|
-
if (tones.has("red")) return "red";
|
|
54028
|
-
if (tones.has("amber")) return "amber";
|
|
54029
|
-
return tones.size === 1 && tones.has("green") ? "green" : "grey";
|
|
54030
|
-
}
|
|
54031
|
-
function residentSlug(record2) {
|
|
54032
|
-
return str8(record2.resource).replace(/^repo:/, "");
|
|
54033
|
-
}
|
|
54034
|
-
function residentLive(record2) {
|
|
54035
|
-
const live = rec(record2.live);
|
|
54036
|
-
if (typeof live.error === "string") return { ...live, state: "unreachable", reason: live.error };
|
|
54037
|
-
return { ...live, state: str8(live.state) || "unknown", reason: str8(live.reason) };
|
|
54038
|
-
}
|
|
54039
|
-
var RESIDENT_SLUG_RE, str8, rec;
|
|
54040
|
-
var init_residentsModel = __esm({
|
|
54041
|
-
"../../src/channels/residentsModel.ts"() {
|
|
54042
|
-
"use strict";
|
|
54043
|
-
init_residentDiskBudget();
|
|
54044
|
-
RESIDENT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?\/[a-z0-9](?:[a-z0-9._-]{0,98}[a-z0-9])?$/;
|
|
54045
|
-
str8 = (v) => typeof v === "string" ? v : typeof v === "number" ? String(v) : "";
|
|
54046
|
-
rec = (v) => v && typeof v === "object" ? v : {};
|
|
54047
|
-
}
|
|
54048
|
-
});
|
|
54049
|
-
|
|
54050
|
-
// ../../src/channels/webSeed.ts
|
|
54051
|
-
function serializeSeed(seed) {
|
|
54052
|
-
return JSON.stringify(seed).replace(
|
|
54053
|
-
/[<>&\u2028\u2029]/g,
|
|
54054
|
-
(ch) => `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`
|
|
54055
|
-
);
|
|
54056
|
-
}
|
|
54057
|
-
var SEED_ELEMENT_ID;
|
|
54058
|
-
var init_webSeed = __esm({
|
|
54059
|
-
"../../src/channels/webSeed.ts"() {
|
|
54060
|
-
"use strict";
|
|
54061
|
-
SEED_ELEMENT_ID = "sb-seed";
|
|
54062
|
-
}
|
|
54063
|
-
});
|
|
54064
|
-
|
|
54065
|
-
// ../../src/channels/webShell.ts
|
|
54066
|
-
function renderShell(title, seed, assets) {
|
|
54067
|
-
const css = assets.css.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`).join("\n");
|
|
54068
|
-
const favicon = pageFavicon(seed);
|
|
54069
|
-
return `<!doctype html>
|
|
54070
|
-
<html lang="en" class="dark">
|
|
54071
|
-
<head>
|
|
54072
|
-
<meta charset="utf-8" />
|
|
54073
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
54074
|
-
<meta name="robots" content="noindex" />
|
|
54075
|
-
<title>${escapeHtml(title)}</title>
|
|
54076
|
-
<link rel="icon" id="favicon" href="${favicon}" />
|
|
54077
|
-
${css}
|
|
54078
|
-
</head>
|
|
54079
|
-
<body>
|
|
54080
|
-
<div id="app"></div>
|
|
54081
|
-
<script type="application/json" id="${SEED_ELEMENT_ID}">${serializeSeed(seed)}</script>
|
|
54082
|
-
<script type="module" src="${escapeHtml(assets.js)}"></script>
|
|
54083
|
-
</body>
|
|
54084
|
-
</html>`;
|
|
54085
|
-
}
|
|
54086
|
-
function pageFavicon(seed) {
|
|
54087
|
-
switch (seed.page) {
|
|
54088
|
-
case "runs":
|
|
54089
|
-
case "run":
|
|
54090
|
-
return FAVICON_IDLE;
|
|
54091
|
-
case "residents":
|
|
54092
|
-
return FAVICON_BY_TONE[residentsFleetTone(seed.residents)];
|
|
54093
|
-
default:
|
|
54094
|
-
return FAVICON_DEFAULT;
|
|
54095
|
-
}
|
|
54096
|
-
}
|
|
54097
|
-
function makeShellRenderer(assets, capabilities) {
|
|
54098
|
-
return (viewer, title, seed) => renderShell(
|
|
54099
|
-
title,
|
|
54100
|
-
{ ...seed, capabilities, ...viewer?.viewingAs ? { viewingAs: viewer.viewingAs } : {} },
|
|
54101
|
-
assets
|
|
54102
|
-
);
|
|
54103
|
-
}
|
|
54104
|
-
var PAGE_CSP, FORM_PAGE_CSP, WEB_HTML_HEADERS;
|
|
54105
|
-
var init_webShell = __esm({
|
|
54106
|
-
"../../src/channels/webShell.ts"() {
|
|
54107
|
-
"use strict";
|
|
54108
|
-
init_html();
|
|
54109
|
-
init_favicon();
|
|
54110
|
-
init_residentsModel();
|
|
54111
|
-
init_webSeed();
|
|
54112
|
-
PAGE_CSP = "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self'; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
|
|
54113
|
-
FORM_PAGE_CSP = PAGE_CSP.replace("form-action 'none'", "form-action 'self'");
|
|
54114
|
-
WEB_HTML_HEADERS = {
|
|
54115
|
-
"content-type": "text/html; charset=utf-8",
|
|
54116
|
-
"content-security-policy": PAGE_CSP,
|
|
54117
|
-
"x-frame-options": "DENY",
|
|
54118
|
-
"cache-control": "no-store"
|
|
54119
|
-
};
|
|
54120
|
-
}
|
|
54121
|
-
});
|
|
54122
|
-
|
|
54123
54434
|
// ../../src/channels/liveView/sse.ts
|
|
54124
54435
|
function sseData(event, seq) {
|
|
54125
54436
|
return `id: ${seq}
|
|
@@ -54233,8 +54544,8 @@ function serveIndexEvents(subscribeIndex, sink, onLive) {
|
|
|
54233
54544
|
onLive?.();
|
|
54234
54545
|
}
|
|
54235
54546
|
function withOmittedMarkers(events, eventCount) {
|
|
54236
|
-
const
|
|
54237
|
-
const omitted = eventCount -
|
|
54547
|
+
const counted2 = events.filter((e) => !(isSpanRecord(e) && e.seq === void 0)).length;
|
|
54548
|
+
const omitted = eventCount - counted2;
|
|
54238
54549
|
if (omitted <= 0) return [...events];
|
|
54239
54550
|
const marker = (n2) => ({
|
|
54240
54551
|
type: "replay_note",
|
|
@@ -54290,6 +54601,31 @@ var init_sse = __esm({
|
|
|
54290
54601
|
}
|
|
54291
54602
|
});
|
|
54292
54603
|
|
|
54604
|
+
// ../../src/channels/liveView/html.ts
|
|
54605
|
+
function escapeHtml(s) {
|
|
54606
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54607
|
+
}
|
|
54608
|
+
var init_html = __esm({
|
|
54609
|
+
"../../src/channels/liveView/html.ts"() {
|
|
54610
|
+
"use strict";
|
|
54611
|
+
}
|
|
54612
|
+
});
|
|
54613
|
+
|
|
54614
|
+
// ../../src/channels/webSeed.ts
|
|
54615
|
+
function serializeSeed(seed) {
|
|
54616
|
+
return JSON.stringify(seed).replace(
|
|
54617
|
+
/[<>&\u2028\u2029]/g,
|
|
54618
|
+
(ch) => `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`
|
|
54619
|
+
);
|
|
54620
|
+
}
|
|
54621
|
+
var SEED_ELEMENT_ID;
|
|
54622
|
+
var init_webSeed = __esm({
|
|
54623
|
+
"../../src/channels/webSeed.ts"() {
|
|
54624
|
+
"use strict";
|
|
54625
|
+
SEED_ELEMENT_ID = "sb-seed";
|
|
54626
|
+
}
|
|
54627
|
+
});
|
|
54628
|
+
|
|
54293
54629
|
// ../../src/channels/liveView.ts
|
|
54294
54630
|
function parseRunRoute(pathname) {
|
|
54295
54631
|
if (pathname === "/runs" || pathname === "/runs/") return { kind: "index" };
|
|
@@ -54457,14 +54793,11 @@ function createLiveViewHandler(deps) {
|
|
|
54457
54793
|
return token2 === void 0 ? v : { ...v, token: token2 };
|
|
54458
54794
|
};
|
|
54459
54795
|
const liveTokens = () => new Map(index.listActive().map((s) => [s.id, s.token]));
|
|
54460
|
-
const notFoundPage = (res, viewer) => {
|
|
54461
|
-
|
|
54462
|
-
|
|
54463
|
-
deps.
|
|
54464
|
-
|
|
54465
|
-
retentionDays: deps.retention ? deps.retention.retentionDays : null
|
|
54466
|
-
})
|
|
54467
|
-
);
|
|
54796
|
+
const notFoundPage = (req, res, viewer) => {
|
|
54797
|
+
deps.page(req, res, 404, viewer, "Run not found", {
|
|
54798
|
+
page: "runNotFound",
|
|
54799
|
+
retentionDays: deps.retention ? deps.retention.retentionDays : null
|
|
54800
|
+
});
|
|
54468
54801
|
};
|
|
54469
54802
|
const mergedRows = async (live, visibleTo, cursor, view = {}) => {
|
|
54470
54803
|
const tokens = new Map(live.map((s) => [s.id, s.token]));
|
|
@@ -54508,6 +54841,7 @@ function createLiveViewHandler(deps) {
|
|
|
54508
54841
|
}
|
|
54509
54842
|
const live = index.listActive().filter((s) => matchesPredicate(visibleTo, s));
|
|
54510
54843
|
const render3 = (page2) => {
|
|
54844
|
+
const people = holdsAll(ctx.actor) ? peopleOf(page2.rows, deps.people?.() ?? []) : void 0;
|
|
54511
54845
|
const liveCount = page2.rows.filter((r) => !r.finished).length;
|
|
54512
54846
|
const title = `${liveCount > 0 ? `(${liveCount}) ` : ""}${all ? "All runs" : "Live runs"}`;
|
|
54513
54847
|
const seed = {
|
|
@@ -54515,9 +54849,7 @@ function createLiveViewHandler(deps) {
|
|
|
54515
54849
|
all,
|
|
54516
54850
|
mine,
|
|
54517
54851
|
...ctx.actor.asUser ? { asUser: ctx.actor.asUser } : {},
|
|
54518
|
-
|
|
54519
|
-
// picker offers the requesters this page names; the seed says nothing to anyone else.
|
|
54520
|
-
...holdsAll(ctx.actor) ? { viewAs: { people: requestersOf(page2.rows) } } : {},
|
|
54852
|
+
...people ? { viewAs: { people } } : {},
|
|
54521
54853
|
retentionDays: deps.retention ? deps.retention.retentionDays : null,
|
|
54522
54854
|
now: now(),
|
|
54523
54855
|
rows: [...page2.rows],
|
|
@@ -54525,8 +54857,7 @@ function createLiveViewHandler(deps) {
|
|
|
54525
54857
|
...page2.olderHref ? { olderHref: page2.olderHref } : {},
|
|
54526
54858
|
...page2.olderThan !== void 0 ? { olderThan: page2.olderThan } : {}
|
|
54527
54859
|
};
|
|
54528
|
-
|
|
54529
|
-
res.end(deps.shell(ctx.actor, title, seed));
|
|
54860
|
+
deps.page(req, res, 200, ctx.actor, title, seed);
|
|
54530
54861
|
};
|
|
54531
54862
|
if (!all) {
|
|
54532
54863
|
run2(
|
|
@@ -54541,8 +54872,7 @@ function createLiveViewHandler(deps) {
|
|
|
54541
54872
|
if (route3.kind === "scheduled") {
|
|
54542
54873
|
const scheduled = deps.scheduled;
|
|
54543
54874
|
if (!scheduled) {
|
|
54544
|
-
|
|
54545
|
-
res.end(deps.shell(ctx.actor, "Scheduled runs", { page: "scheduled", now: now(), rows: null }));
|
|
54875
|
+
deps.page(req, res, 200, ctx.actor, "Scheduled runs", { page: "scheduled", now: now(), rows: null });
|
|
54546
54876
|
return true;
|
|
54547
54877
|
}
|
|
54548
54878
|
const visibleTo = readableRuns(ctx.actor);
|
|
@@ -54555,8 +54885,7 @@ function createLiveViewHandler(deps) {
|
|
|
54555
54885
|
rows: buildScheduledRows(scheduled.schedules, firings, live, t),
|
|
54556
54886
|
...firings.ok ? {} : { firingsUnavailable: firings.reason }
|
|
54557
54887
|
};
|
|
54558
|
-
|
|
54559
|
-
res.end(deps.shell(ctx.actor, "Scheduled runs", seed));
|
|
54888
|
+
deps.page(req, res, 200, ctx.actor, "Scheduled runs", seed);
|
|
54560
54889
|
};
|
|
54561
54890
|
if (!scheduled.store) {
|
|
54562
54891
|
render3({ ok: false, reason: NO_STORE_REASON });
|
|
@@ -54570,7 +54899,7 @@ function createLiveViewHandler(deps) {
|
|
|
54570
54899
|
run2(res, async () => {
|
|
54571
54900
|
const found = await service.listUnitRuns(route3.key, visibleTo);
|
|
54572
54901
|
if (!found.ok) {
|
|
54573
|
-
notFoundPage(res, ctx.actor);
|
|
54902
|
+
notFoundPage(req, res, ctx.actor);
|
|
54574
54903
|
return;
|
|
54575
54904
|
}
|
|
54576
54905
|
const findings = found.value.pr ? await service.listFindings({ repo: found.value.instance.repo, number: found.value.pr.number }, visibleTo) : void 0;
|
|
@@ -54585,8 +54914,7 @@ function createLiveViewHandler(deps) {
|
|
|
54585
54914
|
now: now(),
|
|
54586
54915
|
retentionDays: deps.retention ? deps.retention.retentionDays : null
|
|
54587
54916
|
};
|
|
54588
|
-
|
|
54589
|
-
res.end(deps.shell(ctx.actor, `Unit ${found.value.id}`, seed));
|
|
54917
|
+
deps.page(req, res, 200, ctx.actor, `Unit ${found.value.id}`, seed);
|
|
54590
54918
|
});
|
|
54591
54919
|
return true;
|
|
54592
54920
|
}
|
|
@@ -54599,29 +54927,26 @@ function createLiveViewHandler(deps) {
|
|
|
54599
54927
|
const { token: own, ...row2 } = s;
|
|
54600
54928
|
return s.finished ? row2 : { ...row2, token: own };
|
|
54601
54929
|
});
|
|
54602
|
-
|
|
54603
|
-
|
|
54604
|
-
|
|
54605
|
-
|
|
54606
|
-
|
|
54607
|
-
id
|
|
54608
|
-
|
|
54609
|
-
|
|
54610
|
-
|
|
54611
|
-
|
|
54612
|
-
|
|
54613
|
-
|
|
54614
|
-
|
|
54615
|
-
|
|
54616
|
-
|
|
54617
|
-
|
|
54618
|
-
|
|
54619
|
-
|
|
54620
|
-
|
|
54621
|
-
|
|
54622
|
-
...snap?.finishedAt !== void 0 ? { finishedAt: snap.finishedAt } : {}
|
|
54623
|
-
})
|
|
54624
|
-
);
|
|
54930
|
+
deps.page(req, res, 200, ctx.actor, "Live run", {
|
|
54931
|
+
page: "run",
|
|
54932
|
+
mode: "live",
|
|
54933
|
+
id: route3.id,
|
|
54934
|
+
...(() => {
|
|
54935
|
+
const key = index.listActive().find((s) => s.id === route3.id)?.threadKey;
|
|
54936
|
+
return key !== void 0 ? { threadKey: key } : {};
|
|
54937
|
+
})(),
|
|
54938
|
+
...children.length > 0 ? { children } : {},
|
|
54939
|
+
// Stop control: same token, POST-only; `&mode=` is appended client-side.
|
|
54940
|
+
eventsUrl: `/runs/${encodeURIComponent(route3.id)}/events?t=${encodeURIComponent(token2)}`,
|
|
54941
|
+
stopUrl: `/runs/${encodeURIComponent(route3.id)}/stop?t=${encodeURIComponent(token2)}`,
|
|
54942
|
+
// The files' URL base with the same token (item 26): the page appends each key.
|
|
54943
|
+
...artifactsSeed(route3.id, token2),
|
|
54944
|
+
// The stamps the header's one duration reads (docs/reference/specs/tracing.md).
|
|
54945
|
+
serverNow: now(),
|
|
54946
|
+
startedAt: snap?.startedAt ?? now(),
|
|
54947
|
+
...snap?.receivedAt !== void 0 ? { receivedAt: snap.receivedAt } : {},
|
|
54948
|
+
...snap?.finishedAt !== void 0 ? { finishedAt: snap.finishedAt } : {}
|
|
54949
|
+
});
|
|
54625
54950
|
return true;
|
|
54626
54951
|
}
|
|
54627
54952
|
if (route3.kind === "artifact") {
|
|
@@ -54724,7 +55049,7 @@ function createLiveViewHandler(deps) {
|
|
|
54724
55049
|
sink.write(NOT_FOUND2);
|
|
54725
55050
|
sink.end();
|
|
54726
55051
|
} else if (route3.kind === "page") {
|
|
54727
|
-
notFoundPage(res, ctx.actor);
|
|
55052
|
+
notFoundPage(req, res, ctx.actor);
|
|
54728
55053
|
} else text(res, 404, NOT_FOUND2);
|
|
54729
55054
|
return;
|
|
54730
55055
|
}
|
|
@@ -54750,37 +55075,34 @@ function createLiveViewHandler(deps) {
|
|
|
54750
55075
|
]);
|
|
54751
55076
|
const findingsLedger = ledger?.ok && ledger.value.unit !== void 0 ? { unit: ledger.value.unit, rows: ledger.value.findings.length } : void 0;
|
|
54752
55077
|
const tokens = liveTokens();
|
|
54753
|
-
|
|
54754
|
-
|
|
54755
|
-
|
|
54756
|
-
|
|
54757
|
-
|
|
54758
|
-
|
|
54759
|
-
|
|
54760
|
-
|
|
54761
|
-
|
|
54762
|
-
|
|
54763
|
-
|
|
54764
|
-
|
|
54765
|
-
|
|
54766
|
-
|
|
54767
|
-
|
|
54768
|
-
|
|
54769
|
-
|
|
54770
|
-
|
|
54771
|
-
|
|
54772
|
-
|
|
54773
|
-
|
|
54774
|
-
|
|
54775
|
-
|
|
54776
|
-
|
|
54777
|
-
|
|
54778
|
-
|
|
54779
|
-
|
|
54780
|
-
|
|
54781
|
-
...artifactsSeed(route3.id)
|
|
54782
|
-
})
|
|
54783
|
-
);
|
|
55078
|
+
deps.page(req, res, 200, ctx.actor, "Run", {
|
|
55079
|
+
page: "run",
|
|
55080
|
+
mode: "history",
|
|
55081
|
+
id: route3.id,
|
|
55082
|
+
...view.threadKey !== void 0 ? { threadKey: view.threadKey } : {},
|
|
55083
|
+
...children.length > 0 ? { children: children.map((c) => withLiveToken(c, tokens)) } : {},
|
|
55084
|
+
...units.length > 0 ? { units } : {},
|
|
55085
|
+
...findingsLedger !== void 0 ? { findingsLedger } : {},
|
|
55086
|
+
// The stored stream with the truncation made visible (AE11): the
|
|
55087
|
+
// seed IS the stream on a history page — normalized first on a
|
|
55088
|
+
// span-schema record, so a pair whose twin the budget dropped gets
|
|
55089
|
+
// it back.
|
|
55090
|
+
events: withOmittedMarkers(timed ? normalizeSpans(events) : events, view.eventCount),
|
|
55091
|
+
...timed ? {} : { untimed: true },
|
|
55092
|
+
...view.status ? { status: view.status } : {},
|
|
55093
|
+
eventCount: view.eventCount,
|
|
55094
|
+
startedAt: view.startedAt,
|
|
55095
|
+
...view.receivedAt !== void 0 ? { receivedAt: view.receivedAt } : {},
|
|
55096
|
+
...view.finishedAt !== void 0 ? { finishedAt: view.finishedAt } : {},
|
|
55097
|
+
...view.sealedAt !== void 0 ? { sealedAt: view.sealedAt } : {},
|
|
55098
|
+
...view.replyOk !== void 0 ? { replyOk: view.replyOk } : {},
|
|
55099
|
+
...runDurationMs(view) !== void 0 ? { durationMs: runDurationMs(view) } : {},
|
|
55100
|
+
// The run's dollars beside its duration (costs.md item 4c); a record without usage has none.
|
|
55101
|
+
...view.cost !== void 0 ? { cost: view.cost } : {},
|
|
55102
|
+
...view.truncated !== void 0 ? { truncated: view.truncated } : {},
|
|
55103
|
+
// Tokenless: the files are read under the same decision as this page (item 26).
|
|
55104
|
+
...artifactsSeed(route3.id)
|
|
55105
|
+
});
|
|
54784
55106
|
return;
|
|
54785
55107
|
}
|
|
54786
55108
|
serveHistoryEvents(view.events ?? [], view.eventCount, nodeSseSink(req, res));
|
|
@@ -54806,7 +55128,6 @@ var init_liveView = __esm({
|
|
|
54806
55128
|
init_scheduledPanel();
|
|
54807
55129
|
init_viewAs();
|
|
54808
55130
|
init_viewAs2();
|
|
54809
|
-
init_webShell();
|
|
54810
55131
|
init_favicon();
|
|
54811
55132
|
init_sse();
|
|
54812
55133
|
init_html();
|
|
@@ -54898,6 +55219,138 @@ var init_webAssets = __esm({
|
|
|
54898
55219
|
}
|
|
54899
55220
|
});
|
|
54900
55221
|
|
|
55222
|
+
// ../../src/channels/residentsModel.ts
|
|
55223
|
+
function residentStateTone(state) {
|
|
55224
|
+
switch (state) {
|
|
55225
|
+
case "warm":
|
|
55226
|
+
return "green";
|
|
55227
|
+
case "onboarding":
|
|
55228
|
+
case "refreshing":
|
|
55229
|
+
case "restoring":
|
|
55230
|
+
return "amber";
|
|
55231
|
+
case "degraded":
|
|
55232
|
+
case "down":
|
|
55233
|
+
return "red";
|
|
55234
|
+
default:
|
|
55235
|
+
return "grey";
|
|
55236
|
+
}
|
|
55237
|
+
}
|
|
55238
|
+
function residentsFleetTone(records) {
|
|
55239
|
+
if (records.length === 0) return "grey";
|
|
55240
|
+
const tones = new Set(records.map((record2) => residentStateTone(residentLive(rec(record2)).state)));
|
|
55241
|
+
if (tones.has("red")) return "red";
|
|
55242
|
+
if (tones.has("amber")) return "amber";
|
|
55243
|
+
return tones.size === 1 && tones.has("green") ? "green" : "grey";
|
|
55244
|
+
}
|
|
55245
|
+
function residentSlug(record2) {
|
|
55246
|
+
return str8(record2.resource).replace(/^repo:/, "");
|
|
55247
|
+
}
|
|
55248
|
+
function residentLive(record2) {
|
|
55249
|
+
const live = rec(record2.live);
|
|
55250
|
+
if (typeof live.error === "string") return { ...live, state: "unreachable", reason: live.error };
|
|
55251
|
+
return { ...live, state: str8(live.state) || "unknown", reason: str8(live.reason) };
|
|
55252
|
+
}
|
|
55253
|
+
var RESIDENT_SLUG_RE, str8, rec;
|
|
55254
|
+
var init_residentsModel = __esm({
|
|
55255
|
+
"../../src/channels/residentsModel.ts"() {
|
|
55256
|
+
"use strict";
|
|
55257
|
+
init_residentDiskBudget();
|
|
55258
|
+
RESIDENT_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?\/[a-z0-9](?:[a-z0-9._-]{0,98}[a-z0-9])?$/;
|
|
55259
|
+
str8 = (v) => typeof v === "string" ? v : typeof v === "number" ? String(v) : "";
|
|
55260
|
+
rec = (v) => v && typeof v === "object" ? v : {};
|
|
55261
|
+
}
|
|
55262
|
+
});
|
|
55263
|
+
|
|
55264
|
+
// ../../src/channels/pageFavicon.ts
|
|
55265
|
+
function pageFavicon(seed) {
|
|
55266
|
+
switch (seed.page) {
|
|
55267
|
+
case "runs":
|
|
55268
|
+
case "run":
|
|
55269
|
+
return FAVICON_IDLE;
|
|
55270
|
+
case "residents":
|
|
55271
|
+
return FAVICON_BY_TONE[residentsFleetTone(seed.residents)];
|
|
55272
|
+
default:
|
|
55273
|
+
return FAVICON_DEFAULT;
|
|
55274
|
+
}
|
|
55275
|
+
}
|
|
55276
|
+
var init_pageFavicon = __esm({
|
|
55277
|
+
"../../src/channels/pageFavicon.ts"() {
|
|
55278
|
+
"use strict";
|
|
55279
|
+
init_favicon();
|
|
55280
|
+
init_residentsModel();
|
|
55281
|
+
}
|
|
55282
|
+
});
|
|
55283
|
+
|
|
55284
|
+
// ../../src/channels/webShell.ts
|
|
55285
|
+
function renderShell(seed, assets) {
|
|
55286
|
+
const css = assets.css.map((href) => `<link rel="stylesheet" href="${escapeHtml(href)}">`).join("\n");
|
|
55287
|
+
const favicon = pageFavicon(seed);
|
|
55288
|
+
return `<!doctype html>
|
|
55289
|
+
<html lang="en" class="dark">
|
|
55290
|
+
<head>
|
|
55291
|
+
<meta charset="utf-8" />
|
|
55292
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
55293
|
+
<meta name="robots" content="noindex" />
|
|
55294
|
+
<title>${escapeHtml(seed.title)}</title>
|
|
55295
|
+
<link rel="icon" id="favicon" href="${favicon}" />
|
|
55296
|
+
${css}
|
|
55297
|
+
</head>
|
|
55298
|
+
<body>
|
|
55299
|
+
<div id="app"></div>
|
|
55300
|
+
<script type="application/json" id="${SEED_ELEMENT_ID}">${serializeSeed(seed)}</script>
|
|
55301
|
+
<script type="module" src="${escapeHtml(assets.js)}"></script>
|
|
55302
|
+
</body>
|
|
55303
|
+
</html>`;
|
|
55304
|
+
}
|
|
55305
|
+
function wantsSeed(req) {
|
|
55306
|
+
const accept = req.headers.accept;
|
|
55307
|
+
if (typeof accept !== "string") return false;
|
|
55308
|
+
const types = accept.split(",").map((t) => t.split(";")[0].trim().toLowerCase());
|
|
55309
|
+
return types.includes("application/json") && !types.includes("text/html");
|
|
55310
|
+
}
|
|
55311
|
+
function makePageSender(assets, capabilities) {
|
|
55312
|
+
return (req, res, status3, viewer, title, seed) => {
|
|
55313
|
+
const island = {
|
|
55314
|
+
...seed,
|
|
55315
|
+
title,
|
|
55316
|
+
capabilities,
|
|
55317
|
+
...viewer?.viewingAs ? { viewingAs: viewer.viewingAs } : {}
|
|
55318
|
+
};
|
|
55319
|
+
if (wantsSeed(req)) {
|
|
55320
|
+
const body = JSON.stringify(island);
|
|
55321
|
+
res.writeHead(status3, WEB_SEED_HEADERS);
|
|
55322
|
+
res.end(body);
|
|
55323
|
+
return;
|
|
55324
|
+
}
|
|
55325
|
+
const html = renderShell(island, assets);
|
|
55326
|
+
res.writeHead(status3, WEB_HTML_HEADERS);
|
|
55327
|
+
res.end(html);
|
|
55328
|
+
};
|
|
55329
|
+
}
|
|
55330
|
+
var PAGE_CSP, FORM_PAGE_CSP, WEB_HTML_HEADERS, WEB_SEED_HEADERS;
|
|
55331
|
+
var init_webShell = __esm({
|
|
55332
|
+
"../../src/channels/webShell.ts"() {
|
|
55333
|
+
"use strict";
|
|
55334
|
+
init_html();
|
|
55335
|
+
init_pageFavicon();
|
|
55336
|
+
init_webSeed();
|
|
55337
|
+
PAGE_CSP = "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; media-src 'self'; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
|
|
55338
|
+
FORM_PAGE_CSP = PAGE_CSP.replace("form-action 'none'", "form-action 'self'");
|
|
55339
|
+
WEB_HTML_HEADERS = {
|
|
55340
|
+
"content-type": "text/html; charset=utf-8",
|
|
55341
|
+
"content-security-policy": PAGE_CSP,
|
|
55342
|
+
"x-frame-options": "DENY",
|
|
55343
|
+
"cache-control": "no-store",
|
|
55344
|
+
vary: "accept"
|
|
55345
|
+
};
|
|
55346
|
+
WEB_SEED_HEADERS = {
|
|
55347
|
+
"content-type": "application/json; charset=utf-8",
|
|
55348
|
+
"cache-control": "no-store",
|
|
55349
|
+
vary: "accept"
|
|
55350
|
+
};
|
|
55351
|
+
}
|
|
55352
|
+
});
|
|
55353
|
+
|
|
54901
55354
|
// ../../src/channels/residentsFeed.ts
|
|
54902
55355
|
function frame(f) {
|
|
54903
55356
|
return `data: ${JSON.stringify(f)}
|
|
@@ -55028,7 +55481,7 @@ async function readListing(client) {
|
|
|
55028
55481
|
return { listing: { cap: r.data.cap, count: r.data.count, residents } };
|
|
55029
55482
|
}
|
|
55030
55483
|
function createResidentsViewHandler(deps) {
|
|
55031
|
-
const { client,
|
|
55484
|
+
const { client, page: page2, runs } = deps;
|
|
55032
55485
|
const now = deps.now ?? Date.now;
|
|
55033
55486
|
return (req, res, ctx) => {
|
|
55034
55487
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
@@ -55089,8 +55542,7 @@ function createResidentsViewHandler(deps) {
|
|
|
55089
55542
|
now: now(),
|
|
55090
55543
|
runs: live
|
|
55091
55544
|
};
|
|
55092
|
-
res
|
|
55093
|
-
res.end(shell(ctx.actor, "Resident repos", seed));
|
|
55545
|
+
page2(req, res, 200, ctx.actor, "Resident repos", seed);
|
|
55094
55546
|
finish2(200);
|
|
55095
55547
|
return;
|
|
55096
55548
|
}
|
|
@@ -55099,8 +55551,7 @@ function createResidentsViewHandler(deps) {
|
|
|
55099
55551
|
plainEnd(404, `${route3.slug} is not onboarded as a resident`);
|
|
55100
55552
|
return;
|
|
55101
55553
|
}
|
|
55102
|
-
res
|
|
55103
|
-
res.end(shell(ctx.actor, route3.slug, { page: "resident", slug: route3.slug, record: record2 }));
|
|
55554
|
+
page2(req, res, 200, ctx.actor, route3.slug, { page: "resident", slug: route3.slug, record: record2 });
|
|
55104
55555
|
finish2(200);
|
|
55105
55556
|
});
|
|
55106
55557
|
return true;
|
|
@@ -55119,7 +55570,6 @@ var init_residentsView = __esm({
|
|
|
55119
55570
|
init_viewer();
|
|
55120
55571
|
init_residentsFeed();
|
|
55121
55572
|
init_residentsModel();
|
|
55122
|
-
init_webShell();
|
|
55123
55573
|
init_residentsModel();
|
|
55124
55574
|
UPSTREAM_REASON_MAX = 500;
|
|
55125
55575
|
}
|
|
@@ -55451,10 +55901,7 @@ function createWebChatHandler(deps) {
|
|
|
55451
55901
|
plain5(405, "method not allowed", { allow: "GET" });
|
|
55452
55902
|
return true;
|
|
55453
55903
|
}
|
|
55454
|
-
const render3 = (title, seed) =>
|
|
55455
|
-
res.writeHead(200, WEB_HTML_HEADERS);
|
|
55456
|
-
res.end(deps.shell(ctx.actor, title, seed));
|
|
55457
|
-
};
|
|
55904
|
+
const render3 = (title, seed) => deps.page(req, res, 200, ctx.actor, title, seed);
|
|
55458
55905
|
const sub = subOf(ctx.actor);
|
|
55459
55906
|
if (route3.kind === "new") {
|
|
55460
55907
|
const conversation = mintId();
|
|
@@ -55464,8 +55911,7 @@ function createWebChatHandler(deps) {
|
|
|
55464
55911
|
const threadKey = threadKeyFor(sub, route3.id);
|
|
55465
55912
|
turnsOf(threadKey, ctx.actor).then(async (open2) => {
|
|
55466
55913
|
if (open2.runs.length === 0 && !ownLane(sub, threadKey)) {
|
|
55467
|
-
|
|
55468
|
-
res.end(deps.shell(ctx.actor, "Run not found", { page: "runNotFound", retentionDays }));
|
|
55914
|
+
deps.page(req, res, 404, ctx.actor, "Run not found", { page: "runNotFound", retentionDays });
|
|
55469
55915
|
return;
|
|
55470
55916
|
}
|
|
55471
55917
|
const seed = await seedFor(ctx, route3.id, threadKey, open2);
|
|
@@ -55489,7 +55935,6 @@ var init_web2 = __esm({
|
|
|
55489
55935
|
init_commandHttp();
|
|
55490
55936
|
init_http();
|
|
55491
55937
|
init_viewer();
|
|
55492
|
-
init_webShell();
|
|
55493
55938
|
init_viewAs();
|
|
55494
55939
|
PLATFORM5 = "web";
|
|
55495
55940
|
CONVERSATION_ID_BYTES = 10;
|
|
@@ -55572,7 +56017,7 @@ function json(res, body) {
|
|
|
55572
56017
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
55573
56018
|
res.end(JSON.stringify(body));
|
|
55574
56019
|
}
|
|
55575
|
-
function createCostsViewHandler(service,
|
|
56020
|
+
function createCostsViewHandler(service, page2, opts = {}) {
|
|
55576
56021
|
const names = opts.names ?? NO_NAMES;
|
|
55577
56022
|
return (req, res, ctx = {}) => {
|
|
55578
56023
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
@@ -55621,19 +56066,16 @@ function createCostsViewHandler(service, shell, opts = {}) {
|
|
|
55621
56066
|
}
|
|
55622
56067
|
const by = route3.view === "daily" ? Promise.resolve(null) : orNone(byReport(route3.view));
|
|
55623
56068
|
Promise.all([orNone(service.report(group, days)), by]).then(([report2, byReport2]) => {
|
|
55624
|
-
res
|
|
55625
|
-
|
|
55626
|
-
|
|
55627
|
-
|
|
55628
|
-
|
|
55629
|
-
|
|
55630
|
-
|
|
55631
|
-
|
|
55632
|
-
|
|
55633
|
-
|
|
55634
|
-
canSnapshot: canSnapshot(ctx.actor)
|
|
55635
|
-
})
|
|
55636
|
-
);
|
|
56069
|
+
page2(req, res, 200, ctx.actor, `${report2?.label ?? group} spend`, {
|
|
56070
|
+
page: "costs",
|
|
56071
|
+
group,
|
|
56072
|
+
report: report2,
|
|
56073
|
+
groups,
|
|
56074
|
+
view: route3.view,
|
|
56075
|
+
...byReport2 ? { by: byReport2 } : {},
|
|
56076
|
+
snapshot: service.status(),
|
|
56077
|
+
canSnapshot: canSnapshot(ctx.actor)
|
|
56078
|
+
});
|
|
55637
56079
|
}).catch(failed);
|
|
55638
56080
|
return true;
|
|
55639
56081
|
};
|
|
@@ -55646,7 +56088,6 @@ var init_costsView = __esm({
|
|
|
55646
56088
|
init_names();
|
|
55647
56089
|
init_costsService();
|
|
55648
56090
|
init_sse();
|
|
55649
|
-
init_webShell();
|
|
55650
56091
|
DIMENSION_OF_VIEW = Object.freeze({
|
|
55651
56092
|
users: "user",
|
|
55652
56093
|
threads: "thread",
|
|
@@ -55679,7 +56120,7 @@ function plain3(res, status3, body, extra = {}) {
|
|
|
55679
56120
|
res.writeHead(status3, { "content-type": "text/plain; charset=utf-8", "cache-control": "no-store", ...extra });
|
|
55680
56121
|
res.end(body);
|
|
55681
56122
|
}
|
|
55682
|
-
function createDeliveryViewHandler(deps,
|
|
56123
|
+
function createDeliveryViewHandler(deps, page2) {
|
|
55683
56124
|
return (req, res, ctx) => {
|
|
55684
56125
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
55685
56126
|
const route3 = parseDeliveryRoute(url.pathname);
|
|
@@ -55708,13 +56149,13 @@ function createDeliveryViewHandler(deps, shell) {
|
|
|
55708
56149
|
const fresh = url.searchParams.get("fresh") === "1";
|
|
55709
56150
|
const runs = async (range) => {
|
|
55710
56151
|
if (!deps.runs) return [];
|
|
55711
|
-
const
|
|
56152
|
+
const page3 = await deps.runs.listRuns({
|
|
55712
56153
|
status: "finished",
|
|
55713
56154
|
visibleTo: predicateFor(ctx.actor, "runs:read", "run"),
|
|
55714
56155
|
sinceMs: Date.parse(`${range.since}T00:00:00Z`),
|
|
55715
56156
|
limit: RUN_LIST_MAX_LIMIT
|
|
55716
56157
|
});
|
|
55717
|
-
return runFactsOf(
|
|
56158
|
+
return runFactsOf(page3.runs, repo);
|
|
55718
56159
|
};
|
|
55719
56160
|
deps.service.report(repo, {
|
|
55720
56161
|
...weeksParam !== null ? { weeks: Number(weeksParam) } : {},
|
|
@@ -55728,9 +56169,7 @@ function createDeliveryViewHandler(deps, shell) {
|
|
|
55728
56169
|
res.end(body);
|
|
55729
56170
|
return;
|
|
55730
56171
|
}
|
|
55731
|
-
|
|
55732
|
-
res.writeHead(200, WEB_HTML_HEADERS);
|
|
55733
|
-
res.end(html);
|
|
56172
|
+
page2(req, res, 200, ctx.actor, `${report2.repo} delivery`, { page: "delivery", report: report2, repos });
|
|
55734
56173
|
}).catch((err2) => {
|
|
55735
56174
|
const reason2 = (err2 instanceof Error ? err2.message : String(err2)).slice(0, UPSTREAM_REASON_MAX4);
|
|
55736
56175
|
plain3(res, 502, `delivery sources unavailable: ${reason2}`);
|
|
@@ -55745,7 +56184,6 @@ var init_deliveryView = __esm({
|
|
|
55745
56184
|
init_predicate();
|
|
55746
56185
|
init_delivery();
|
|
55747
56186
|
init_runRecord();
|
|
55748
|
-
init_webShell();
|
|
55749
56187
|
SEGMENT = /^(?!\.+$)[\w.-]{1,100}$/;
|
|
55750
56188
|
DELIVERY_NO_REPOS_MESSAGE = "No repository to report on \u2014 name one or more `owner/name` under delivery.repos in config to enable this view (the `delivery report --repo` command needs no config).";
|
|
55751
56189
|
UPSTREAM_REASON_MAX4 = 400;
|
|
@@ -55810,7 +56248,7 @@ function channelScopeView(description) {
|
|
|
55810
56248
|
function homeTab(caps) {
|
|
55811
56249
|
return caps.mcp ? "mcps" : "channels";
|
|
55812
56250
|
}
|
|
55813
|
-
function createSettingsViewHandler(deps,
|
|
56251
|
+
function createSettingsViewHandler(deps, page2) {
|
|
55814
56252
|
const canWrite = (caller, action, kind, id) => authorize(
|
|
55815
56253
|
caller.actor,
|
|
55816
56254
|
action,
|
|
@@ -55905,10 +56343,7 @@ function createSettingsViewHandler(deps, shell) {
|
|
|
55905
56343
|
}
|
|
55906
56344
|
plain4(res, 502, `settings unavailable: ${reason2}`);
|
|
55907
56345
|
};
|
|
55908
|
-
const render3 = (viewer, seed) =>
|
|
55909
|
-
res.writeHead(200, WEB_HTML_HEADERS);
|
|
55910
|
-
res.end(shell(viewer, "Settings", seed));
|
|
55911
|
-
};
|
|
56346
|
+
const render3 = (viewer, seed) => page2(req, res, 200, viewer, "Settings", seed);
|
|
55912
56347
|
const channel = "channel" in route3 ? route3.channel : void 0;
|
|
55913
56348
|
deps.callerFor(ctx.identity).then(async (caller) => {
|
|
55914
56349
|
const base = {
|
|
@@ -55941,7 +56376,6 @@ var init_settingsView = __esm({
|
|
|
55941
56376
|
"use strict";
|
|
55942
56377
|
init_authorize();
|
|
55943
56378
|
init_names();
|
|
55944
|
-
init_webShell();
|
|
55945
56379
|
CHANNEL_ID_RE = /^[a-z]+:[A-Za-z0-9_.:-]{1,80}$/;
|
|
55946
56380
|
UPSTREAM_REASON_MAX5 = 400;
|
|
55947
56381
|
failureText = (r) => r.message;
|
|
@@ -57335,18 +57769,22 @@ async function contractFor(instance, unit, readers) {
|
|
|
57335
57769
|
if (isGenerated(instance)) {
|
|
57336
57770
|
source2 = { unit: await generatedUnitOf(instance, unit, readers) };
|
|
57337
57771
|
} else {
|
|
57338
|
-
const
|
|
57339
|
-
if (
|
|
57772
|
+
const plan3 = await readers.readRepoFile(instance.plan.path, { maxChars: PLAN_MAX_CHARS });
|
|
57773
|
+
if (plan3 === void 0)
|
|
57340
57774
|
throw new Error(`the plan ${instance.plan.path} is not readable at ${rebase.onto} in ${instance.repo}`);
|
|
57341
|
-
|
|
57775
|
+
if (plan3.truncated)
|
|
57776
|
+
throw new Error(
|
|
57777
|
+
`the plan ${instance.plan.path} is longer than ${PLAN_MAX_CHARS.toLocaleString("en-US")} characters at ${rebase.onto} in ${instance.repo}; a unit read from a cut plan could be briefed short, so none is`
|
|
57778
|
+
);
|
|
57779
|
+
source2 = { planMarkdown: plan3.content, unitId: unit.unit };
|
|
57342
57780
|
}
|
|
57343
57781
|
const section = ("unit" in source2 ? source2.unit : parsePlanUnit(source2.planMarkdown, unit.unit))?.section ?? "";
|
|
57344
57782
|
const specs = /* @__PURE__ */ new Map();
|
|
57345
57783
|
for (const spec of new Set(specItemRefs(section).map((r) => r.spec)))
|
|
57346
|
-
specs.set(spec, await readers.readRepoFile(`${SPECS_DIR2}/${spec}`));
|
|
57784
|
+
specs.set(spec, (await readers.readRepoFile(`${SPECS_DIR2}/${spec}`))?.content);
|
|
57347
57785
|
let agentRules;
|
|
57348
57786
|
for (const file of RULES_FILES2) {
|
|
57349
|
-
const text = await readers.readRepoFile(file);
|
|
57787
|
+
const text = (await readers.readRepoFile(file))?.content;
|
|
57350
57788
|
if (text !== void 0) {
|
|
57351
57789
|
agentRules = { file, text };
|
|
57352
57790
|
break;
|
|
@@ -58474,9 +58912,10 @@ ${planSummary(units)}`).catch(() => {
|
|
|
58474
58912
|
function briefReaders(deps, instance) {
|
|
58475
58913
|
const ref = instance.base ?? "main";
|
|
58476
58914
|
return {
|
|
58477
|
-
readRepoFile: async (path) => {
|
|
58915
|
+
readRepoFile: async (path, opts) => {
|
|
58478
58916
|
try {
|
|
58479
|
-
|
|
58917
|
+
const file = await deps.github.readFile(instance.repo, path, ref, opts);
|
|
58918
|
+
return { content: file.content, truncated: file.truncated };
|
|
58480
58919
|
} catch {
|
|
58481
58920
|
return void 0;
|
|
58482
58921
|
}
|
|
@@ -59180,17 +59619,17 @@ async function runBot() {
|
|
|
59180
59619
|
const cronArmed = Object.values(auth.tokens).some((id) => id.subject === "cron");
|
|
59181
59620
|
const schedulesState = `${SCHEDULES.length} schedule(s) on /runs (${capabilities.schedules ? `firings from ${config.config.schedules?.worker?.baseUrl}` : "no firing store"}; cron identity ${cronArmed ? "armed" : "NOT in SWITCHBOARD_INGRESS_TOKENS \u2014 scheduled runs fail closed"})`;
|
|
59182
59621
|
const webAssets = loadWebAssets(webDistDir(publicEnv(), PACKAGE_ROOT));
|
|
59183
|
-
const
|
|
59622
|
+
const page2 = makePageSender(webAssets.entry, capabilities);
|
|
59184
59623
|
const residentsView = createResidentsViewHandler({
|
|
59185
59624
|
client: residentAdminClient,
|
|
59186
|
-
|
|
59625
|
+
page: page2,
|
|
59187
59626
|
runs: defaultRunRegistry,
|
|
59188
59627
|
trace: { config, spanLog }
|
|
59189
59628
|
});
|
|
59190
59629
|
const residentsState = residentAdminClient instanceof NullResidentAdminClient ? `GET /residents (503 \u2014 ${residentAdminClient.reason})` : `GET /residents (dash \u2192 ${config.config.execution?.resident?.baseUrl})`;
|
|
59191
|
-
const costsView = createCostsViewHandler(costsService,
|
|
59630
|
+
const costsView = createCostsViewHandler(costsService, page2, { names });
|
|
59192
59631
|
const costsState = costs ? `GET /costs (${costsService.groups().join(",")}; LLM ${costs.llmOn ? "on" : "off"}; snapshot every ${costsCfg?.snapshot.everyHours ?? "?"} h)` : costsCfg ? `GET /costs (503 \u2014 ${costsCfg.cloudflareTokenEnv} not set)` : "GET /costs (503 \u2014 no costs config)";
|
|
59193
|
-
const deliveryView = createDeliveryViewHandler({ service: deliveryService, runs: runsService },
|
|
59632
|
+
const deliveryView = createDeliveryViewHandler({ service: deliveryService, runs: runsService }, page2);
|
|
59194
59633
|
const settingsView = createSettingsViewHandler(
|
|
59195
59634
|
{
|
|
59196
59635
|
commands,
|
|
@@ -59205,14 +59644,14 @@ async function runBot() {
|
|
|
59205
59644
|
},
|
|
59206
59645
|
capabilities
|
|
59207
59646
|
},
|
|
59208
|
-
|
|
59647
|
+
page2
|
|
59209
59648
|
);
|
|
59210
59649
|
const webChat = createWebChatHandler({
|
|
59211
59650
|
core: deps,
|
|
59212
59651
|
service: runsService,
|
|
59213
59652
|
registry: defaultRunRegistry,
|
|
59214
59653
|
commands,
|
|
59215
|
-
|
|
59654
|
+
page: page2,
|
|
59216
59655
|
capabilities,
|
|
59217
59656
|
names,
|
|
59218
59657
|
retention: capabilities.runHistory && runHistoryCfg ? { retentionDays: retentionPolicyOf(runHistoryCfg).retentionDays } : null,
|
|
@@ -59223,10 +59662,18 @@ async function runBot() {
|
|
|
59223
59662
|
const liveViewState = process.env.PUBLIC_BASE_URL ? "GET /runs (index) + /runs/:id (live view)" : "GET /runs (index) + live view (no PUBLIC_BASE_URL \u2014 per-run links omitted)";
|
|
59224
59663
|
const accessConfig = parseAccessConfig(publicEnv());
|
|
59225
59664
|
const publicBaseUrl = process.env.PUBLIC_BASE_URL;
|
|
59665
|
+
const viewAsPeople = peopleSource({
|
|
59666
|
+
granted: () => config.grantedPeople(),
|
|
59667
|
+
requesters: async () => (await runsService.listRuns({ status: "all", visibleTo: { kind: "all" }, limit: RUN_LIST_MAX_LIMIT })).runs,
|
|
59668
|
+
name: personName
|
|
59669
|
+
});
|
|
59670
|
+
void viewAsPeople.refresh();
|
|
59226
59671
|
const liveView2 = createLiveViewHandler({
|
|
59227
|
-
|
|
59672
|
+
page: page2,
|
|
59228
59673
|
service: runsService,
|
|
59229
59674
|
index: defaultRunRegistry,
|
|
59675
|
+
// The view-as picker's people (record 0053): the cache `viewAsPeople` keeps.
|
|
59676
|
+
people: () => viewAsPeople.current(),
|
|
59230
59677
|
retention: capabilities.runHistory && runHistoryCfg ? { retentionDays: retentionPolicyOf(runHistoryCfg).retentionDays } : null,
|
|
59231
59678
|
// The panel's own off-state wording (live-view item 14: "firing history
|
|
59232
59679
|
// unavailable", never "never fired") stands until the dashboard paints the
|
|
@@ -59603,6 +60050,7 @@ var init_index = __esm({
|
|
|
59603
60050
|
init_confirmations();
|
|
59604
60051
|
init_mcpConnectView();
|
|
59605
60052
|
init_viewAs2();
|
|
60053
|
+
init_runRecord();
|
|
59606
60054
|
init_lookups();
|
|
59607
60055
|
init_slackNames();
|
|
59608
60056
|
init_names();
|