usebeeline 0.0.58 → 0.0.61
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 +206 -155
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -16944,8 +16944,6 @@ var DaemonApiClient = class {
|
|
|
16944
16944
|
liveReconnect;
|
|
16945
16945
|
liveReconnectDelayMs = 1e3;
|
|
16946
16946
|
liveRooms = /* @__PURE__ */ new Map();
|
|
16947
|
-
parityPending = /* @__PURE__ */ new Map();
|
|
16948
|
-
parityMisses = /* @__PURE__ */ new Map();
|
|
16949
16947
|
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
|
|
16950
16948
|
this.baseUrl = baseUrl;
|
|
16951
16949
|
this.daemonToken = daemonToken;
|
|
@@ -16957,18 +16955,21 @@ var DaemonApiClient = class {
|
|
|
16957
16955
|
connection() {
|
|
16958
16956
|
return { baseUrl: this.baseUrl, daemonToken: this.daemonToken, agentId: this.agentId };
|
|
16959
16957
|
}
|
|
16960
|
-
/** Add one Room to this agent's shared live socket.
|
|
16961
|
-
liveSubscribe(roomId, cursor3, onItems) {
|
|
16958
|
+
/** Add one Room to this agent's shared live socket. */
|
|
16959
|
+
liveSubscribe(roomId, cursor3, onItems, onState, presence) {
|
|
16962
16960
|
const existing = this.liveRooms.get(roomId);
|
|
16963
16961
|
if (existing) {
|
|
16964
16962
|
existing.cursor = cursor3 ?? existing.cursor;
|
|
16965
16963
|
existing.onItems = onItems ?? existing.onItems;
|
|
16964
|
+
existing.onState = onState ?? existing.onState;
|
|
16965
|
+
existing.presence = presence ?? existing.presence;
|
|
16966
16966
|
} else {
|
|
16967
16967
|
this.liveRooms.set(roomId, {
|
|
16968
16968
|
...cursor3 ? { cursor: cursor3 } : {},
|
|
16969
16969
|
pushedIds: /* @__PURE__ */ new Set(),
|
|
16970
|
-
|
|
16971
|
-
...
|
|
16970
|
+
...onItems ? { onItems } : {},
|
|
16971
|
+
...onState ? { onState } : {},
|
|
16972
|
+
...presence ? { presence } : {}
|
|
16972
16973
|
});
|
|
16973
16974
|
}
|
|
16974
16975
|
this.ensureLiveSocket();
|
|
@@ -16976,11 +16977,6 @@ var DaemonApiClient = class {
|
|
|
16976
16977
|
this.sendLiveSubscription(roomId);
|
|
16977
16978
|
return () => {
|
|
16978
16979
|
this.liveRooms.delete(roomId);
|
|
16979
|
-
const pending = this.parityPending.get(roomId);
|
|
16980
|
-
if (pending)
|
|
16981
|
-
for (const timer of pending.values())
|
|
16982
|
-
clearTimeout(timer);
|
|
16983
|
-
this.parityPending.delete(roomId);
|
|
16984
16980
|
if (this.liveSocket?.readyState === wrapper_default.OPEN) {
|
|
16985
16981
|
this.liveSocket.send(JSON.stringify({ type: "unsubscribe", roomId }));
|
|
16986
16982
|
}
|
|
@@ -16996,42 +16992,6 @@ var DaemonApiClient = class {
|
|
|
16996
16992
|
if (room && cursor3)
|
|
16997
16993
|
room.cursor = cursor3;
|
|
16998
16994
|
}
|
|
16999
|
-
/** Count a poll delivery only if push still has not produced its id after a grace window. */
|
|
17000
|
-
notePolled(roomId, items) {
|
|
17001
|
-
const room = this.liveRooms.get(roomId);
|
|
17002
|
-
if (!room)
|
|
17003
|
-
return;
|
|
17004
|
-
const now2 = Date.now();
|
|
17005
|
-
if (room.parityReportedAt === void 0 || now2 - room.parityReportedAt >= 6e4) {
|
|
17006
|
-
room.parityReportedAt = now2;
|
|
17007
|
-
console.info(`[thin-core] push parity room=${roomId} poll_only=${room.parityMissIds.size}`);
|
|
17008
|
-
}
|
|
17009
|
-
let pending = this.parityPending.get(roomId);
|
|
17010
|
-
if (!pending) {
|
|
17011
|
-
pending = /* @__PURE__ */ new Map();
|
|
17012
|
-
this.parityPending.set(roomId, pending);
|
|
17013
|
-
}
|
|
17014
|
-
for (const item of items) {
|
|
17015
|
-
if (room.pushedIds.has(item.id) || pending.has(item.id))
|
|
17016
|
-
continue;
|
|
17017
|
-
const timer = setTimeout(() => {
|
|
17018
|
-
pending.delete(item.id);
|
|
17019
|
-
if (room.pushedIds.has(item.id) || room.parityMissIds.has(item.id))
|
|
17020
|
-
return;
|
|
17021
|
-
room.parityMissIds.add(item.id);
|
|
17022
|
-
while (room.parityMissIds.size > 1e4)
|
|
17023
|
-
room.parityMissIds.delete(room.parityMissIds.values().next().value);
|
|
17024
|
-
const count = room.parityMissIds.size;
|
|
17025
|
-
this.parityMisses.set(roomId, count);
|
|
17026
|
-
console.warn(`[thin-core] push parity miss room=${roomId} count=${count}`);
|
|
17027
|
-
}, 5e3);
|
|
17028
|
-
timer.unref?.();
|
|
17029
|
-
pending.set(item.id, timer);
|
|
17030
|
-
}
|
|
17031
|
-
}
|
|
17032
|
-
pushParityMissCount(roomId) {
|
|
17033
|
-
return this.parityMisses.get(roomId) ?? 0;
|
|
17034
|
-
}
|
|
17035
16995
|
async execute(name, input) {
|
|
17036
16996
|
const candidate = input;
|
|
17037
16997
|
if (typeof candidate.agentId === "string" && candidate.agentId !== this.agentId) {
|
|
@@ -17069,6 +17029,14 @@ var DaemonApiClient = class {
|
|
|
17069
17029
|
if (!value || typeof value !== "object")
|
|
17070
17030
|
return;
|
|
17071
17031
|
const event = value;
|
|
17032
|
+
if (event.type === "subscribed" && typeof event.roomId === "string") {
|
|
17033
|
+
const capabilities = event.capabilities;
|
|
17034
|
+
this.liveRooms.get(event.roomId)?.onState?.(true, {
|
|
17035
|
+
pushIntake: capabilities?.pushIntake === true,
|
|
17036
|
+
connectionPresence: capabilities?.connectionPresence === true
|
|
17037
|
+
});
|
|
17038
|
+
return;
|
|
17039
|
+
}
|
|
17072
17040
|
if (event.type !== "inbox" || typeof event.roomId !== "string" || !Array.isArray(event.items))
|
|
17073
17041
|
return;
|
|
17074
17042
|
const room = this.liveRooms.get(event.roomId);
|
|
@@ -17083,14 +17051,6 @@ var DaemonApiClient = class {
|
|
|
17083
17051
|
if (!room.pushedIds.has(id))
|
|
17084
17052
|
items.push(candidate);
|
|
17085
17053
|
room.pushedIds.add(id);
|
|
17086
|
-
if (room.parityMissIds.delete(id)) {
|
|
17087
|
-
this.parityMisses.set(event.roomId, room.parityMissIds.size);
|
|
17088
|
-
console.info(`[thin-core] push parity room=${event.roomId} poll_only=${room.parityMissIds.size}`);
|
|
17089
|
-
}
|
|
17090
|
-
const pending = this.parityPending.get(event.roomId)?.get(id);
|
|
17091
|
-
if (pending)
|
|
17092
|
-
clearTimeout(pending);
|
|
17093
|
-
this.parityPending.get(event.roomId)?.delete(id);
|
|
17094
17054
|
}
|
|
17095
17055
|
}
|
|
17096
17056
|
while (room.pushedIds.size > 1e4)
|
|
@@ -17102,6 +17062,8 @@ var DaemonApiClient = class {
|
|
|
17102
17062
|
if (this.liveSocket !== socket)
|
|
17103
17063
|
return;
|
|
17104
17064
|
this.liveSocket = void 0;
|
|
17065
|
+
for (const room of this.liveRooms.values())
|
|
17066
|
+
room.onState?.(false);
|
|
17105
17067
|
if (!this.liveRooms.size)
|
|
17106
17068
|
return;
|
|
17107
17069
|
const delay = this.liveReconnectDelayMs;
|
|
@@ -17119,7 +17081,8 @@ var DaemonApiClient = class {
|
|
|
17119
17081
|
this.liveSocket.send(JSON.stringify({
|
|
17120
17082
|
type: "subscribe",
|
|
17121
17083
|
roomId,
|
|
17122
|
-
...room.cursor ? { cursor: room.cursor } : {}
|
|
17084
|
+
...room.cursor ? { cursor: room.cursor } : {},
|
|
17085
|
+
...room.presence
|
|
17123
17086
|
}));
|
|
17124
17087
|
}
|
|
17125
17088
|
};
|
|
@@ -20807,6 +20770,14 @@ function cornerClosePollMs(random = Math.random) {
|
|
|
20807
20770
|
var MonolithCornerTurnLoop = class {
|
|
20808
20771
|
options;
|
|
20809
20772
|
agent;
|
|
20773
|
+
reconciliationRequested = true;
|
|
20774
|
+
wakeIntake;
|
|
20775
|
+
/** Called by the daemon's one slow workspace reconciliation sweep. */
|
|
20776
|
+
requestReconciliation() {
|
|
20777
|
+
this.reconciliationRequested = true;
|
|
20778
|
+
this.wakeIntake?.();
|
|
20779
|
+
this.wakeIntake = void 0;
|
|
20780
|
+
}
|
|
20810
20781
|
client;
|
|
20811
20782
|
sessionId;
|
|
20812
20783
|
/** The configuration the live session baked in; a change invalidates it. */
|
|
@@ -20951,11 +20922,9 @@ var MonolithCornerTurnLoop = class {
|
|
|
20951
20922
|
})
|
|
20952
20923
|
}) : {};
|
|
20953
20924
|
const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
};
|
|
20958
|
-
if (this.options.config.runtimeConfigPath && this.options.config.agentHomeRoot) {
|
|
20925
|
+
const repository = this.options.repository;
|
|
20926
|
+
let githubEnv = repository ? { GH_TOKEN: repository.githubToken, GITHUB_TOKEN: repository.githubToken } : {};
|
|
20927
|
+
if (repository && this.options.config.runtimeConfigPath && this.options.config.agentHomeRoot) {
|
|
20959
20928
|
const gitBinary = (await execFileAsync3("which", ["git"])).stdout.trim();
|
|
20960
20929
|
const ghBinary = await execFileAsync3("which", ["gh"]).then((result) => result.stdout.trim()).catch(() => void 0);
|
|
20961
20930
|
githubEnv = await installCornerGitHubWrappers({
|
|
@@ -20994,7 +20963,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
20994
20963
|
mode: "edit",
|
|
20995
20964
|
cwd: this.options.worktreePath,
|
|
20996
20965
|
worktreePath: this.options.worktreePath,
|
|
20997
|
-
gitCommonDir:
|
|
20966
|
+
...repository ? { gitCommonDir: repository.gitCommonDir } : {},
|
|
20998
20967
|
protectedPaths: [this.options.runtime.supervisorRoot],
|
|
20999
20968
|
harnessStateDirs: stateDirs,
|
|
21000
20969
|
harnessHomeStateDirs: homeStateDirs,
|
|
@@ -21017,19 +20986,21 @@ var MonolithCornerTurnLoop = class {
|
|
|
21017
20986
|
this.client = (this.options.createAcpClient ?? ((value) => new AcpClient(value)))(clientOptions);
|
|
21018
20987
|
await this.client.start();
|
|
21019
20988
|
const servers = [
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
-
|
|
21030
|
-
|
|
21031
|
-
|
|
21032
|
-
|
|
20989
|
+
...repository ? [
|
|
20990
|
+
{
|
|
20991
|
+
name: "buzz-dev-mcp",
|
|
20992
|
+
command: this.options.config.mcpBinary,
|
|
20993
|
+
args: [],
|
|
20994
|
+
// ACP hosts launch stdio MCP servers with an explicit, sanitized env.
|
|
20995
|
+
// This token is minted for this exact corner and is also the credential
|
|
20996
|
+
// helper's password source, so its shell commands need the same scope as
|
|
20997
|
+
// the corner harness without inheriting any host credentials.
|
|
20998
|
+
env: [
|
|
20999
|
+
{ name: "GH_TOKEN", value: repository.githubToken },
|
|
21000
|
+
{ name: "GITHUB_TOKEN", value: repository.githubToken }
|
|
21001
|
+
]
|
|
21002
|
+
}
|
|
21003
|
+
] : [],
|
|
21033
21004
|
beelineAgentMcpServer(this.options.config, this.options.api, {
|
|
21034
21005
|
roomId: this.options.parentRoomId,
|
|
21035
21006
|
workspaceId: this.options.workspaceId,
|
|
@@ -21060,17 +21031,23 @@ var MonolithCornerTurnLoop = class {
|
|
|
21060
21031
|
systemPrompt: [
|
|
21061
21032
|
identityInstructions,
|
|
21062
21033
|
personaInstructions,
|
|
21063
|
-
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21073
|
-
|
|
21034
|
+
...repository ? [
|
|
21035
|
+
`You are in an isolated git worktree on ${repository.featureBranch}, targeting ${repository.targetBranch}.`,
|
|
21036
|
+
"Work normally with the full coding tools. Commit and push only this feature branch. Use gh to open its pull request.",
|
|
21037
|
+
`This corner is shared: any of its member agents may be addressed in it and work on ${repository.featureBranch}. Run git pull --rebase origin ${repository.featureBranch} before you push, and never force-push it.`,
|
|
21038
|
+
"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.",
|
|
21039
|
+
'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.',
|
|
21040
|
+
"Merge the PR yourself only after the checks-passed event shows every check green; if any check failed or is still running, say exactly which and stop - never merge red.",
|
|
21041
|
+
"If any human in this corner says hold or do not merge, do not merge until a later human explicitly resumes it.",
|
|
21042
|
+
"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.",
|
|
21043
|
+
'GitHub check and merge notes are server lines already in the corner: never restate them (no "checks passed", "CI is green", "PR ready for review"). On a checks turn, say nothing unless you act - a merge or a pushed fix - and then one short line about that.',
|
|
21044
|
+
"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.",
|
|
21045
|
+
"Never push directly to the target branch. Never merge a different pull request."
|
|
21046
|
+
] : [
|
|
21047
|
+
"This is a chat-only corner with no repository or GitHub workflow.",
|
|
21048
|
+
"Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then attach_file to send them back to the corner.",
|
|
21049
|
+
"Do not initialize a repository, create a branch, push, open a pull request, or wait for GitHub checks."
|
|
21050
|
+
]
|
|
21074
21051
|
].filter(Boolean).join("\n\n")
|
|
21075
21052
|
});
|
|
21076
21053
|
this.sessionId = opened.sessionId;
|
|
@@ -21203,7 +21180,7 @@ ${this.options.objective}`,
|
|
|
21203
21180
|
${trigger}`,
|
|
21204
21181
|
...attachmentPromptLines(attachments, delivered, this.acceptsImages())
|
|
21205
21182
|
].join("\n"),
|
|
21206
|
-
"Continue the objective. Obey the PR checks and human hold rules in your session instructions.",
|
|
21183
|
+
this.options.repository ? "Continue the objective. Obey the PR checks and human hold rules in your session instructions." : "Continue the objective. Attach completed files before calling close_corner.",
|
|
21207
21184
|
MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE
|
|
21208
21185
|
].filter(Boolean).join("\n\n");
|
|
21209
21186
|
const stream = new AgentTurnStream({
|
|
@@ -21353,10 +21330,13 @@ ${trigger}`,
|
|
|
21353
21330
|
* fails with that sentence and the server inscribes it in the corner.
|
|
21354
21331
|
*/
|
|
21355
21332
|
async syncBranch() {
|
|
21356
|
-
const
|
|
21333
|
+
const repository = this.options.repository;
|
|
21334
|
+
if (!repository)
|
|
21335
|
+
return;
|
|
21336
|
+
const token = await this.options.api.execute("getRoomGitHubToken", { roomId: this.options.parentRoomId }).then((granted) => granted.token).catch(() => repository.githubToken);
|
|
21357
21337
|
await syncCornerBranch({
|
|
21358
21338
|
worktreePath: this.options.worktreePath,
|
|
21359
|
-
featureBranch:
|
|
21339
|
+
featureBranch: repository.featureBranch,
|
|
21360
21340
|
env: { ...process.env, GH_TOKEN: token, GITHUB_TOKEN: token, GIT_TERMINAL_PROMPT: "0" }
|
|
21361
21341
|
});
|
|
21362
21342
|
}
|
|
@@ -21384,15 +21364,23 @@ ${trigger}`,
|
|
|
21384
21364
|
const processedInboxIds = /* @__PURE__ */ new Set();
|
|
21385
21365
|
const pushedInbox = [];
|
|
21386
21366
|
let pendingPushedCursor;
|
|
21387
|
-
let
|
|
21367
|
+
let liveConnected = false;
|
|
21388
21368
|
const rewindSupported = Array.isArray(activation.rewindIds);
|
|
21389
21369
|
for (const id of activation.rewindIds ?? [])
|
|
21390
21370
|
processedInboxIds.add(id);
|
|
21391
21371
|
const stopLive = api.liveSubscribe?.(cornerId, cursor3, (items, pushedCursor) => {
|
|
21392
21372
|
pushedInbox.push(...items);
|
|
21393
21373
|
pendingPushedCursor = laterInboxCursor(pendingPushedCursor, pushedCursor);
|
|
21394
|
-
|
|
21395
|
-
|
|
21374
|
+
this.wakeIntake?.();
|
|
21375
|
+
this.wakeIntake = void 0;
|
|
21376
|
+
}, (connected, capabilities) => {
|
|
21377
|
+
liveConnected = connected && capabilities?.pushIntake === true;
|
|
21378
|
+
this.wakeIntake?.();
|
|
21379
|
+
this.wakeIntake = void 0;
|
|
21380
|
+
}, {
|
|
21381
|
+
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
21382
|
+
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {},
|
|
21383
|
+
available: !this.options.config.modelUnavailable
|
|
21396
21384
|
});
|
|
21397
21385
|
const history = await api.execute("getRoomConversation", { roomId: cornerId, limit: 200 });
|
|
21398
21386
|
await this.roster().catch(() => void 0);
|
|
@@ -21407,14 +21395,14 @@ ${trigger}`,
|
|
|
21407
21395
|
let pollWithoutWait = false;
|
|
21408
21396
|
while (!signal?.aborted) {
|
|
21409
21397
|
try {
|
|
21410
|
-
const pollNow = pushedInbox.length
|
|
21398
|
+
const pollNow = pushedInbox.length > 0 || !liveConnected || this.reconciliationRequested;
|
|
21411
21399
|
const inbox = !pollNow ? { items: [], cursor: void 0, closeRequested: false } : await api.execute("getCornerCloseRequests", {
|
|
21412
21400
|
cornerId,
|
|
21413
21401
|
...cursor3 ? { after: cursor3 } : {},
|
|
21414
21402
|
...rewindSupported ? { rewind: true } : {}
|
|
21415
21403
|
});
|
|
21416
21404
|
if (pollNow) {
|
|
21417
|
-
|
|
21405
|
+
this.reconciliationRequested = false;
|
|
21418
21406
|
}
|
|
21419
21407
|
if (inbox.closeRequested) {
|
|
21420
21408
|
await this.options.onCloseRequested();
|
|
@@ -21473,10 +21461,9 @@ This answers your grant request; resume the paused work. If approved and it is a
|
|
|
21473
21461
|
if (pollNow)
|
|
21474
21462
|
this.options.onPoll();
|
|
21475
21463
|
await Promise.race([
|
|
21476
|
-
wait(pollWithoutWait ? 0 : this.options.pollMs ?? cornerClosePollMs(), signal),
|
|
21477
|
-
waitForWake(api, cornerId, signal),
|
|
21464
|
+
wait(pollWithoutWait ? 0 : liveConnected ? 2147483647 : this.options.pollMs ?? cornerClosePollMs(), signal),
|
|
21478
21465
|
pushedInbox.length ? Promise.resolve() : new Promise((resolve30) => {
|
|
21479
|
-
|
|
21466
|
+
this.wakeIntake = resolve30;
|
|
21480
21467
|
})
|
|
21481
21468
|
]);
|
|
21482
21469
|
pollWithoutWait = false;
|
|
@@ -21490,27 +21477,12 @@ This answers your grant request; resume the paused work. If approved and it is a
|
|
|
21490
21477
|
}
|
|
21491
21478
|
} finally {
|
|
21492
21479
|
stopLive?.();
|
|
21480
|
+
this.wakeIntake = void 0;
|
|
21493
21481
|
this.options.grantRunner?.unregister(cornerId);
|
|
21494
21482
|
await this.options.scheduler.suspend(cornerId);
|
|
21495
21483
|
}
|
|
21496
21484
|
}
|
|
21497
21485
|
};
|
|
21498
|
-
async function waitForWake(api, cornerId, signal) {
|
|
21499
|
-
if (signal?.aborted)
|
|
21500
|
-
return;
|
|
21501
|
-
try {
|
|
21502
|
-
await api.execute("waitForCornerWake", { cornerId });
|
|
21503
|
-
} catch {
|
|
21504
|
-
await never(signal);
|
|
21505
|
-
}
|
|
21506
|
-
}
|
|
21507
|
-
async function never(signal) {
|
|
21508
|
-
if (signal?.aborted)
|
|
21509
|
-
return;
|
|
21510
|
-
await new Promise((resolveNever) => {
|
|
21511
|
-
signal?.addEventListener("abort", () => resolveNever(), { once: true });
|
|
21512
|
-
});
|
|
21513
|
-
}
|
|
21514
21486
|
async function wait(ms, signal) {
|
|
21515
21487
|
if (signal?.aborted)
|
|
21516
21488
|
return;
|
|
@@ -21658,6 +21630,14 @@ function agentReplyMentionIds(text2, roster, authorId) {
|
|
|
21658
21630
|
var MonolithRoomTurnLoop = class {
|
|
21659
21631
|
options;
|
|
21660
21632
|
agent;
|
|
21633
|
+
reconciliationRequested = true;
|
|
21634
|
+
wakeIntake;
|
|
21635
|
+
/** Called by the daemon's one slow workspace reconciliation sweep. */
|
|
21636
|
+
requestReconciliation() {
|
|
21637
|
+
this.reconciliationRequested = true;
|
|
21638
|
+
this.wakeIntake?.();
|
|
21639
|
+
this.wakeIntake = void 0;
|
|
21640
|
+
}
|
|
21661
21641
|
client;
|
|
21662
21642
|
sessionId;
|
|
21663
21643
|
/** The configuration the live session baked in; a change invalidates it. */
|
|
@@ -22252,7 +22232,10 @@ var MonolithRoomTurnLoop = class {
|
|
|
22252
22232
|
async run() {
|
|
22253
22233
|
const { api, roomId, signal } = this.options;
|
|
22254
22234
|
const status = this.options.config.modelUnavailable ? "offline" : "online";
|
|
22255
|
-
|
|
22235
|
+
let legacyPresence = false;
|
|
22236
|
+
let legacyPresenceHeartbeat;
|
|
22237
|
+
let legacyPresenceFallback;
|
|
22238
|
+
const postLegacyPresence = async (presence) => {
|
|
22256
22239
|
await api.execute("postAgentPresence", {
|
|
22257
22240
|
agentId: this.agent.publicKey,
|
|
22258
22241
|
roomId,
|
|
@@ -22260,17 +22243,30 @@ var MonolithRoomTurnLoop = class {
|
|
|
22260
22243
|
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
22261
22244
|
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {}
|
|
22262
22245
|
});
|
|
22263
|
-
this.options.health.presence(presence);
|
|
22264
22246
|
};
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
|
|
22247
|
+
const useLegacyPresence = () => {
|
|
22248
|
+
if (legacyPresence)
|
|
22249
|
+
return;
|
|
22250
|
+
legacyPresence = true;
|
|
22251
|
+
void postLegacyPresence(status).catch((error) => console.error(`[thin-core] monolith Room ${roomId} presence fallback failed:`, error));
|
|
22252
|
+
legacyPresenceHeartbeat = setInterval(() => void postLegacyPresence(status).catch((error) => console.error(`[thin-core] monolith Room ${roomId} presence fallback failed:`, error)), 3e4);
|
|
22253
|
+
legacyPresenceHeartbeat.unref?.();
|
|
22254
|
+
};
|
|
22255
|
+
const stopLegacyPresence = () => {
|
|
22256
|
+
legacyPresence = false;
|
|
22257
|
+
clearTimeout(legacyPresenceFallback);
|
|
22258
|
+
legacyPresenceFallback = void 0;
|
|
22259
|
+
clearInterval(legacyPresenceHeartbeat);
|
|
22260
|
+
legacyPresenceHeartbeat = void 0;
|
|
22261
|
+
};
|
|
22268
22262
|
let cursor3;
|
|
22269
22263
|
const processedInboxIds = /* @__PURE__ */ new Set();
|
|
22270
22264
|
const pushedInbox = [];
|
|
22271
22265
|
let pendingPushedCursor;
|
|
22272
|
-
let
|
|
22266
|
+
let liveConnected = false;
|
|
22273
22267
|
let stopLive;
|
|
22268
|
+
legacyPresenceFallback = setTimeout(useLegacyPresence, 1e3);
|
|
22269
|
+
legacyPresenceFallback.unref?.();
|
|
22274
22270
|
try {
|
|
22275
22271
|
const activation = await api.execute("getRoomInbox", { roomId, startAtLatest: true });
|
|
22276
22272
|
cursor3 = activation.cursor;
|
|
@@ -22280,15 +22276,28 @@ var MonolithRoomTurnLoop = class {
|
|
|
22280
22276
|
stopLive = api.liveSubscribe?.(roomId, cursor3, (items, pushedCursor) => {
|
|
22281
22277
|
pushedInbox.push(...items);
|
|
22282
22278
|
pendingPushedCursor = laterInboxCursor(pendingPushedCursor, pushedCursor);
|
|
22283
|
-
|
|
22284
|
-
|
|
22279
|
+
this.wakeIntake?.();
|
|
22280
|
+
this.wakeIntake = void 0;
|
|
22281
|
+
}, (connected, capabilities) => {
|
|
22282
|
+
liveConnected = connected && capabilities?.pushIntake === true;
|
|
22283
|
+
if (connected && capabilities?.connectionPresence !== true)
|
|
22284
|
+
useLegacyPresence();
|
|
22285
|
+
else if (connected)
|
|
22286
|
+
stopLegacyPresence();
|
|
22287
|
+
this.options.health.presence(connected && !this.options.config.modelUnavailable ? "online" : "offline");
|
|
22288
|
+
this.wakeIntake?.();
|
|
22289
|
+
this.wakeIntake = void 0;
|
|
22290
|
+
}, {
|
|
22291
|
+
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
22292
|
+
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {},
|
|
22293
|
+
available: !this.options.config.modelUnavailable
|
|
22285
22294
|
});
|
|
22286
22295
|
while (!signal?.aborted) {
|
|
22287
22296
|
try {
|
|
22288
22297
|
if (!this.activeTurn && this.queuedTurns.length) {
|
|
22289
22298
|
this.startPrompt(this.queuedTurns.shift());
|
|
22290
22299
|
}
|
|
22291
|
-
const pollNow = pushedInbox.length === 0;
|
|
22300
|
+
const pollNow = pushedInbox.length === 0 && (!liveConnected || this.reconciliationRequested);
|
|
22292
22301
|
const inbox = !pollNow ? { items: [], cursor: void 0 } : await api.execute("getRoomInbox", {
|
|
22293
22302
|
roomId,
|
|
22294
22303
|
...cursor3 ? { after: cursor3 } : {},
|
|
@@ -22296,7 +22305,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
22296
22305
|
limit: 200
|
|
22297
22306
|
});
|
|
22298
22307
|
if (pollNow) {
|
|
22299
|
-
|
|
22308
|
+
this.reconciliationRequested = false;
|
|
22300
22309
|
}
|
|
22301
22310
|
const delivered = [...pushedInbox.splice(0), ...inbox.items];
|
|
22302
22311
|
for (const item of delivered) {
|
|
@@ -22331,9 +22340,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
22331
22340
|
this.options.health.poll();
|
|
22332
22341
|
if (!pushedInbox.length) {
|
|
22333
22342
|
await Promise.race([
|
|
22334
|
-
wait2(this.options.pollMs ?? 1e3, signal),
|
|
22343
|
+
wait2(liveConnected ? 2147483647 : this.options.pollMs ?? 1e3, signal),
|
|
22335
22344
|
new Promise((resolve30) => {
|
|
22336
|
-
|
|
22345
|
+
this.wakeIntake = resolve30;
|
|
22337
22346
|
})
|
|
22338
22347
|
]);
|
|
22339
22348
|
}
|
|
@@ -22347,13 +22356,17 @@ var MonolithRoomTurnLoop = class {
|
|
|
22347
22356
|
}
|
|
22348
22357
|
} finally {
|
|
22349
22358
|
stopLive?.();
|
|
22350
|
-
|
|
22359
|
+
this.wakeIntake = void 0;
|
|
22351
22360
|
this.options.grantRunner?.unregister(roomId);
|
|
22352
22361
|
if (this.activeTurn?.phase === "prompting" && this.client && this.sessionId) {
|
|
22353
22362
|
this.client.sessionCancel(this.sessionId);
|
|
22354
22363
|
}
|
|
22355
22364
|
await this.activeTurn?.promise;
|
|
22356
|
-
|
|
22365
|
+
clearTimeout(legacyPresenceFallback);
|
|
22366
|
+
clearInterval(legacyPresenceHeartbeat);
|
|
22367
|
+
if (legacyPresence) {
|
|
22368
|
+
await postLegacyPresence("offline").catch((error) => console.error(`[thin-core] monolith Room ${roomId} offline presence failed:`, error));
|
|
22369
|
+
}
|
|
22357
22370
|
await this.options.scheduler.suspend(roomId);
|
|
22358
22371
|
}
|
|
22359
22372
|
}
|
|
@@ -22844,7 +22857,14 @@ async function materializeCornerWorktree(input) {
|
|
|
22844
22857
|
"credential.https://github.com.helper",
|
|
22845
22858
|
"!f() { echo username=x-access-token; echo password=$GH_TOKEN; }; f"
|
|
22846
22859
|
]);
|
|
22847
|
-
await execFileAsync4("git", [
|
|
22860
|
+
await execFileAsync4("git", [
|
|
22861
|
+
"-C",
|
|
22862
|
+
path,
|
|
22863
|
+
"config",
|
|
22864
|
+
"--worktree",
|
|
22865
|
+
"user.name",
|
|
22866
|
+
input.committer.name
|
|
22867
|
+
]);
|
|
22848
22868
|
await execFileAsync4("git", [
|
|
22849
22869
|
"-C",
|
|
22850
22870
|
path,
|
|
@@ -22873,6 +22893,13 @@ async function mapWithConcurrency(values, limit, visit) {
|
|
|
22873
22893
|
}));
|
|
22874
22894
|
}
|
|
22875
22895
|
var execFileAsync4 = promisify4(execFile5);
|
|
22896
|
+
async function removeCornerScratchWorkspace(input) {
|
|
22897
|
+
const expected = resolve19(input.roomRoot, "scratch");
|
|
22898
|
+
if (resolve19(input.scratchPath) !== expected) {
|
|
22899
|
+
throw new Error(`refusing to remove scratch outside corner ${input.cornerId}`);
|
|
22900
|
+
}
|
|
22901
|
+
await rm4(expected, { recursive: true, force: true });
|
|
22902
|
+
}
|
|
22876
22903
|
var RoomRuntimeCoordinator = class {
|
|
22877
22904
|
configPath;
|
|
22878
22905
|
baseConfig;
|
|
@@ -23037,6 +23064,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
23037
23064
|
await running.promise.catch(() => void 0);
|
|
23038
23065
|
if (running.worktree)
|
|
23039
23066
|
await this.reapCornerWorktree(running.worktree);
|
|
23067
|
+
else if (running.scratch)
|
|
23068
|
+
await this.reapCornerScratch(running.scratch);
|
|
23040
23069
|
}
|
|
23041
23070
|
await mapWithConcurrency(desiredTopRooms, ROOM_JOIN_CONCURRENCY, async (roomId) => {
|
|
23042
23071
|
if (!this.running.has(roomId))
|
|
@@ -23046,6 +23075,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
23046
23075
|
if (!this.running.has(corner.cornerId))
|
|
23047
23076
|
await this.startCorner(corner);
|
|
23048
23077
|
});
|
|
23078
|
+
for (const running of this.running.values())
|
|
23079
|
+
running.body.requestReconciliation();
|
|
23049
23080
|
return "member";
|
|
23050
23081
|
}
|
|
23051
23082
|
roomRecord(roomId) {
|
|
@@ -23069,8 +23100,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
23069
23100
|
return void 0;
|
|
23070
23101
|
}
|
|
23071
23102
|
}
|
|
23072
|
-
roomConfig(roomId) {
|
|
23073
|
-
const workspaceRoot = this.roomRoot(roomId);
|
|
23103
|
+
roomConfig(roomId, workspaceRoot = this.roomRoot(roomId)) {
|
|
23074
23104
|
const agentHomeRoot = this.roomAgentHomeRoot(workspaceRoot, true);
|
|
23075
23105
|
return {
|
|
23076
23106
|
...this.baseConfig,
|
|
@@ -23174,7 +23204,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
23174
23204
|
return;
|
|
23175
23205
|
this.startingCorners.add(corner.cornerId);
|
|
23176
23206
|
try {
|
|
23177
|
-
const [restore, repository, conversation
|
|
23207
|
+
const [restore, repository, conversation] = await Promise.all([
|
|
23178
23208
|
this.options.daemonApi.execute("getCornerRestoreState", { cornerId: corner.cornerId }),
|
|
23179
23209
|
this.options.daemonApi.execute("getRoomRepositoryState", {
|
|
23180
23210
|
roomId: corner.parentRoomId
|
|
@@ -23186,28 +23216,35 @@ var RoomRuntimeCoordinator = class {
|
|
|
23186
23216
|
roomId: corner.cornerId,
|
|
23187
23217
|
limit: 200,
|
|
23188
23218
|
window: "earliest"
|
|
23189
|
-
}),
|
|
23190
|
-
this.options.daemonApi.execute("getRoomGitHubToken", {
|
|
23191
|
-
roomId: corner.parentRoomId
|
|
23192
23219
|
})
|
|
23193
23220
|
]);
|
|
23194
|
-
if (repository.resolution
|
|
23195
|
-
throw new Error("corner parent Room
|
|
23221
|
+
if (repository.resolution === "unverified") {
|
|
23222
|
+
throw new Error("corner parent Room repository state is not verified yet");
|
|
23223
|
+
}
|
|
23224
|
+
if (repository.resolution === "repository" && (!repository.remote || !repository.key)) {
|
|
23225
|
+
throw new Error("corner parent Room has an incomplete repository binding");
|
|
23196
23226
|
}
|
|
23197
23227
|
const objective = conversation.items.find((item) => item.type === "message")?.body.trim();
|
|
23198
23228
|
if (!objective)
|
|
23199
23229
|
throw new Error("corner has no durable objective post");
|
|
23200
|
-
const
|
|
23201
|
-
const
|
|
23202
|
-
const
|
|
23230
|
+
const repositoryBacked = repository.resolution === "repository";
|
|
23231
|
+
const targetBranch = repositoryBacked ? repository.targetBranch || "main" : void 0;
|
|
23232
|
+
const featureBranch = repositoryBacked ? restore.featureBranch ?? `feature/corner-${corner.cornerId.replaceAll("-", "").slice(0, 12)}` : void 0;
|
|
23233
|
+
const granted = repositoryBacked ? await this.options.daemonApi.execute("getRoomGitHubToken", {
|
|
23234
|
+
roomId: corner.parentRoomId
|
|
23235
|
+
}) : void 0;
|
|
23236
|
+
const worktree = repositoryBacked ? await this.materializeCornerWorktree({
|
|
23203
23237
|
cornerId: corner.cornerId,
|
|
23204
23238
|
remote: repository.remote,
|
|
23205
23239
|
targetBranch,
|
|
23206
23240
|
featureBranch,
|
|
23207
23241
|
token: granted.token
|
|
23208
|
-
});
|
|
23242
|
+
}) : void 0;
|
|
23243
|
+
const workspacePath = worktree?.path ?? resolve19(this.roomRoot(corner.cornerId), "scratch");
|
|
23244
|
+
if (!worktree)
|
|
23245
|
+
await mkdir11(workspacePath, { recursive: true, mode: 448 });
|
|
23209
23246
|
const isOpener = !corner.openedBy || corner.openedBy === this.agent.publicKey;
|
|
23210
|
-
if (shouldPostInitialCornerWorkingState(restore, isOpener)) {
|
|
23247
|
+
if (worktree && shouldPostInitialCornerWorkingState(restore, isOpener)) {
|
|
23211
23248
|
await this.options.daemonApi.execute("postCornerRemoteState", {
|
|
23212
23249
|
cornerId: corner.cornerId,
|
|
23213
23250
|
branch: featureBranch,
|
|
@@ -23226,23 +23263,27 @@ var RoomRuntimeCoordinator = class {
|
|
|
23226
23263
|
workspaceId: this.runtime.communityId,
|
|
23227
23264
|
...corner.openedBy ? { openedBy: corner.openedBy } : {},
|
|
23228
23265
|
objective,
|
|
23229
|
-
|
|
23230
|
-
|
|
23231
|
-
|
|
23232
|
-
|
|
23233
|
-
|
|
23266
|
+
worktreePath: workspacePath,
|
|
23267
|
+
...worktree ? {
|
|
23268
|
+
repository: {
|
|
23269
|
+
featureBranch,
|
|
23270
|
+
targetBranch,
|
|
23271
|
+
gitCommonDir: worktree.gitCommonDir,
|
|
23272
|
+
githubToken: granted.token
|
|
23273
|
+
}
|
|
23274
|
+
} : {},
|
|
23234
23275
|
runtime: this.runtime,
|
|
23235
|
-
config: this.roomConfig(corner.cornerId),
|
|
23276
|
+
config: this.roomConfig(corner.cornerId, worktree ? void 0 : workspacePath),
|
|
23236
23277
|
api: this.options.daemonApi,
|
|
23237
23278
|
scheduler: this.scheduler,
|
|
23238
23279
|
signal: controller.signal,
|
|
23239
23280
|
onPoll: () => this.notePoll(corner.cornerId),
|
|
23240
23281
|
onFailure: (retryInMs) => this.noteFailure(corner.cornerId, retryInMs),
|
|
23241
|
-
onCloseRequested: () => this.reapCornerWorktree({
|
|
23282
|
+
onCloseRequested: () => worktree ? this.reapCornerWorktree({
|
|
23242
23283
|
...worktree,
|
|
23243
23284
|
cornerId: corner.cornerId,
|
|
23244
23285
|
branch: featureBranch
|
|
23245
|
-
})
|
|
23286
|
+
}) : this.reapCornerScratch({ path: workspacePath, cornerId: corner.cornerId })
|
|
23246
23287
|
});
|
|
23247
23288
|
const promise = loop.run().catch((error) => {
|
|
23248
23289
|
if (!controller.signal.aborted) {
|
|
@@ -23260,14 +23301,17 @@ var RoomRuntimeCoordinator = class {
|
|
|
23260
23301
|
lastPollAt: startedAt,
|
|
23261
23302
|
backoffUntil: 0,
|
|
23262
23303
|
recovering: false,
|
|
23263
|
-
worktree
|
|
23264
|
-
|
|
23265
|
-
|
|
23266
|
-
|
|
23267
|
-
|
|
23304
|
+
...worktree ? {
|
|
23305
|
+
worktree: {
|
|
23306
|
+
...worktree,
|
|
23307
|
+
cornerId: corner.cornerId,
|
|
23308
|
+
branch: featureBranch
|
|
23309
|
+
}
|
|
23310
|
+
} : {},
|
|
23311
|
+
...!worktree ? { scratch: { path: workspacePath, cornerId: corner.cornerId } } : {}
|
|
23268
23312
|
});
|
|
23269
23313
|
this.reportedCornerStartFailures.delete(corner.cornerId);
|
|
23270
|
-
console.log(`[thin-core] serving corner ${corner.cornerId} on ${featureBranch} at ${
|
|
23314
|
+
console.log(worktree ? `[thin-core] serving corner ${corner.cornerId} on ${featureBranch} at ${workspacePath}` : `[thin-core] serving chat-only corner ${corner.cornerId} at ${workspacePath}`);
|
|
23271
23315
|
} catch (error) {
|
|
23272
23316
|
console.error(`[thin-core] failed to start corner ${corner.cornerId}:`, error);
|
|
23273
23317
|
await this.reportCornerStartFailure(corner.cornerId, error);
|
|
@@ -23334,6 +23378,13 @@ var RoomRuntimeCoordinator = class {
|
|
|
23334
23378
|
checks: "unknown"
|
|
23335
23379
|
});
|
|
23336
23380
|
}
|
|
23381
|
+
async reapCornerScratch(scratch) {
|
|
23382
|
+
await removeCornerScratchWorkspace({
|
|
23383
|
+
cornerId: scratch.cornerId,
|
|
23384
|
+
roomRoot: this.roomRoot(scratch.cornerId),
|
|
23385
|
+
scratchPath: scratch.path
|
|
23386
|
+
});
|
|
23387
|
+
}
|
|
23337
23388
|
notePoll(roomId) {
|
|
23338
23389
|
const room = this.running.get(roomId);
|
|
23339
23390
|
if (!room)
|