usebeeline 0.0.30 → 0.0.32
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 +140 -44
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -3193,14 +3193,6 @@ function toolCallEntries(updates) {
|
|
|
3193
3193
|
}
|
|
3194
3194
|
return [...calls.values()];
|
|
3195
3195
|
}
|
|
3196
|
-
function isMutatingPermissionRequest(request) {
|
|
3197
|
-
const tool = request.toolCall;
|
|
3198
|
-
const kind = tool?.kind?.toLowerCase();
|
|
3199
|
-
if (kind && ["edit", "execute", "delete", "move"].includes(kind))
|
|
3200
|
-
return true;
|
|
3201
|
-
const description = [tool?.title, tool?.rawInput].filter((value) => value !== void 0).map((value) => typeof value === "string" ? value : JSON.stringify(value)).join(" ").toLowerCase();
|
|
3202
|
-
return /(^|[^a-z])(str_replace|write|edit|shell|bash|execute|create|delete|remove|move|rename|patch|apply_patch)([^a-z]|$)/.test(description);
|
|
3203
|
-
}
|
|
3204
3196
|
var AcpClient = class extends EventEmitter {
|
|
3205
3197
|
child = null;
|
|
3206
3198
|
buf = "";
|
|
@@ -3227,6 +3219,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
3227
3219
|
inheritProcessEnv;
|
|
3228
3220
|
autoApprove;
|
|
3229
3221
|
permissionHandler;
|
|
3222
|
+
permissionAllowlist;
|
|
3230
3223
|
constructor(opts) {
|
|
3231
3224
|
super();
|
|
3232
3225
|
const command = opts.agentCommand ?? opts.agentBinary;
|
|
@@ -3241,6 +3234,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
3241
3234
|
this.inheritProcessEnv = opts.inheritProcessEnv ?? process.env.BUZZY_BODY_AGENT_ENV_INHERIT === "1";
|
|
3242
3235
|
this.autoApprove = opts.autoApprovePermissions ?? true;
|
|
3243
3236
|
this.permissionHandler = opts.permissionHandler;
|
|
3237
|
+
this.permissionAllowlist = opts.permissionAllowlist;
|
|
3244
3238
|
}
|
|
3245
3239
|
async start(timeoutMs = 6e4) {
|
|
3246
3240
|
if (this.alive)
|
|
@@ -3679,7 +3673,14 @@ var AcpClient = class extends EventEmitter {
|
|
|
3679
3673
|
if (tracked)
|
|
3680
3674
|
p.toolCall = { ...tracked, ...p.toolCall };
|
|
3681
3675
|
let decision = this.autoApprove ? "allow" : "reject";
|
|
3682
|
-
if (this.
|
|
3676
|
+
if (!this.autoApprove && this.permissionAllowlist) {
|
|
3677
|
+
try {
|
|
3678
|
+
decision = this.permissionAllowlist(p) ? "allow" : "reject";
|
|
3679
|
+
} catch (error) {
|
|
3680
|
+
this.emit("permission/error", error);
|
|
3681
|
+
decision = "reject";
|
|
3682
|
+
}
|
|
3683
|
+
} else if (this.permissionHandler) {
|
|
3683
3684
|
try {
|
|
3684
3685
|
decision = await this.permissionHandler(p);
|
|
3685
3686
|
} catch (error) {
|
|
@@ -4170,15 +4171,22 @@ var BEELINE_ROOM_CAPABILITIES = [
|
|
|
4170
4171
|
"The repository filesystem is read-only in this Room session.",
|
|
4171
4172
|
"You may address any Room member, including another agent, by writing @name in your reply; the server routes that mention to them.",
|
|
4172
4173
|
"Tag another agent only when you need something from them: a question, a handoff, a task. Never tag to acknowledge, agree, or say you are ready. If nothing is actionable, do not reply.",
|
|
4173
|
-
"
|
|
4174
|
+
"Tag the user only when you need a decision or input, or when the task they asked for is finished. Never tag for progress, acknowledgement, or questions the transcript already answers.",
|
|
4175
|
+
"The beeline-readonly-mcp inspection tools and beeline-agent Room action tools are mounted. You can use beeline-readonly-mcp to search and read the bound repository without opening a corner; beeline-agent provides the host-governed Room actions.",
|
|
4176
|
+
"To send a file, call beeline-agent attach_file with a path inside your checkout; it is attached to your reply.",
|
|
4174
4177
|
"When repository work is needed, you MUST call beeline-agent open_corner with a one-paragraph summary of the complete objective. The host-governed call is the only way to start write work.",
|
|
4175
4178
|
"Never claim an action or reply happened unless the prompt or a tool result proves it."
|
|
4176
4179
|
].join(" ");
|
|
4177
|
-
|
|
4178
|
-
|
|
4180
|
+
function beelinePrimer(repository) {
|
|
4181
|
+
const repositoryLine = repository ? ` This Room is bound to ${repository.name} (branch ${repository.branch}); you have a read-only checkout at the session root.` : "";
|
|
4182
|
+
return `Consult the release-versioned using-beeline skill (SKILL.md) when you need the managed Room mechanics. ${BEELINE_ROOM_CAPABILITIES}${repositoryLine}`;
|
|
4183
|
+
}
|
|
4184
|
+
var BEELINE_CAPABILITIES_PRIMER = beelinePrimer();
|
|
4185
|
+
function beelineCapabilityContextForHarness(agentCommand, repository) {
|
|
4186
|
+
const primer = beelinePrimer(repository);
|
|
4179
4187
|
return {
|
|
4180
|
-
sessionPrompt:
|
|
4181
|
-
...harnessHonorsSessionSystemPrompt(agentCommand) ? {} : { compatibilityTurnPrefix:
|
|
4188
|
+
sessionPrompt: primer,
|
|
4189
|
+
...harnessHonorsSessionSystemPrompt(agentCommand) ? {} : { compatibilityTurnPrefix: primer }
|
|
4182
4190
|
};
|
|
4183
4191
|
}
|
|
4184
4192
|
function runningBeelineReleaseId(env = process.env, read = (path) => readFileSync3(path, "utf8")) {
|
|
@@ -4781,6 +4789,7 @@ var READ_ONLY_PERMISSION_TITLES = new Set(READ_ONLY_TOOL_NAMES.flatMap((tool) =>
|
|
|
4781
4789
|
`${READ_ONLY_MCP_SERVER_NAME}/${tool}`
|
|
4782
4790
|
]));
|
|
4783
4791
|
var READ_ONLY_TOOL_SET = new Set(READ_ONLY_TOOL_NAMES);
|
|
4792
|
+
var TOOL_NAME_SEPARATORS = ["__", ".", "/", ":", " "];
|
|
4784
4793
|
var READ_ONLY_SERVER_TITLE_PREFIXES = [
|
|
4785
4794
|
`mcp__${READ_ONLY_MCP_SERVER_NAME}__`,
|
|
4786
4795
|
`mcp__${READ_ONLY_MCP_TOOL_SERVER_NAME}__`,
|
|
@@ -4798,27 +4807,46 @@ function shellPayload(toolCall) {
|
|
|
4798
4807
|
const record2 = rawInput;
|
|
4799
4808
|
return typeof record2.command === "string" || typeof record2.cmd === "string";
|
|
4800
4809
|
}
|
|
4810
|
+
function isReadOnlyMcpPermissionRequest(request) {
|
|
4811
|
+
const toolCall = request.toolCall;
|
|
4812
|
+
const title = toolCall?.title?.trim() ?? "";
|
|
4813
|
+
const rawInput = toolCall?.rawInput;
|
|
4814
|
+
const mcpCall = Boolean(rawInput) && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput : void 0;
|
|
4815
|
+
if (mcpCall?.server === READ_ONLY_MCP_SERVER_NAME && typeof mcpCall.tool === "string" && READ_ONLY_TOOL_SET.has(mcpCall.tool)) {
|
|
4816
|
+
return true;
|
|
4817
|
+
}
|
|
4818
|
+
if (shellPayload(toolCall))
|
|
4819
|
+
return false;
|
|
4820
|
+
if (READ_ONLY_PERMISSION_TITLES.has(title))
|
|
4821
|
+
return true;
|
|
4822
|
+
if (READ_ONLY_SERVER_TITLE_PREFIXES.some((prefix) => title.startsWith(prefix))) {
|
|
4823
|
+
for (const tool of READ_ONLY_TOOL_NAMES) {
|
|
4824
|
+
if (title === tool || title.endsWith(`(${tool})`) || TOOL_NAME_SEPARATORS.some((separator) => title.endsWith(`${separator}${tool}`))) {
|
|
4825
|
+
return true;
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
return false;
|
|
4830
|
+
}
|
|
4831
|
+
var AGENT_SURFACE_TOOL_NAMES = ["open_corner", "pr_checks_status", "attach_file"];
|
|
4801
4832
|
function isBeelineAgentMcpPermissionRequest(request) {
|
|
4802
4833
|
const toolCall = request.toolCall;
|
|
4803
4834
|
const title = toolCall?.title?.trim() ?? "";
|
|
4804
4835
|
const rawInput = toolCall?.rawInput;
|
|
4805
4836
|
if (rawInput && typeof rawInput === "object" && !Array.isArray(rawInput)) {
|
|
4806
4837
|
const call = rawInput;
|
|
4807
|
-
if (call.server === BEELINE_AGENT_MCP_SERVER_NAME &&
|
|
4838
|
+
if (call.server === BEELINE_AGENT_MCP_SERVER_NAME && typeof call.tool === "string" && AGENT_SURFACE_TOOL_NAMES.includes(call.tool)) {
|
|
4808
4839
|
return true;
|
|
4809
4840
|
}
|
|
4810
4841
|
}
|
|
4811
4842
|
if (shellPayload(toolCall))
|
|
4812
4843
|
return false;
|
|
4813
4844
|
const normalized = BEELINE_AGENT_MCP_SERVER_NAME.replaceAll("-", "_");
|
|
4814
|
-
return [
|
|
4815
|
-
`mcp__${BEELINE_AGENT_MCP_SERVER_NAME}
|
|
4816
|
-
`mcp__${normalized}
|
|
4817
|
-
`mcp.${BEELINE_AGENT_MCP_SERVER_NAME}
|
|
4818
|
-
|
|
4819
|
-
`mcp__${normalized}__pr_checks_status`,
|
|
4820
|
-
`mcp.${BEELINE_AGENT_MCP_SERVER_NAME}.pr_checks_status`
|
|
4821
|
-
].includes(title);
|
|
4845
|
+
return AGENT_SURFACE_TOOL_NAMES.flatMap((tool) => [
|
|
4846
|
+
`mcp__${BEELINE_AGENT_MCP_SERVER_NAME}__${tool}`,
|
|
4847
|
+
`mcp__${normalized}__${tool}`,
|
|
4848
|
+
`mcp.${BEELINE_AGENT_MCP_SERVER_NAME}.${tool}`
|
|
4849
|
+
]).includes(title);
|
|
4822
4850
|
}
|
|
4823
4851
|
|
|
4824
4852
|
// apps/body/dist/room-session.js
|
|
@@ -4841,7 +4869,8 @@ function beelineAgentMcpServer(config, api, context) {
|
|
|
4841
4869
|
{ name: "BEELINE_DAEMON_AGENT_ID", value: connection.agentId },
|
|
4842
4870
|
{ name: "BEELINE_DAEMON_ROOM_ID", value: context.roomId },
|
|
4843
4871
|
{ name: "BEELINE_DAEMON_WORKSPACE_ID", value: context.workspaceId },
|
|
4844
|
-
...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : []
|
|
4872
|
+
...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : [],
|
|
4873
|
+
...context.attachRoot ? [{ name: "BEELINE_ATTACH_ROOT", value: context.attachRoot }] : []
|
|
4845
4874
|
]
|
|
4846
4875
|
};
|
|
4847
4876
|
}
|
|
@@ -13997,6 +14026,10 @@ async function cornerToolActivity(call, worktreePath) {
|
|
|
13997
14026
|
...paths.length ? { files: paths.map((path) => ({ path })) } : {}
|
|
13998
14027
|
};
|
|
13999
14028
|
}
|
|
14029
|
+
var CORNER_CLOSE_POLL_BASE_MS = 12e3;
|
|
14030
|
+
function cornerClosePollMs(random = Math.random) {
|
|
14031
|
+
return CORNER_CLOSE_POLL_BASE_MS + Math.floor(random() * 3e3);
|
|
14032
|
+
}
|
|
14000
14033
|
var MonolithCornerTurnLoop = class {
|
|
14001
14034
|
options;
|
|
14002
14035
|
agent;
|
|
@@ -14110,7 +14143,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
14110
14143
|
beelineAgentMcpServer(this.options.config, this.options.api, {
|
|
14111
14144
|
roomId: this.options.parentRoomId,
|
|
14112
14145
|
workspaceId: this.options.workspaceId,
|
|
14113
|
-
cornerId: this.options.cornerId
|
|
14146
|
+
cornerId: this.options.cornerId,
|
|
14147
|
+
attachRoot: this.options.worktreePath
|
|
14114
14148
|
})
|
|
14115
14149
|
];
|
|
14116
14150
|
const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
|
|
@@ -14127,6 +14161,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
14127
14161
|
"PR-opening turn rule: as soon as a pull request exists, print its full GitHub URL as your final response and end the turn immediately. Do not call pr_checks_status in that same turn and do not wait for checks inside it. Then stay idle until a later corner fact or human message starts another turn.",
|
|
14128
14162
|
'Never merge because local tests pass or because gh reports passing checks. On a later turn triggered by a server-posted checks-passed note, call beeline-agent pr_checks_status. Merge only when it returns checks="passed", held=false, and approvalPending=false.',
|
|
14129
14163
|
"If any human in this corner says hold or do not merge, do not merge until a later human explicitly resumes it.",
|
|
14164
|
+
"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.",
|
|
14130
14165
|
"A human approval in the app asks the server to merge. When approval is pending, wait for the server close request instead of racing it with gh. If checks passed, no hold exists, and no approval is pending, merge the pull request yourself with gh.",
|
|
14131
14166
|
"Never push directly to the target branch. Never merge a different pull request."
|
|
14132
14167
|
].filter(Boolean).join("\n\n")
|
|
@@ -14180,7 +14215,7 @@ ${trigger}`,
|
|
|
14180
14215
|
"Continue the objective. Obey the PR checks and human hold rules in your session instructions."
|
|
14181
14216
|
].filter(Boolean).join("\n\n");
|
|
14182
14217
|
const sessionId = this.sessionId;
|
|
14183
|
-
const turnId =
|
|
14218
|
+
const turnId = requestId;
|
|
14184
14219
|
const publishedToolCalls = /* @__PURE__ */ new Set();
|
|
14185
14220
|
const publishToolCalls = (calls, settledOnly) => {
|
|
14186
14221
|
calls.forEach((call, index) => {
|
|
@@ -14270,6 +14305,7 @@ ${pullRequest}`,
|
|
|
14270
14305
|
await this.prompt(history.items.find((item) => item.requestId)?.requestId ?? cornerId.replaceAll("-", ""), this.options.objective);
|
|
14271
14306
|
}
|
|
14272
14307
|
try {
|
|
14308
|
+
let pollWithoutWait = false;
|
|
14273
14309
|
while (!signal?.aborted) {
|
|
14274
14310
|
try {
|
|
14275
14311
|
const inbox = await api.execute("getCornerCloseRequests", {
|
|
@@ -14291,14 +14327,18 @@ ${pullRequest}`,
|
|
|
14291
14327
|
if (!authority.member || authority.principalKind !== "human")
|
|
14292
14328
|
continue;
|
|
14293
14329
|
await this.prompt(item.id, item.body);
|
|
14330
|
+
pollWithoutWait = true;
|
|
14294
14331
|
continue;
|
|
14295
14332
|
}
|
|
14296
|
-
if (/\bchecks?\b/i.test(item.body))
|
|
14333
|
+
if (/\bchecks?\b/i.test(item.body)) {
|
|
14297
14334
|
await this.prompt(item.id, item.body);
|
|
14335
|
+
pollWithoutWait = true;
|
|
14336
|
+
}
|
|
14298
14337
|
}
|
|
14299
14338
|
cursor3 = inbox.cursor ?? cursor3;
|
|
14300
14339
|
this.options.onPoll();
|
|
14301
|
-
await wait(this.options.pollMs ??
|
|
14340
|
+
await wait(pollWithoutWait ? 0 : this.options.pollMs ?? cornerClosePollMs(), signal);
|
|
14341
|
+
pollWithoutWait = false;
|
|
14302
14342
|
} catch (error) {
|
|
14303
14343
|
if (signal?.aborted)
|
|
14304
14344
|
break;
|
|
@@ -14329,6 +14369,9 @@ async function wait(ms, signal) {
|
|
|
14329
14369
|
// apps/body/dist/monolith-room-turn.js
|
|
14330
14370
|
import { mkdir as mkdir4 } from "node:fs/promises";
|
|
14331
14371
|
import { homedir as homedir5 } from "node:os";
|
|
14372
|
+
function isRoomMcpPermissionRequest(request) {
|
|
14373
|
+
return isReadOnlyMcpPermissionRequest(request) || isBeelineAgentMcpPermissionRequest(request);
|
|
14374
|
+
}
|
|
14332
14375
|
function roomPrincipalMayAddressAgent(authority, humanPermitted) {
|
|
14333
14376
|
return authority.member && (authority.principalKind === "agent" || authority.principalKind === "human" && humanPermitted);
|
|
14334
14377
|
}
|
|
@@ -14338,7 +14381,7 @@ function escapeRegExp(value) {
|
|
|
14338
14381
|
function agentReplyMentionIds(text2, roster, authorId) {
|
|
14339
14382
|
const aliases = /* @__PURE__ */ new Map();
|
|
14340
14383
|
for (const member of roster.members) {
|
|
14341
|
-
if (member.
|
|
14384
|
+
if (member.identityId === authorId)
|
|
14342
14385
|
continue;
|
|
14343
14386
|
for (const raw of [member.name, member.handle, member.soul?.name]) {
|
|
14344
14387
|
const display = raw?.trim().replace(/^@/, "");
|
|
@@ -14402,12 +14445,13 @@ var MonolithRoomTurnLoop = class {
|
|
|
14402
14445
|
async activate() {
|
|
14403
14446
|
if (this.client?.isAlive && this.sessionId)
|
|
14404
14447
|
return this.sessionId;
|
|
14405
|
-
const [configuration, roster] = await Promise.all([
|
|
14448
|
+
const [configuration, roster, repositoryState] = await Promise.all([
|
|
14406
14449
|
this.options.api.execute("getAgentConfiguration", {
|
|
14407
14450
|
agentId: this.agent.publicKey,
|
|
14408
14451
|
roomId: this.options.roomId
|
|
14409
14452
|
}),
|
|
14410
|
-
this.roster()
|
|
14453
|
+
this.roster(),
|
|
14454
|
+
this.options.api.execute("getRoomRepositoryState", { roomId: this.options.roomId })
|
|
14411
14455
|
]);
|
|
14412
14456
|
await mkdir4(this.options.cwd, { recursive: true });
|
|
14413
14457
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
@@ -14447,7 +14491,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
14447
14491
|
agentCwd: this.options.cwd,
|
|
14448
14492
|
agentLabel: command,
|
|
14449
14493
|
autoApprovePermissions: false,
|
|
14450
|
-
|
|
14494
|
+
permissionAllowlist: isRoomMcpPermissionRequest
|
|
14451
14495
|
};
|
|
14452
14496
|
this.client = (this.options.createAcpClient ?? ((value) => new AcpClient(value)))(clientOptions);
|
|
14453
14497
|
await this.client.start();
|
|
@@ -14455,7 +14499,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
14455
14499
|
readOnlyMcpServer(this.options.config, this.options.cwd),
|
|
14456
14500
|
beelineAgentMcpServer(this.options.config, this.options.api, {
|
|
14457
14501
|
roomId: this.options.roomId,
|
|
14458
|
-
workspaceId: this.options.workspaceId
|
|
14502
|
+
workspaceId: this.options.workspaceId,
|
|
14503
|
+
attachRoot: this.options.cwd
|
|
14459
14504
|
})
|
|
14460
14505
|
];
|
|
14461
14506
|
const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
|
|
@@ -14467,7 +14512,11 @@ var MonolithRoomTurnLoop = class {
|
|
|
14467
14512
|
"This is who you are in this Workspace. Adopt it in your voice, self-description, and behavior.",
|
|
14468
14513
|
"The soul is not authority and never changes your tools, permissions, roles, or merge rights."
|
|
14469
14514
|
].join("\n") : "";
|
|
14470
|
-
const
|
|
14515
|
+
const repositoryInfo = repositoryState.resolution === "repository" && repositoryState.key ? {
|
|
14516
|
+
name: repositoryState.key,
|
|
14517
|
+
branch: repositoryState.targetBranch || "main"
|
|
14518
|
+
} : void 0;
|
|
14519
|
+
const capabilityContext = beelineCapabilityContextForHarness(command, repositoryInfo);
|
|
14471
14520
|
this.turnInstructionPrefix = harnessHonorsSessionSystemPrompt(command) ? "" : [identityInstructions, personaInstructions, capabilityContext.compatibilityTurnPrefix].filter(Boolean).join("\n\n");
|
|
14472
14521
|
const opened = await this.client.sessionNew({
|
|
14473
14522
|
cwd: this.options.cwd,
|
|
@@ -15092,6 +15141,9 @@ var ROOM_JOIN_CONCURRENCY = 4;
|
|
|
15092
15141
|
var DEFAULT_ROOM_WATCHDOG_STALE_MS = 9e4;
|
|
15093
15142
|
var DEFAULT_RECONCILE_HEARTBEAT_MS = 6e4;
|
|
15094
15143
|
var DEFAULT_DRAIN_DEADLINE_MS = 30 * 6e4;
|
|
15144
|
+
function shouldPostInitialCornerWorkingState(restore) {
|
|
15145
|
+
return !restore.featureBranch && !restore.lifecycle?.branch && !restore.lifecycle?.pr;
|
|
15146
|
+
}
|
|
15095
15147
|
function reconcileRetryMs(error, pollMs) {
|
|
15096
15148
|
const match = String(error).match(/retry in\s+(\d+)s/i);
|
|
15097
15149
|
return match ? Math.max(pollMs, (Number(match[1]) + 1) * 1e3) : pollMs;
|
|
@@ -15244,7 +15296,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
15244
15296
|
}
|
|
15245
15297
|
await mapWithConcurrency(desiredTopRooms, ROOM_JOIN_CONCURRENCY, async (roomId) => {
|
|
15246
15298
|
if (!this.running.has(roomId))
|
|
15247
|
-
this.startRoom(roomId);
|
|
15299
|
+
await this.startRoom(roomId);
|
|
15248
15300
|
});
|
|
15249
15301
|
await mapWithConcurrency([...desiredCorners.values()], ROOM_JOIN_CONCURRENCY, async (corner) => {
|
|
15250
15302
|
if (!this.running.has(corner.cornerId))
|
|
@@ -15284,10 +15336,9 @@ var RoomRuntimeCoordinator = class {
|
|
|
15284
15336
|
...agentHomeRoot ? { agentHomeRoot } : {}
|
|
15285
15337
|
};
|
|
15286
15338
|
}
|
|
15287
|
-
startRoom(roomId) {
|
|
15339
|
+
async startRoom(roomId) {
|
|
15288
15340
|
const controller = new AbortController();
|
|
15289
|
-
const
|
|
15290
|
-
const cwd = record2?.repo.root ?? this.roomRoot(roomId);
|
|
15341
|
+
const cwd = await this.materializeRoomCheckout(roomId);
|
|
15291
15342
|
const startedAt = this.now();
|
|
15292
15343
|
const loop = new MonolithRoomTurnLoop({
|
|
15293
15344
|
roomId,
|
|
@@ -15324,6 +15375,44 @@ var RoomRuntimeCoordinator = class {
|
|
|
15324
15375
|
});
|
|
15325
15376
|
console.log(`[thin-core] serving monolith Room ${roomId}`);
|
|
15326
15377
|
}
|
|
15378
|
+
/**
|
|
15379
|
+
* A Room is a repository inspection surface, so its session cwd must be the
|
|
15380
|
+
* current server-bound repository rather than a legacy runtime path (or the
|
|
15381
|
+
* otherwise-empty per-Room state directory). The daemon consumes the
|
|
15382
|
+
* short-lived GitHub token itself; it is never included in the Room MCP or
|
|
15383
|
+
* harness environment.
|
|
15384
|
+
*/
|
|
15385
|
+
async materializeRoomCheckout(roomId) {
|
|
15386
|
+
const repository = await this.options.daemonApi.execute("getRoomRepositoryState", { roomId });
|
|
15387
|
+
if (repository.resolution !== "repository" || !repository.remote)
|
|
15388
|
+
return this.roomRoot(roomId);
|
|
15389
|
+
const remote = roomCheckoutRemote(repository.remote);
|
|
15390
|
+
const targetBranch = repository.targetBranch || "main";
|
|
15391
|
+
const checkoutId = createHash("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
15392
|
+
const path = resolve10(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
15393
|
+
await mkdir5(dirname4(path), { recursive: true, mode: 448 });
|
|
15394
|
+
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
15395
|
+
const env = token ? githubGitEnv(token.token) : process.env;
|
|
15396
|
+
if (!existsSync4(resolve10(path, ".git"))) {
|
|
15397
|
+
await execFileAsync3("git", ["clone", "--no-checkout", remote, path], {
|
|
15398
|
+
env,
|
|
15399
|
+
maxBuffer: 4 * 1024 * 1024
|
|
15400
|
+
});
|
|
15401
|
+
}
|
|
15402
|
+
await execFileAsync3("git", [
|
|
15403
|
+
"-C",
|
|
15404
|
+
path,
|
|
15405
|
+
"fetch",
|
|
15406
|
+
"--prune",
|
|
15407
|
+
"origin",
|
|
15408
|
+
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`
|
|
15409
|
+
], { env, maxBuffer: 4 * 1024 * 1024 });
|
|
15410
|
+
await execFileAsync3("git", ["-C", path, "checkout", "--detach", "--force", `origin/${targetBranch}`], {
|
|
15411
|
+
env,
|
|
15412
|
+
maxBuffer: 4 * 1024 * 1024
|
|
15413
|
+
});
|
|
15414
|
+
return path;
|
|
15415
|
+
}
|
|
15327
15416
|
async startCorner(corner) {
|
|
15328
15417
|
if (this.running.has(corner.cornerId) || this.startingCorners.has(corner.cornerId))
|
|
15329
15418
|
return;
|
|
@@ -15357,12 +15446,14 @@ var RoomRuntimeCoordinator = class {
|
|
|
15357
15446
|
featureBranch,
|
|
15358
15447
|
token: granted.token
|
|
15359
15448
|
});
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15449
|
+
if (shouldPostInitialCornerWorkingState(restore)) {
|
|
15450
|
+
await this.options.daemonApi.execute("postCornerRemoteState", {
|
|
15451
|
+
cornerId: corner.cornerId,
|
|
15452
|
+
branch: featureBranch,
|
|
15453
|
+
state: "working",
|
|
15454
|
+
checks: "unknown"
|
|
15455
|
+
});
|
|
15456
|
+
}
|
|
15366
15457
|
const controller = new AbortController();
|
|
15367
15458
|
const startedAt = this.now();
|
|
15368
15459
|
const loop = new MonolithCornerTurnLoop({
|
|
@@ -15555,6 +15646,11 @@ function githubHttpsRemote(remote) {
|
|
|
15555
15646
|
url.password = "";
|
|
15556
15647
|
return url.toString().replace(/\/$/, "").replace(/\.git$/i, "") + ".git";
|
|
15557
15648
|
}
|
|
15649
|
+
function roomCheckoutRemote(remote) {
|
|
15650
|
+
if (remote.startsWith("file://"))
|
|
15651
|
+
return remote;
|
|
15652
|
+
return githubHttpsRemote(remote);
|
|
15653
|
+
}
|
|
15558
15654
|
function githubGitEnv(token) {
|
|
15559
15655
|
const authorization = Buffer.from(`x-access-token:${token}`).toString("base64");
|
|
15560
15656
|
return {
|