baxian 2.0.16 → 2.0.17
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/agent/branch.d.ts +5 -0
- package/dist/agent/branch.d.ts.map +1 -1
- package/dist/agent/branch.js +119 -29
- package/dist/agent/branch.js.map +1 -1
- package/dist/agent/manager.d.ts +2 -0
- package/dist/agent/manager.d.ts.map +1 -1
- package/dist/agent/manager.js +80 -118
- package/dist/agent/manager.js.map +1 -1
- package/package.json +1 -1
package/dist/agent/manager.js
CHANGED
|
@@ -19,7 +19,7 @@ import { createRunner, LocalRunner, shellQuote, resolveAgentHost, workdirHostGro
|
|
|
19
19
|
import { isTransientNetworkFailure } from './net-exec.js';
|
|
20
20
|
import { imageFilename, agentHostPath, writeImageToHost } from './image-input.js';
|
|
21
21
|
import { TmuxManager, ReplNotReadyError, detectStartupDialog, detectRuntimeMenu, runtimeBusyCheck, hasRuntimeReadyView, hasReplProcTitle, isShellProcTitle, hasOscTitleWorking, hasOscTitleIdle, screenAllowsTitleIdle, PaneGoneError, } from './tmux.js';
|
|
22
|
-
import { BranchManager, DirtyWorkdirError, ReviewHeadMismatchError, isAutoDeletableTaskBranch } from './branch.js';
|
|
22
|
+
import { BranchManager, DirtyWorkdirError, ReviewHeadMismatchError, isAutoDeletableTaskBranch, } from './branch.js';
|
|
23
23
|
import { RepoStore, createRepoStoreCache } from './repo-store.js';
|
|
24
24
|
import { PhaseSignalWatcher, } from './phase-signal-watcher.js';
|
|
25
25
|
import { buildPromptInline, buildGreetingPrompt, PromptSizeError, MAX_PROMPT_BYTES_ROUTE_LIMIT, } from './prompt.js';
|
|
@@ -150,6 +150,13 @@ const DEFAULT_NEED_INPUT_RETRY_INTERVAL_MS = 5_000;
|
|
|
150
150
|
const DEFAULT_DISPATCH_SETTLE_TIMEOUT_MS = 3_000;
|
|
151
151
|
const DELETE_CLEANUP_TIMEOUT_MS = 15_000;
|
|
152
152
|
const PLATFORM_HEAD_SHA_RE = new RegExp(`^${SHA_HEX_SOURCE}$`);
|
|
153
|
+
function branchCleanupIdentity(task) {
|
|
154
|
+
return {
|
|
155
|
+
taskId: task.id,
|
|
156
|
+
taskBranch: task.branch,
|
|
157
|
+
branchCreatedByBaxian: task.branchCreatedByBaxian,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
153
160
|
function createSignalTokenExcluding(...excluded) {
|
|
154
161
|
let token = createSignalToken();
|
|
155
162
|
while (excluded.includes(token))
|
|
@@ -1701,9 +1708,7 @@ export class AgentManager {
|
|
|
1701
1708
|
}
|
|
1702
1709
|
const pendingAt = new Date().toISOString();
|
|
1703
1710
|
if (boundTask && cfg.role === 'dev') {
|
|
1704
|
-
boundTask
|
|
1705
|
-
boundTask.updatedAt = pendingAt;
|
|
1706
|
-
await this.taskStore.set(boundTask);
|
|
1711
|
+
await this.persistBranchCleanupOutcome(boundTask, agentId, { status: 'pending', reason });
|
|
1707
1712
|
}
|
|
1708
1713
|
let wrote = false;
|
|
1709
1714
|
await this.agentStore.update(agentId, (latest2) => {
|
|
@@ -1769,59 +1774,8 @@ export class AgentManager {
|
|
|
1769
1774
|
}
|
|
1770
1775
|
if (cfg.role === 'dev' && boundTask) {
|
|
1771
1776
|
const branches = new BranchManager(runner);
|
|
1772
|
-
const cleanup = await branches.cleanupTaskBranch(releaseWorkdir,
|
|
1773
|
-
|
|
1774
|
-
taskBranch: boundTask.branch,
|
|
1775
|
-
branchCreatedByBaxian: boundTask.branchCreatedByBaxian,
|
|
1776
|
-
}, () => this.assertTaskGeneration(agentId, expectedTaskId, lockToken, releaseWorkdir).then(() => undefined));
|
|
1777
|
-
let cleanupStateChanged = false;
|
|
1778
|
-
if (cleanup.status === 'pending') {
|
|
1779
|
-
if (boundTask.branchCleanupPending?.agentId !== agentId
|
|
1780
|
-
|| boundTask.branchCleanupPending.reason !== cleanup.reason
|
|
1781
|
-
|| boundTask.branchCleanupSkipped !== undefined) {
|
|
1782
|
-
boundTask.branchCleanupPending = {
|
|
1783
|
-
agentId,
|
|
1784
|
-
reason: cleanup.reason,
|
|
1785
|
-
updatedAt: new Date().toISOString(),
|
|
1786
|
-
};
|
|
1787
|
-
delete boundTask.branchCleanupSkipped;
|
|
1788
|
-
cleanupStateChanged = true;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
else if (cleanup.status === 'skipped') {
|
|
1792
|
-
if (boundTask.branchCleanupSkipped?.agentId !== agentId
|
|
1793
|
-
|| boundTask.branchCleanupSkipped.reason !== cleanup.reason
|
|
1794
|
-
|| boundTask.branchCleanupPending !== undefined) {
|
|
1795
|
-
boundTask.branchCleanupSkipped = {
|
|
1796
|
-
agentId,
|
|
1797
|
-
reason: cleanup.reason,
|
|
1798
|
-
updatedAt: new Date().toISOString(),
|
|
1799
|
-
};
|
|
1800
|
-
delete boundTask.branchCleanupPending;
|
|
1801
|
-
cleanupStateChanged = true;
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
else {
|
|
1805
|
-
if (boundTask.branchCleanupPending?.agentId === agentId) {
|
|
1806
|
-
delete boundTask.branchCleanupPending;
|
|
1807
|
-
cleanupStateChanged = true;
|
|
1808
|
-
}
|
|
1809
|
-
if (boundTask.branchCleanupSkipped?.agentId === agentId) {
|
|
1810
|
-
delete boundTask.branchCleanupSkipped;
|
|
1811
|
-
cleanupStateChanged = true;
|
|
1812
|
-
}
|
|
1813
|
-
if (cleanup.remoteTipSha) {
|
|
1814
|
-
boundTask.branchLocalCleaned = {
|
|
1815
|
-
remoteTipSha: cleanup.remoteTipSha,
|
|
1816
|
-
updatedAt: new Date().toISOString(),
|
|
1817
|
-
};
|
|
1818
|
-
cleanupStateChanged = true;
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
if (cleanupStateChanged) {
|
|
1822
|
-
boundTask.updatedAt = new Date().toISOString();
|
|
1823
|
-
await this.taskStore.set(boundTask);
|
|
1824
|
-
}
|
|
1777
|
+
const cleanup = await branches.cleanupTaskBranch(releaseWorkdir, this.cleanupIdentityFor(boundTask), () => this.assertTaskGeneration(agentId, expectedTaskId, lockToken, releaseWorkdir).then(() => undefined));
|
|
1778
|
+
await this.persistBranchCleanupOutcome(boundTask, agentId, cleanup);
|
|
1825
1779
|
if (cleanup.status !== 'deleted')
|
|
1826
1780
|
await branches.parkOnDefaultDetached(releaseWorkdir);
|
|
1827
1781
|
}
|
|
@@ -1891,6 +1845,64 @@ export class AgentManager {
|
|
|
1891
1845
|
console.warn(`[AgentManager] clearing branchLocalCleaned(${taskId}) failed:`, err);
|
|
1892
1846
|
});
|
|
1893
1847
|
}
|
|
1848
|
+
cleanupIdentityFor(task) {
|
|
1849
|
+
const identity = branchCleanupIdentity(task);
|
|
1850
|
+
if (task.status === 'merged' && task.prNumber !== undefined) {
|
|
1851
|
+
identity.resolveMergedHeadSha = () => this.platformFetchPrHeadSha(task.id);
|
|
1852
|
+
}
|
|
1853
|
+
return identity;
|
|
1854
|
+
}
|
|
1855
|
+
async persistBranchCleanupOutcome(task, agentId, cleanup, guard) {
|
|
1856
|
+
await this.withTaskLock(async () => {
|
|
1857
|
+
const latest = await this.taskStore.get(task.id);
|
|
1858
|
+
if (!latest || latest.branch !== task.branch || (guard !== undefined && !guard(latest)))
|
|
1859
|
+
return;
|
|
1860
|
+
const effective = cleanup;
|
|
1861
|
+
const now = new Date().toISOString();
|
|
1862
|
+
let changed = false;
|
|
1863
|
+
if (effective.status === 'pending') {
|
|
1864
|
+
if (latest.branchCleanupPending?.agentId !== agentId
|
|
1865
|
+
|| latest.branchCleanupPending.reason !== effective.reason
|
|
1866
|
+
|| latest.branchCleanupSkipped !== undefined) {
|
|
1867
|
+
latest.branchCleanupPending = { agentId, reason: effective.reason, updatedAt: now };
|
|
1868
|
+
delete latest.branchCleanupSkipped;
|
|
1869
|
+
changed = true;
|
|
1870
|
+
}
|
|
1871
|
+
if (effective.remoteTipSha !== undefined
|
|
1872
|
+
&& latest.branchLocalCleaned?.remoteTipSha !== effective.remoteTipSha) {
|
|
1873
|
+
latest.branchLocalCleaned = { remoteTipSha: effective.remoteTipSha, updatedAt: now };
|
|
1874
|
+
changed = true;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
else if (effective.status === 'skipped') {
|
|
1878
|
+
if (latest.branchCleanupSkipped?.agentId !== agentId
|
|
1879
|
+
|| latest.branchCleanupSkipped.reason !== effective.reason
|
|
1880
|
+
|| latest.branchCleanupPending !== undefined) {
|
|
1881
|
+
latest.branchCleanupSkipped = { agentId, reason: effective.reason, updatedAt: now };
|
|
1882
|
+
delete latest.branchCleanupPending;
|
|
1883
|
+
changed = true;
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
if (latest.branchCleanupPending !== undefined) {
|
|
1888
|
+
delete latest.branchCleanupPending;
|
|
1889
|
+
changed = true;
|
|
1890
|
+
}
|
|
1891
|
+
if (latest.branchCleanupSkipped !== undefined) {
|
|
1892
|
+
delete latest.branchCleanupSkipped;
|
|
1893
|
+
changed = true;
|
|
1894
|
+
}
|
|
1895
|
+
if (effective.remoteTipSha) {
|
|
1896
|
+
latest.branchLocalCleaned = { remoteTipSha: effective.remoteTipSha, updatedAt: now };
|
|
1897
|
+
changed = true;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
if (!changed)
|
|
1901
|
+
return;
|
|
1902
|
+
latest.updatedAt = now;
|
|
1903
|
+
await this.taskStore.set(latest);
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1894
1906
|
async releaseAgentIfBound(agentId, expectedTaskId, opts) {
|
|
1895
1907
|
const state = await this.agentStore.get(agentId);
|
|
1896
1908
|
if (state?.taskId !== expectedTaskId)
|
|
@@ -1911,50 +1923,7 @@ export class AgentManager {
|
|
|
1911
1923
|
&& task.branchCleanupSkipped === undefined)
|
|
1912
1924
|
.map(task => [task.branch, task]));
|
|
1913
1925
|
const owner = 'maintenance:branch-reconcile';
|
|
1914
|
-
const persistCleanup =
|
|
1915
|
-
await this.withTaskLock(async () => {
|
|
1916
|
-
const latest = await this.taskStore.get(task.id);
|
|
1917
|
-
if (!latest
|
|
1918
|
-
|| latest.branch !== task.branch
|
|
1919
|
-
|| latest.branchCreatedByBaxian !== true
|
|
1920
|
-
|| !TERMINAL_STATUSES.includes(latest.status))
|
|
1921
|
-
return;
|
|
1922
|
-
const now = new Date().toISOString();
|
|
1923
|
-
let changed = false;
|
|
1924
|
-
if (cleanup.status === 'pending') {
|
|
1925
|
-
if (latest.branchCleanupPending?.agentId !== agentId
|
|
1926
|
-
|| latest.branchCleanupPending.reason !== cleanup.reason
|
|
1927
|
-
|| latest.branchCleanupSkipped !== undefined) {
|
|
1928
|
-
latest.branchCleanupPending = { agentId, reason: cleanup.reason, updatedAt: now };
|
|
1929
|
-
delete latest.branchCleanupSkipped;
|
|
1930
|
-
changed = true;
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
else if (cleanup.status === 'skipped') {
|
|
1934
|
-
if (latest.branchCleanupSkipped?.agentId !== agentId
|
|
1935
|
-
|| latest.branchCleanupSkipped.reason !== cleanup.reason
|
|
1936
|
-
|| latest.branchCleanupPending !== undefined) {
|
|
1937
|
-
latest.branchCleanupSkipped = { agentId, reason: cleanup.reason, updatedAt: now };
|
|
1938
|
-
delete latest.branchCleanupPending;
|
|
1939
|
-
changed = true;
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
else {
|
|
1943
|
-
if (latest.branchCleanupPending !== undefined) {
|
|
1944
|
-
delete latest.branchCleanupPending;
|
|
1945
|
-
changed = true;
|
|
1946
|
-
}
|
|
1947
|
-
if (latest.branchCleanupSkipped !== undefined) {
|
|
1948
|
-
delete latest.branchCleanupSkipped;
|
|
1949
|
-
changed = true;
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
if (!changed)
|
|
1953
|
-
return;
|
|
1954
|
-
latest.updatedAt = now;
|
|
1955
|
-
await this.taskStore.set(latest);
|
|
1956
|
-
});
|
|
1957
|
-
};
|
|
1926
|
+
const persistCleanup = (task, agentId, cleanup) => this.persistBranchCleanupOutcome(task, agentId, cleanup, latest => latest.branchCreatedByBaxian === true && TERMINAL_STATUSES.includes(latest.status));
|
|
1958
1927
|
for (const cfg of this.agentIndex.values()) {
|
|
1959
1928
|
if (cfg.role !== 'dev')
|
|
1960
1929
|
continue;
|
|
@@ -1980,7 +1949,9 @@ export class AgentManager {
|
|
|
1980
1949
|
`${err instanceof Error ? err.message : String(err)}`);
|
|
1981
1950
|
continue;
|
|
1982
1951
|
}
|
|
1983
|
-
|
|
1952
|
+
const isTarget = (task, present) => present.has(task.branch) || task.branchCleanupPending !== undefined;
|
|
1953
|
+
let present = new Set(refs.map(ref => ref.slice('refs/heads/'.length)));
|
|
1954
|
+
if (!candidateTasks.some(task => isTarget(task, present)))
|
|
1984
1955
|
continue;
|
|
1985
1956
|
if (this.isDeletionInFlight(cfg.id))
|
|
1986
1957
|
continue;
|
|
@@ -1988,6 +1959,7 @@ export class AgentManager {
|
|
|
1988
1959
|
if (!token)
|
|
1989
1960
|
continue;
|
|
1990
1961
|
const cleanupResults = [];
|
|
1962
|
+
let targets = [];
|
|
1991
1963
|
try {
|
|
1992
1964
|
if (!this.deletionGateOpen(cfg.id, genAtEntry))
|
|
1993
1965
|
continue;
|
|
@@ -1997,8 +1969,9 @@ export class AgentManager {
|
|
|
1997
1969
|
if (!(await this.lockManager.isOwner(cfg.id, owner, token)))
|
|
1998
1970
|
continue;
|
|
1999
1971
|
refs = await branches.listLocalTaskRefs(workdir);
|
|
2000
|
-
|
|
2001
|
-
|
|
1972
|
+
present = new Set(refs.map(ref => ref.slice('refs/heads/'.length)));
|
|
1973
|
+
targets = [...candidateByBranch.values()].filter(task => isTarget(task, present));
|
|
1974
|
+
if (targets.length === 0)
|
|
2002
1975
|
continue;
|
|
2003
1976
|
const tmux = new TmuxManager(runner);
|
|
2004
1977
|
if (await tmux.hasSession(cfg.id)) {
|
|
@@ -2007,16 +1980,8 @@ export class AgentManager {
|
|
|
2007
1980
|
}
|
|
2008
1981
|
if (!(await this.lockManager.isOwner(cfg.id, owner, token)))
|
|
2009
1982
|
continue;
|
|
2010
|
-
for (const
|
|
2011
|
-
const
|
|
2012
|
-
const task = candidateByBranch.get(branch);
|
|
2013
|
-
if (!task)
|
|
2014
|
-
continue;
|
|
2015
|
-
const cleanup = await branches.cleanupTaskBranch(workdir, {
|
|
2016
|
-
taskId: task.id,
|
|
2017
|
-
taskBranch: task.branch,
|
|
2018
|
-
branchCreatedByBaxian: task.branchCreatedByBaxian,
|
|
2019
|
-
}, () => this.assertTaskLockOwner(cfg.id, owner, token));
|
|
1983
|
+
for (const task of targets) {
|
|
1984
|
+
const cleanup = await branches.cleanupTaskBranch(workdir, this.cleanupIdentityFor(task), () => this.assertTaskLockOwner(cfg.id, owner, token));
|
|
2020
1985
|
cleanupResults.push({ task, cleanup });
|
|
2021
1986
|
}
|
|
2022
1987
|
}
|
|
@@ -2026,10 +1991,7 @@ export class AgentManager {
|
|
|
2026
1991
|
await this.markAwaitingHuman(cfg.id, 'dirty-workdir', message, { expectedTaskId: null });
|
|
2027
1992
|
}
|
|
2028
1993
|
else if (err instanceof ReplNotReadyError || message.includes('stayed busy past')) {
|
|
2029
|
-
for (const
|
|
2030
|
-
const task = candidateByBranch.get(actualRef.slice('refs/heads/'.length));
|
|
2031
|
-
if (!task)
|
|
2032
|
-
continue;
|
|
1994
|
+
for (const task of targets) {
|
|
2033
1995
|
cleanupResults.push({
|
|
2034
1996
|
task,
|
|
2035
1997
|
cleanup: {
|