usebeeline 0.0.92 → 0.0.94
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/usebeeline.mjs +273 -42
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -2413,7 +2413,7 @@ var require_websocket = __commonJS({
|
|
|
2413
2413
|
var EventEmitter2 = __require("events");
|
|
2414
2414
|
var https = __require("https");
|
|
2415
2415
|
var http = __require("http");
|
|
2416
|
-
var
|
|
2416
|
+
var net2 = __require("net");
|
|
2417
2417
|
var tls = __require("tls");
|
|
2418
2418
|
var { randomBytes: randomBytes7, createHash: createHash9 } = __require("crypto");
|
|
2419
2419
|
var { Duplex, Readable } = __require("stream");
|
|
@@ -3157,12 +3157,12 @@ var require_websocket = __commonJS({
|
|
|
3157
3157
|
}
|
|
3158
3158
|
function netConnect(options) {
|
|
3159
3159
|
options.path = options.socketPath;
|
|
3160
|
-
return
|
|
3160
|
+
return net2.connect(options);
|
|
3161
3161
|
}
|
|
3162
3162
|
function tlsConnect(options) {
|
|
3163
3163
|
options.path = void 0;
|
|
3164
3164
|
if (!options.servername && options.servername !== "") {
|
|
3165
|
-
options.servername =
|
|
3165
|
+
options.servername = net2.isIP(options.host) ? "" : options.host;
|
|
3166
3166
|
}
|
|
3167
3167
|
return tls.connect(options);
|
|
3168
3168
|
}
|
|
@@ -4654,6 +4654,19 @@ var init_self_update = __esm({
|
|
|
4654
4654
|
}
|
|
4655
4655
|
});
|
|
4656
4656
|
|
|
4657
|
+
// apps/body/dist/network-family-bootstrap.js
|
|
4658
|
+
import * as net from "node:net";
|
|
4659
|
+
var NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS = 5e3;
|
|
4660
|
+
function configureNetworkFamilyDefaults(network = net) {
|
|
4661
|
+
if (typeof network.setDefaultAutoSelectFamilyAttemptTimeout === "function") {
|
|
4662
|
+
network.setDefaultAutoSelectFamilyAttemptTimeout(NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS);
|
|
4663
|
+
return "timeout";
|
|
4664
|
+
}
|
|
4665
|
+
network.setDefaultAutoSelectFamily?.(false);
|
|
4666
|
+
return "autoselection-disabled";
|
|
4667
|
+
}
|
|
4668
|
+
configureNetworkFamilyDefaults();
|
|
4669
|
+
|
|
4657
4670
|
// apps/body/dist/cli.js
|
|
4658
4671
|
import { dirname as dirname17, resolve as resolve30 } from "node:path";
|
|
4659
4672
|
import { readFile as readFile15, unlink as unlink5, writeFile as writeFile16 } from "node:fs/promises";
|
|
@@ -8010,7 +8023,24 @@ async function withAgentModelCatalog(agent, agentEnv, selection, inspect, limits
|
|
|
8010
8023
|
}
|
|
8011
8024
|
}
|
|
8012
8025
|
async function fetchAgentModelCatalog(agent, agentEnv, selection, limits = {}) {
|
|
8013
|
-
return withAgentModelCatalog(agent, agentEnv, selection, async ({ raw, catalog }) => ({
|
|
8026
|
+
return withAgentModelCatalog(agent, agentEnv, selection, async ({ client, sessionId, raw, catalog }) => ({
|
|
8027
|
+
raw,
|
|
8028
|
+
catalog: await filterModelChoicesByLiveValidation(client, sessionId, catalog)
|
|
8029
|
+
}), limits);
|
|
8030
|
+
}
|
|
8031
|
+
async function filterModelChoicesByLiveValidation(client, sessionId, catalog) {
|
|
8032
|
+
const modelAxis = catalog.find((axis) => axis.category === "model");
|
|
8033
|
+
if (!modelAxis)
|
|
8034
|
+
return catalog;
|
|
8035
|
+
const available = [];
|
|
8036
|
+
for (const choice of modelAxis.options) {
|
|
8037
|
+
try {
|
|
8038
|
+
await applyAgentModelSelection(client, sessionId, catalog, { model: choice.id });
|
|
8039
|
+
available.push(choice);
|
|
8040
|
+
} catch {
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
return catalog.map((axis) => axis === modelAxis ? { ...axis, options: available } : axis);
|
|
8014
8044
|
}
|
|
8015
8045
|
async function validateAgentModelSelection(agent, agentEnv, selection) {
|
|
8016
8046
|
return withAgentModelCatalog(agent, agentEnv, selection, async ({ client, sessionId, raw, catalog }) => {
|
|
@@ -8066,8 +8096,12 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
8066
8096
|
import { resolve as resolve5 } from "node:path";
|
|
8067
8097
|
var MODEL_CATALOG_PROBE_TIMEOUT_MS = 3e4;
|
|
8068
8098
|
var MODEL_CATALOG_HASH_FILE = "model-catalog.sha256";
|
|
8069
|
-
function modelCatalogHash(options, selection) {
|
|
8070
|
-
return createHash("sha256").update(JSON.stringify({
|
|
8099
|
+
function modelCatalogHash(options, selection, startupUnavailable) {
|
|
8100
|
+
return createHash("sha256").update(JSON.stringify({
|
|
8101
|
+
options,
|
|
8102
|
+
selection: selection ?? null,
|
|
8103
|
+
startupUnavailable: startupUnavailable ?? null
|
|
8104
|
+
})).digest("hex");
|
|
8071
8105
|
}
|
|
8072
8106
|
async function withTimeout(work, timeoutMs, what) {
|
|
8073
8107
|
let timer;
|
|
@@ -8093,8 +8127,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
8093
8127
|
...configuration.model ? { model: configuration.model } : {},
|
|
8094
8128
|
...configuration.effort ? { effort: configuration.effort } : {}
|
|
8095
8129
|
} : input.runtimeSelection;
|
|
8096
|
-
const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
|
|
8097
|
-
const hash = modelCatalogHash(catalog, selection);
|
|
8130
|
+
const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, input.startupUnavailable ? void 0 : selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
|
|
8131
|
+
const hash = modelCatalogHash(catalog, selection, input.startupUnavailable);
|
|
8098
8132
|
const previous = await readFile(hashPath, "utf8").catch(() => "");
|
|
8099
8133
|
if (previous.trim() === hash)
|
|
8100
8134
|
return "unchanged";
|
|
@@ -8103,7 +8137,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
8103
8137
|
workspaceId: input.workspaceId,
|
|
8104
8138
|
// `fetchAgentModelCatalog` already applied the category allow-list.
|
|
8105
8139
|
options: catalog,
|
|
8106
|
-
...selection ? { selection } : {}
|
|
8140
|
+
...selection ? { selection } : {},
|
|
8141
|
+
...input.startupUnavailable ? { unavailable: input.startupUnavailable } : {}
|
|
8107
8142
|
});
|
|
8108
8143
|
await writeFile(hashPath, `${hash}
|
|
8109
8144
|
`, { mode: 384 });
|
|
@@ -18201,6 +18236,7 @@ function isAgentPairingCode(value) {
|
|
|
18201
18236
|
|
|
18202
18237
|
// apps/body/dist/beeline-skill.js
|
|
18203
18238
|
var USING_BEELINE_SKILL_NAME = "using-beeline";
|
|
18239
|
+
var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
|
|
18204
18240
|
var BEELINE_ROOM_CAPABILITIES = [
|
|
18205
18241
|
"The repository filesystem is read-only in this Room session.",
|
|
18206
18242
|
"You may address any Room member, including another agent, by writing @name in your reply; the server routes that mention to them. Each turn prompt lists the Room members and the exact spelling that tags each one - use those spellings, and never guess or reuse one from an older message.",
|
|
@@ -18265,6 +18301,86 @@ description: How to answer inside a Beeline Room.
|
|
|
18265
18301
|
You are answering inside a Room whose filesystem is read-only. ${BEELINE_ROOM_CAPABILITIES}
|
|
18266
18302
|
`;
|
|
18267
18303
|
}
|
|
18304
|
+
function beelineReviewSkillMarkdown(releaseId) {
|
|
18305
|
+
return `---
|
|
18306
|
+
name: beeline-review
|
|
18307
|
+
description: Review a corner pull request against its objective and the Beeline merge gate.
|
|
18308
|
+
---
|
|
18309
|
+
|
|
18310
|
+
<!-- beeline-release: ${releaseId} -->
|
|
18311
|
+
|
|
18312
|
+
# Beeline pull-request review
|
|
18313
|
+
|
|
18314
|
+
Follow these steps in order. Do not skip or reorder them.
|
|
18315
|
+
|
|
18316
|
+
## 1. Isolate the revision
|
|
18317
|
+
|
|
18318
|
+
- Run \`gh pr view N --json headRefOid,files\` and record \`headRefOid\`.
|
|
18319
|
+
- Run \`gh pr diff N\`.
|
|
18320
|
+
- Check out that exact head in a new scratch git worktree. Never use the author's worktree.
|
|
18321
|
+
- Review and test only the recorded revision. If the head moves, start over.
|
|
18322
|
+
|
|
18323
|
+
## 2. P0 - OBJECTIVE FULFILLED, DEMONSTRATED
|
|
18324
|
+
|
|
18325
|
+
- Quote the corner objective verbatim.
|
|
18326
|
+
- Derive its end-user story in one sentence: \`a user who does X sees Y\`.
|
|
18327
|
+
- Make Y happen against the built PR head: run the app or affected service and perform X.
|
|
18328
|
+
- If no interactive surface is reachable, run the narrowest test or script that exercises the exact user path and prints the observable Y.
|
|
18329
|
+
- Record the command and the observed Y.
|
|
18330
|
+
- A unit test of an inner function, a log line, \`the code looks right\`, or any other proxy does not count.
|
|
18331
|
+
- If the user-visible Y cannot be produced, FAIL now. Nothing below can rescue the review.
|
|
18332
|
+
- State whether the diff fulfills that objective and only that objective.
|
|
18333
|
+
|
|
18334
|
+
## 3. Empirical pass second
|
|
18335
|
+
|
|
18336
|
+
- Run the repository typecheck and tests touched by the diff.
|
|
18337
|
+
- If the objective names a user path, exercise that path.
|
|
18338
|
+
- Record every command and exit code.
|
|
18339
|
+
- A review with no executed command is invalid and must FAIL.
|
|
18340
|
+
|
|
18341
|
+
## 4. Adversarial pass
|
|
18342
|
+
|
|
18343
|
+
- For every changed function, name one concrete input or sequence that breaks it.
|
|
18344
|
+
- If none is found, write \`none found\` for that function.
|
|
18345
|
+
|
|
18346
|
+
## 5. Verify before reporting
|
|
18347
|
+
|
|
18348
|
+
- Confirm every finding by reading the exact line or by running a command.
|
|
18349
|
+
- Put unconfirmed concerns under plausible findings. They never block.
|
|
18350
|
+
|
|
18351
|
+
## 6. Bloat guard
|
|
18352
|
+
|
|
18353
|
+
- Compare net lines with the objective.
|
|
18354
|
+
- FAIL backwards-compatibility shims, dual paths, feature flags, or abstractions with one caller.
|
|
18355
|
+
- FAIL machinery the objective did not ask for.
|
|
18356
|
+
|
|
18357
|
+
## 7. Security and data
|
|
18358
|
+
|
|
18359
|
+
- Check credentials, authorization boundaries, and destructive migrations.
|
|
18360
|
+
|
|
18361
|
+
## 8. Gate and verdict
|
|
18362
|
+
|
|
18363
|
+
- The merge gate is open only when \`pr_checks_status\` reports checks=passed, held=false, approvalPending=false.
|
|
18364
|
+
- Green \`gh pr checks\` alone never opens the gate.
|
|
18365
|
+
- Always use this exact verdict shape:
|
|
18366
|
+
|
|
18367
|
+
\`objective quoted:\`
|
|
18368
|
+
\`user story:\`
|
|
18369
|
+
\`how Y was demonstrated (or FAIL):\`
|
|
18370
|
+
\`commands run + results:\`
|
|
18371
|
+
\`critical findings (block):\`
|
|
18372
|
+
\`plausible findings (do not block):\`
|
|
18373
|
+
\`net lines:\`
|
|
18374
|
+
\`decision: PASS|FAIL\`
|
|
18375
|
+
|
|
18376
|
+
Then take exactly one action:
|
|
18377
|
+
|
|
18378
|
+
- FAIL: reply \`@author\` with the confirmed findings; do not merge.
|
|
18379
|
+
- PASS with pending checks: reply \`approved pending checks <reviewed sha>\` and stop.
|
|
18380
|
+
- PASS with the gate open and your yolo on: run \`gh pr merge --squash --match-head-commit <reviewed sha> N\`.
|
|
18381
|
+
- PASS with the gate open and your yolo off: reply \`approved <reviewed sha>\` and stop.
|
|
18382
|
+
`;
|
|
18383
|
+
}
|
|
18268
18384
|
|
|
18269
18385
|
// apps/body/dist/external-mcp-capabilities.js
|
|
18270
18386
|
var SQUIRE_MCP_VERSION = "1.1.12";
|
|
@@ -18862,7 +18978,10 @@ var SHARED_CREDENTIALS = [
|
|
|
18862
18978
|
{ dir: "pi", source: ".pi/agent/auth.json", target: "auth.json" }
|
|
18863
18979
|
];
|
|
18864
18980
|
var GOOSE_SHARED_CONFIG_FILES = ["config.yaml", "secrets.yaml"];
|
|
18865
|
-
var BEELINE_DEFAULT_SKILL_NAMES = [
|
|
18981
|
+
var BEELINE_DEFAULT_SKILL_NAMES = [
|
|
18982
|
+
BEELINE_REVIEW_SKILL_NAME,
|
|
18983
|
+
USING_BEELINE_SKILL_NAME
|
|
18984
|
+
];
|
|
18866
18985
|
var OPERATOR_SKILL_SOURCE_DIRS = [
|
|
18867
18986
|
".agents/skills",
|
|
18868
18987
|
".claude/skills",
|
|
@@ -18941,7 +19060,8 @@ async function prepareRoomAgentHome(input) {
|
|
|
18941
19060
|
}
|
|
18942
19061
|
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting) {
|
|
18943
19062
|
const managedSkills = [
|
|
18944
|
-
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) }
|
|
19063
|
+
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) },
|
|
19064
|
+
{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }
|
|
18945
19065
|
];
|
|
18946
19066
|
const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
|
|
18947
19067
|
await provisionManagedSkillsDir(resolve13(root, skillDir, "skills"), managedSkills, shared, sharedSkills.length === 0);
|
|
@@ -19617,6 +19737,12 @@ var AgentTurnStream = class {
|
|
|
19617
19737
|
inFlight;
|
|
19618
19738
|
/** Closed lanes publish nothing more, so the answer never queues behind a draft. */
|
|
19619
19739
|
closed = false;
|
|
19740
|
+
/**
|
|
19741
|
+
* The retract this lane already sent. A settled turn that throws afterwards
|
|
19742
|
+
* reaches the same retract a second time, and one empty lane is the whole
|
|
19743
|
+
* point: asking twice would only be a second write saying what is already so.
|
|
19744
|
+
*/
|
|
19745
|
+
retraction;
|
|
19620
19746
|
constructor(options) {
|
|
19621
19747
|
this.options = options;
|
|
19622
19748
|
}
|
|
@@ -19676,14 +19802,45 @@ var AgentTurnStream = class {
|
|
|
19676
19802
|
this.closed = true;
|
|
19677
19803
|
this.pending = void 0;
|
|
19678
19804
|
}
|
|
19805
|
+
/**
|
|
19806
|
+
* Dissolve the draft, publishing nothing.
|
|
19807
|
+
*
|
|
19808
|
+
* Every ending uses this: the settle below calls it once the durable reply is
|
|
19809
|
+
* on the wire, and a turn that THROWS calls it directly. A throw never
|
|
19810
|
+
* reaches a settle, and `close()` alone only stops future writes — the last
|
|
19811
|
+
* snapshot stays live on the page, so a turn the Room has already reported
|
|
19812
|
+
* stopped or failed keeps a half-written answer visibly in progress under it.
|
|
19813
|
+
*/
|
|
19814
|
+
async retract() {
|
|
19815
|
+
this.close();
|
|
19816
|
+
this.retraction ??= (async () => {
|
|
19817
|
+
await this.inFlight;
|
|
19818
|
+
const { api, agentId, roomId, requestId, label } = this.options;
|
|
19819
|
+
await api.execute("retractAgentLiveOutput", {
|
|
19820
|
+
agentId,
|
|
19821
|
+
roomId,
|
|
19822
|
+
turnId: requestId,
|
|
19823
|
+
kind: "draft"
|
|
19824
|
+
}).catch((error) => console.error(`[thin-core] ${label} draft retract failed:`, error));
|
|
19825
|
+
})();
|
|
19826
|
+
await this.retraction;
|
|
19827
|
+
}
|
|
19679
19828
|
/**
|
|
19680
19829
|
* Post the durable reply under the turn's request id and dissolve the draft.
|
|
19681
19830
|
* An empty reply settles through the turn receipt instead, and the lane is
|
|
19682
19831
|
* retracted either way.
|
|
19832
|
+
*
|
|
19833
|
+
* The durable reply is this turn's answer and its last word. The draft lane
|
|
19834
|
+
* is presentation, so a refused retract is logged like a refused draft and
|
|
19835
|
+
* the turn still settles complete: raising here failed a turn that had
|
|
19836
|
+
* already answered, which posts a `failed` receipt and inscribes "<agent>
|
|
19837
|
+
* could not answer" UNDER the answer the reader is looking at. The reader
|
|
19838
|
+
* loses nothing by it either — the phone ends a retracted draft on the
|
|
19839
|
+
* turn's own complete receipt (`visibleLiveOverlays`).
|
|
19683
19840
|
*/
|
|
19684
19841
|
async settle(reply, fields = {}, onReplyPosted) {
|
|
19685
19842
|
this.close();
|
|
19686
|
-
const { api,
|
|
19843
|
+
const { api, roomId, requestId } = this.options;
|
|
19687
19844
|
if (reply) {
|
|
19688
19845
|
const posted = await api.execute("postRoomMessage", {
|
|
19689
19846
|
roomId,
|
|
@@ -19694,13 +19851,7 @@ var AgentTurnStream = class {
|
|
|
19694
19851
|
});
|
|
19695
19852
|
onReplyPosted?.(posted);
|
|
19696
19853
|
}
|
|
19697
|
-
await this.
|
|
19698
|
-
await api.execute("retractAgentLiveOutput", {
|
|
19699
|
-
agentId,
|
|
19700
|
-
roomId,
|
|
19701
|
-
turnId: requestId,
|
|
19702
|
-
kind: "draft"
|
|
19703
|
-
});
|
|
19854
|
+
await this.retract();
|
|
19704
19855
|
}
|
|
19705
19856
|
};
|
|
19706
19857
|
|
|
@@ -19710,13 +19861,18 @@ function redactToolDetail(value) {
|
|
|
19710
19861
|
return value.replace(/\b(["']?)(api[_-]?key|token|secret|password|passwd|authorization|credential|cookie|private[_-]?key)\1\s*[:=]\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,}\]]+)/gi, '"$2": "[REDACTED]"').replace(/\b(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\S+)/g, (assignment) => `${assignment.slice(0, assignment.indexOf("="))}=[REDACTED]`).replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/gi, "[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(Bearer\s+)[^\s,]+/gi, "$1[REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g, "[REDACTED]").replace(/(--?(?:api[_-]?key|token|secret|password|authorization|credential|cookie)\s+)(?:"[^"]*"|'[^']*'|\S+)/gi, "$1[REDACTED]");
|
|
19711
19862
|
}
|
|
19712
19863
|
function distillTurnFailureReason(error) {
|
|
19864
|
+
if (error instanceof ModelSelectionUnavailableError) {
|
|
19865
|
+
return { text: "model selection unavailable", kind: "model-selection-unavailable" };
|
|
19866
|
+
}
|
|
19713
19867
|
const raw = error instanceof Error ? error.message : typeof error === "string" ? error : error && typeof error === "object" && "message" in error ? String(error.message) : error == null ? "" : String(error);
|
|
19714
19868
|
const firstLine = raw.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !/^at\s/.test(line)) ?? "";
|
|
19715
19869
|
const stripped = firstLine.replace(/^(?:[A-Za-z]*Error|Error):\s*/, "").replace(/\s+/g, " ");
|
|
19716
19870
|
const clean4 = redactToolDetail(stripped).trim();
|
|
19717
19871
|
if (!clean4)
|
|
19718
|
-
return "turn failed";
|
|
19719
|
-
return
|
|
19872
|
+
return { text: "turn failed" };
|
|
19873
|
+
return {
|
|
19874
|
+
text: clean4.length > TURN_FAILURE_REASON_MAX ? `${clean4.slice(0, TURN_FAILURE_REASON_MAX - 1)}\u2026` : clean4
|
|
19875
|
+
};
|
|
19720
19876
|
}
|
|
19721
19877
|
|
|
19722
19878
|
// apps/body/dist/tool-call-failure.js
|
|
@@ -19750,6 +19906,9 @@ function toolCallText(content) {
|
|
|
19750
19906
|
function isFailedToolCall(call) {
|
|
19751
19907
|
return /^(?:failed|error|denied|rejected)$/i.test(call.status ?? "");
|
|
19752
19908
|
}
|
|
19909
|
+
function isCompletedToolCall(call) {
|
|
19910
|
+
return /^completed$/i.test(call.status ?? "");
|
|
19911
|
+
}
|
|
19753
19912
|
function toolCallFailureLine(call) {
|
|
19754
19913
|
if (!isFailedToolCall(call))
|
|
19755
19914
|
return void 0;
|
|
@@ -19760,14 +19919,17 @@ function toolCallFailureLine(call) {
|
|
|
19760
19919
|
|
|
19761
19920
|
// apps/body/dist/session-config-fingerprint.js
|
|
19762
19921
|
function sessionConfigFingerprint(input) {
|
|
19763
|
-
|
|
19922
|
+
const fingerprint = [
|
|
19764
19923
|
input.model ?? "",
|
|
19765
19924
|
input.effort ?? "",
|
|
19766
19925
|
input.soul?.name ?? "",
|
|
19767
19926
|
input.soul?.instructions ?? "",
|
|
19768
19927
|
input.agentName ?? "",
|
|
19769
19928
|
input.yoloMode ?? false
|
|
19770
|
-
]
|
|
19929
|
+
];
|
|
19930
|
+
if (input.reviewerHandle !== void 0)
|
|
19931
|
+
fingerprint.push(input.reviewerHandle);
|
|
19932
|
+
return JSON.stringify(fingerprint);
|
|
19771
19933
|
}
|
|
19772
19934
|
|
|
19773
19935
|
// apps/body/dist/pi-mcp-bridge.js
|
|
@@ -21558,6 +21720,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21558
21720
|
const api = this.options.api;
|
|
21559
21721
|
this.busy = true;
|
|
21560
21722
|
const trace = this.beginTurnTrace(item.id);
|
|
21723
|
+
let liveStream;
|
|
21561
21724
|
try {
|
|
21562
21725
|
if (!this.memberNames.has(item.authorId))
|
|
21563
21726
|
await this.roster().catch(() => void 0);
|
|
@@ -21624,6 +21787,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21624
21787
|
requestId: item.id,
|
|
21625
21788
|
label: `monolith Room ${this.options.roomId}`
|
|
21626
21789
|
});
|
|
21790
|
+
liveStream = stream;
|
|
21627
21791
|
const runPrompt = async () => {
|
|
21628
21792
|
let nextPrompt = promptWithImages(buildPrompt(), attachmentImageBlocks(delivered, this.acceptsImages()));
|
|
21629
21793
|
let result2;
|
|
@@ -21661,7 +21825,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21661
21825
|
};
|
|
21662
21826
|
let result = await runPrompt();
|
|
21663
21827
|
trace.promptSettled();
|
|
21664
|
-
let
|
|
21828
|
+
let openCornerCall = openCornerToolCall(result.toolCalls);
|
|
21829
|
+
let cornerOpened = openedACorner(openCornerCall);
|
|
21830
|
+
let explained = cornerOpened ? void 0 : await this.explainEmpty(result);
|
|
21665
21831
|
if (explained && shouldRetryEmptyTurn(explained)) {
|
|
21666
21832
|
const silent = this.servingProviders();
|
|
21667
21833
|
const next = await this.repinNextProvider(trace, explained.reason);
|
|
@@ -21669,7 +21835,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21669
21835
|
console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${turnFailureReasonWithProvider(explained.reason, silent)}; retrying on ${next}`);
|
|
21670
21836
|
result = await runPrompt();
|
|
21671
21837
|
trace.promptSettled();
|
|
21672
|
-
|
|
21838
|
+
openCornerCall = openCornerToolCall(result.toolCalls);
|
|
21839
|
+
cornerOpened = openedACorner(openCornerCall);
|
|
21840
|
+
explained = cornerOpened ? void 0 : await this.explainEmpty(result);
|
|
21673
21841
|
}
|
|
21674
21842
|
}
|
|
21675
21843
|
if (active.cancelled) {
|
|
@@ -21683,10 +21851,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21683
21851
|
} else if (resumedRequestId) {
|
|
21684
21852
|
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${resumedRequestId} resumed by grant decision ${item.id}`);
|
|
21685
21853
|
}
|
|
21686
|
-
const openCornerCall = result.toolCalls.find((call) => /(?:^|[._:/-])open_corner$/i.test(call.title ?? ""));
|
|
21687
21854
|
if (openCornerCall) {
|
|
21688
21855
|
console.log(`[thin-core] monolith Room ${this.options.roomId} tool call: ${openCornerCall.title} (${openCornerCall.status ?? "no status"})`);
|
|
21689
|
-
if (
|
|
21856
|
+
if (cornerOpened)
|
|
21690
21857
|
this.options.onCornerOpened?.();
|
|
21691
21858
|
}
|
|
21692
21859
|
for (const call of result?.toolCalls ?? []) {
|
|
@@ -21704,7 +21871,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21704
21871
|
}
|
|
21705
21872
|
console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${explained.reason}`);
|
|
21706
21873
|
}
|
|
21707
|
-
if (
|
|
21874
|
+
if (cornerOpened) {
|
|
21708
21875
|
reply = "";
|
|
21709
21876
|
}
|
|
21710
21877
|
await trace.measure("publish", () => stream.settle(reply, reply ? {
|
|
@@ -21726,6 +21893,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21726
21893
|
await trace.finish("cancelled");
|
|
21727
21894
|
return;
|
|
21728
21895
|
}
|
|
21896
|
+
await liveStream?.retract().catch((retractError) => {
|
|
21897
|
+
console.error(`[thin-core] monolith Room ${this.options.roomId} draft retract failed:`, retractError);
|
|
21898
|
+
});
|
|
21729
21899
|
const reason = distillTurnFailureReason(error);
|
|
21730
21900
|
await api.execute("postAgentTurnReceipt", {
|
|
21731
21901
|
agentId: this.agent.publicKey,
|
|
@@ -21733,9 +21903,10 @@ var MonolithRoomTurnLoop = class {
|
|
|
21733
21903
|
requestId: item.id,
|
|
21734
21904
|
status: "failed",
|
|
21735
21905
|
generationId: this.commandContext.generationId,
|
|
21736
|
-
reason
|
|
21906
|
+
reason: reason.text,
|
|
21907
|
+
...reason.kind ? { reasonKind: reason.kind } : {}
|
|
21737
21908
|
});
|
|
21738
|
-
await trace.finish("failed", reason);
|
|
21909
|
+
await trace.finish("failed", reason.text);
|
|
21739
21910
|
throw error;
|
|
21740
21911
|
} finally {
|
|
21741
21912
|
this.busy = false;
|
|
@@ -21778,6 +21949,12 @@ var MonolithRoomTurnLoop = class {
|
|
|
21778
21949
|
}
|
|
21779
21950
|
}
|
|
21780
21951
|
};
|
|
21952
|
+
function openCornerToolCall(calls) {
|
|
21953
|
+
return calls.find((call) => /(?:^|[._:/-])open_corner$/i.test(call.title ?? ""));
|
|
21954
|
+
}
|
|
21955
|
+
function openedACorner(call) {
|
|
21956
|
+
return !!call && isCompletedToolCall(call);
|
|
21957
|
+
}
|
|
21781
21958
|
function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true) {
|
|
21782
21959
|
const message = body.trim() || "(shared attachments)";
|
|
21783
21960
|
const rendered = author ? `${author}: ${message}` : message;
|
|
@@ -21789,10 +21966,31 @@ var execFileAsync3 = promisify3(execFile4);
|
|
|
21789
21966
|
var TOOL_ARGUMENT_MAX_BYTES = 1200;
|
|
21790
21967
|
var TOOL_OUTPUT_MAX_BYTES = 3200;
|
|
21791
21968
|
var TOOL_PATH_LIMIT = 12;
|
|
21792
|
-
function cornerMergeInstruction(yoloMode) {
|
|
21969
|
+
function cornerMergeInstruction(yoloMode, reviewerHandle) {
|
|
21970
|
+
if (reviewerHandle)
|
|
21971
|
+
return `Commit, push, open the pull request, reply with the PR URL and then @${reviewerHandle} please review; never merge this PR yourself. If you asked any other agent to review in this corner, do not merge until they answer.`;
|
|
21793
21972
|
return yoloMode ? "Yolo is on: when the gate passes, merge this pull request with gh." : "Yolo is off: never merge; wait for explicit human approval in the app.";
|
|
21794
21973
|
}
|
|
21795
|
-
|
|
21974
|
+
function cornerReviewerInstruction(input) {
|
|
21975
|
+
if (!input.reviewerHandle || !input.agentHandle || input.openedByAgent || input.agentHandle.replace(/^@/, "") !== input.reviewerHandle.replace(/^@/, ""))
|
|
21976
|
+
return void 0;
|
|
21977
|
+
const author = input.authorHandle?.replace(/^@/, "") || "author";
|
|
21978
|
+
const number = input.pullRequestNumber ?? "N";
|
|
21979
|
+
return `Review PR #${number} with the beeline-review skill. If it fails, reply @${author} with the findings. If it passes and the gate (pr_checks_status: checks=passed, held=false, approvalPending=false) is open and YOUR yolo is on, merge with gh pr merge --squash --match-head-commit <sha you reviewed>; if your yolo is off, reply approved <sha> and stop; if checks are pending, reply approved pending checks <sha> and stop.`;
|
|
21980
|
+
}
|
|
21981
|
+
var CORNER_AUTHOR_CONTRACT = `The objective text is the user's ask. Keep it verbatim in your head and do not reinterpret it.
|
|
21982
|
+
Before any code, write its end-user story in one sentence: "a person who does X sees Y".
|
|
21983
|
+
If the objective reports a defect, reproduce it first at the layer where it lives: the command, request, or tap sequence, and what was observed.
|
|
21984
|
+
Do not write a fix before you have seen the defect. Turn the reproduction into the regression test.
|
|
21985
|
+
Before opening the pull request, produce Y against the built change: run the app or affected service from your branch and perform X.
|
|
21986
|
+
If no interactive surface is reachable, run the narrowest test or script that exercises the exact user path and prints the observable Y.
|
|
21987
|
+
A unit test of an inner function, a log line, or reading the code is not a demonstration.
|
|
21988
|
+
The pull request body MUST contain two sections with exactly these headings: ## Reproduced and ## Demonstrated.
|
|
21989
|
+
Under ## Reproduced, give the steps or command and what was observed; write "not a defect report" for feature work.
|
|
21990
|
+
Under ## Demonstrated, give the command or steps that produced Y and what was observed.
|
|
21991
|
+
A pull request without both sections is not deliverable and the Room's reviewer will fail it.
|
|
21992
|
+
Change only what the objective asks. No unrequested features, flags, compatibility shims, or refactors.`;
|
|
21993
|
+
var CORNER_DELIVERY_NUDGE = "Before ending this turn, inspect the repository state and finish delivering the work: commit and push the intended changes and open the pull request if one does not exist. Decide yourself whether any remaining dirty work belongs to the objective; do not discard it merely to make the worktree clean. The pull request body must carry ## Reproduced and ## Demonstrated; if they are missing, add them before ending the turn.";
|
|
21796
21994
|
var CORNER_YOLO_MERGE_NUDGE = 'Yolo is on. Check the server merge gate with pr_checks_status now and, if checks="passed", held=false, and approvalPending=false, merge this pull request with gh. Otherwise stop without merging.';
|
|
21797
21995
|
function isCornerChecksTurn(trigger, restates) {
|
|
21798
21996
|
return Boolean(restates) || /\b(?:passed|failed) a check\b/i.test(trigger);
|
|
@@ -21977,6 +22175,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
21977
22175
|
pinnedProviderOverride;
|
|
21978
22176
|
/** The merge authority baked into the current session. */
|
|
21979
22177
|
yoloMode = false;
|
|
22178
|
+
/** The live parent-Room reviewer baked into the current session. */
|
|
22179
|
+
reviewerHandle;
|
|
22180
|
+
/** The role-specific second-chance instruction for this session. */
|
|
22181
|
+
cornerTurnEndNudge = CORNER_DELIVERY_NUDGE;
|
|
21980
22182
|
/** Repository state already given a delivery reminder, until that state changes. */
|
|
21981
22183
|
lastDeliveryNudgeState;
|
|
21982
22184
|
turnIdentityInstructions = "";
|
|
@@ -22094,7 +22296,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
22094
22296
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
22095
22297
|
soul: configuration.soul ?? self?.soul,
|
|
22096
22298
|
agentName: self?.name ?? this.agent.name,
|
|
22097
|
-
yoloMode: configuration.yoloMode
|
|
22299
|
+
yoloMode: configuration.yoloMode,
|
|
22300
|
+
reviewerHandle: configuration.reviewerHandle
|
|
22098
22301
|
});
|
|
22099
22302
|
}
|
|
22100
22303
|
async activate(trace) {
|
|
@@ -22114,9 +22317,30 @@ var MonolithCornerTurnLoop = class {
|
|
|
22114
22317
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
22115
22318
|
soul: configuration.soul ?? self?.soul,
|
|
22116
22319
|
agentName: self?.name ?? this.agent.name,
|
|
22117
|
-
yoloMode: configuration.yoloMode
|
|
22320
|
+
yoloMode: configuration.yoloMode,
|
|
22321
|
+
reviewerHandle: configuration.reviewerHandle
|
|
22118
22322
|
});
|
|
22119
22323
|
this.yoloMode = configuration.yoloMode;
|
|
22324
|
+
this.reviewerHandle = configuration.reviewerHandle;
|
|
22325
|
+
const opener = this.options.openedBy ? roster.members.find((member) => member.identityId === this.options.openedBy) : void 0;
|
|
22326
|
+
const reviewerInput = {
|
|
22327
|
+
reviewerHandle: configuration.reviewerHandle,
|
|
22328
|
+
agentHandle: self?.handle,
|
|
22329
|
+
authorHandle: opener?.handle,
|
|
22330
|
+
openedByAgent: !this.options.openedBy || this.options.openedBy === this.agent.publicKey,
|
|
22331
|
+
yoloMode: configuration.yoloMode
|
|
22332
|
+
};
|
|
22333
|
+
let reviewerInstruction = cornerReviewerInstruction(reviewerInput);
|
|
22334
|
+
if (reviewerInstruction) {
|
|
22335
|
+
const restore = await this.options.api.execute("getCornerRestoreState", {
|
|
22336
|
+
cornerId: this.options.cornerId
|
|
22337
|
+
});
|
|
22338
|
+
reviewerInstruction = cornerReviewerInstruction({
|
|
22339
|
+
...reviewerInput,
|
|
22340
|
+
pullRequestNumber: restore.lifecycle?.pr?.number
|
|
22341
|
+
});
|
|
22342
|
+
}
|
|
22343
|
+
this.cornerTurnEndNudge = reviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
|
|
22120
22344
|
await mkdir12(this.options.worktreePath, { recursive: true });
|
|
22121
22345
|
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
22122
22346
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
@@ -22260,8 +22484,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
22260
22484
|
...repository ? [
|
|
22261
22485
|
`You are in an isolated git worktree on ${repository.featureBranch}, targeting ${repository.targetBranch}.`,
|
|
22262
22486
|
`Commit and push only ${repository.featureBranch}; never force-push or write to ${repository.targetBranch}. Before pushing, rebase on origin/${repository.featureBranch}; resolve conflicts autonomously, realigning to that remote branch and redoing the objective if needed, then rerun affected tests. Open the pull request with gh.`,
|
|
22263
|
-
|
|
22264
|
-
|
|
22487
|
+
...reviewerInstruction ? [reviewerInstruction] : [
|
|
22488
|
+
configuration.reviewerHandle ? `Once the pull request exists, reply with its full URL, then @${configuration.reviewerHandle} please review, and end the turn; do not check or wait for CI.` : 'Once the pull request exists, reply only with its full URL and end the turn; do not check or wait for CI. On a later checks turn, call pr_checks_status. Merge only when checks="passed", held=false, and approvalPending=false; only a later explicit human resume clears a hold.',
|
|
22489
|
+
CORNER_AUTHOR_CONTRACT,
|
|
22490
|
+
cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
|
|
22491
|
+
],
|
|
22265
22492
|
"Do not tag the user when a corner turn finishes: the server posts the merge summary card and its push already cover completion. Tag a human only mid-turn, and only when you need a decision or input.",
|
|
22266
22493
|
"Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. When approval is pending, wait for the server close request. Never merge another pull request."
|
|
22267
22494
|
] : [
|
|
@@ -22532,12 +22759,12 @@ ${trigger}`,
|
|
|
22532
22759
|
const deliveryState = !checksTurn && this.options.repository ? await cornerUndeliveredRepositoryState(this.options.worktreePath, this.options.repository.featureBranch, this.options.repository.targetBranch) : void 0;
|
|
22533
22760
|
const needsDeliveryNudge = deliveryState !== void 0 && deliveryState !== this.lastDeliveryNudgeState;
|
|
22534
22761
|
let replyBeforeNudge = "";
|
|
22535
|
-
if (!explained && (needsDeliveryNudge || checksTurn && this.yoloMode)) {
|
|
22762
|
+
if (!explained && (needsDeliveryNudge || checksTurn && (this.yoloMode || Boolean(this.reviewerHandle)))) {
|
|
22536
22763
|
if (needsDeliveryNudge)
|
|
22537
22764
|
this.lastDeliveryNudgeState = deliveryState;
|
|
22538
22765
|
replyBeforeNudge = durableReplyText(result.agentText);
|
|
22539
22766
|
await flushToolCalls(result.toolCalls, "");
|
|
22540
|
-
result = await runPrompt(checksTurn ? CORNER_YOLO_MERGE_NUDGE : CORNER_DELIVERY_NUDGE);
|
|
22767
|
+
result = await runPrompt(this.reviewerHandle ? this.cornerTurnEndNudge : checksTurn ? CORNER_YOLO_MERGE_NUDGE : CORNER_DELIVERY_NUDGE);
|
|
22541
22768
|
trace.promptSettled();
|
|
22542
22769
|
explained = await this.explainEmpty(result);
|
|
22543
22770
|
}
|
|
@@ -22589,9 +22816,10 @@ ${trigger}`,
|
|
|
22589
22816
|
requestId,
|
|
22590
22817
|
status: "failed",
|
|
22591
22818
|
generationId: this.commandContext.generationId,
|
|
22592
|
-
reason
|
|
22819
|
+
reason: reason.text,
|
|
22820
|
+
...reason.kind ? { reasonKind: reason.kind } : {}
|
|
22593
22821
|
});
|
|
22594
|
-
await trace.finish("failed", reason);
|
|
22822
|
+
await trace.finish("failed", reason.text);
|
|
22595
22823
|
throw error;
|
|
22596
22824
|
} finally {
|
|
22597
22825
|
this.busy = false;
|
|
@@ -23696,13 +23924,15 @@ var RoomRuntimeCoordinator = class {
|
|
|
23696
23924
|
const asked = [...conversation.items].reverse().find((item) => item.type === "message" && item.mentionIds.includes(this.agent.publicKey));
|
|
23697
23925
|
if (!asked)
|
|
23698
23926
|
return;
|
|
23927
|
+
const reason = distillTurnFailureReason(error);
|
|
23699
23928
|
await this.options.daemonApi.execute("postAgentTurnReceipt", {
|
|
23700
23929
|
agentId: this.agent.publicKey,
|
|
23701
23930
|
roomId: cornerId,
|
|
23702
23931
|
requestId: asked.id,
|
|
23703
23932
|
status: "failed",
|
|
23704
23933
|
generationId: `${this.agent.publicKey}:${cornerId}`,
|
|
23705
|
-
reason:
|
|
23934
|
+
reason: reason.text,
|
|
23935
|
+
...reason.kind ? { reasonKind: reason.kind } : {}
|
|
23706
23936
|
});
|
|
23707
23937
|
} catch (reportError) {
|
|
23708
23938
|
console.error(`[thin-core] corner ${cornerId} start-failure report failed:`, reportError);
|
|
@@ -26613,7 +26843,8 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26613
26843
|
agentId: runtime.agent.publicKey,
|
|
26614
26844
|
workspaceId: runtime.communityId,
|
|
26615
26845
|
runtimeDir,
|
|
26616
|
-
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {}
|
|
26846
|
+
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {},
|
|
26847
|
+
...config.modelUnavailable ? { startupUnavailable: config.modelUnavailable.unavailable.label } : {}
|
|
26617
26848
|
});
|
|
26618
26849
|
},
|
|
26619
26850
|
onProgress: async (status) => {
|