baxian 2.0.16 → 2.0.18
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 +4 -1
- package/dist/agent/manager.d.ts.map +1 -1
- package/dist/agent/manager.js +95 -152
- package/dist/agent/manager.js.map +1 -1
- package/dist/agent/prompt.d.ts +1 -0
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js +38 -13
- package/dist/agent/prompt.js.map +1 -1
- package/dist/event/handlers.d.ts.map +1 -1
- package/dist/event/handlers.js +7 -4
- package/dist/event/handlers.js.map +1 -1
- package/dist/platform/driver-host.d.ts.map +1 -1
- package/dist/platform/driver-host.js +8 -6
- package/dist/platform/driver-host.js.map +1 -1
- package/dist/platform/feedback.d.ts +3 -7
- package/dist/platform/feedback.d.ts.map +1 -1
- package/dist/platform/feedback.js +11 -18
- package/dist/platform/feedback.js.map +1 -1
- package/dist/platform/github-driver.d.ts.map +1 -1
- package/dist/platform/github-driver.js +1 -3
- package/dist/platform/github-driver.js.map +1 -1
- package/dist/platform/markers.d.ts +1 -10
- package/dist/platform/markers.d.ts.map +1 -1
- package/dist/platform/markers.js +11 -30
- package/dist/platform/markers.js.map +1 -1
- package/dist/platform/verdict-engine.d.ts +3 -16
- package/dist/platform/verdict-engine.d.ts.map +1 -1
- package/dist/platform/verdict-engine.js +6 -18
- package/dist/platform/verdict-engine.js.map +1 -1
- package/dist/shared/types.d.ts +0 -1
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/state/task-store.js +1 -1
- package/dist/state/task-store.js.map +1 -1
- package/package.json +1 -1
package/dist/agent/manager.js
CHANGED
|
@@ -12,14 +12,14 @@ import { versionTimeOf } from '../platform/row-schema.js';
|
|
|
12
12
|
import { checkOpenPrBinding, checkPrBinding } from '../platform/pr-binding.js';
|
|
13
13
|
import { collectPendingFeedback, scanCommentSourcesOnce } from '../platform/feedback.js';
|
|
14
14
|
import { prReviewCacheRevision } from '../platform/pr-conversation-cache.js';
|
|
15
|
-
import { deadTokens,
|
|
15
|
+
import { deadTokens, liveVerdictVeto } from '../platform/verdict-engine.js';
|
|
16
16
|
import { platformBindingMismatch, taskNeedsPlatformBindingAudit, } from '../platform/startup.js';
|
|
17
17
|
import { AGENT_STORE_NOOP } from '../state/agent-store.js';
|
|
18
18
|
import { createRunner, LocalRunner, shellQuote, resolveAgentHost, workdirHostGroupKey, } from './runner.js';
|
|
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';
|
|
@@ -45,7 +45,6 @@ function samePassProvenance(left, right) {
|
|
|
45
45
|
&& right !== undefined
|
|
46
46
|
&& left.sourceKey === right.sourceKey
|
|
47
47
|
&& left.id === right.id
|
|
48
|
-
&& left.bodyDigest === right.bodyDigest
|
|
49
48
|
&& left.token === right.token
|
|
50
49
|
&& left.failToken === right.failToken
|
|
51
50
|
&& left.anchorSha === right.anchorSha;
|
|
@@ -150,6 +149,13 @@ const DEFAULT_NEED_INPUT_RETRY_INTERVAL_MS = 5_000;
|
|
|
150
149
|
const DEFAULT_DISPATCH_SETTLE_TIMEOUT_MS = 3_000;
|
|
151
150
|
const DELETE_CLEANUP_TIMEOUT_MS = 15_000;
|
|
152
151
|
const PLATFORM_HEAD_SHA_RE = new RegExp(`^${SHA_HEX_SOURCE}$`);
|
|
152
|
+
function branchCleanupIdentity(task) {
|
|
153
|
+
return {
|
|
154
|
+
taskId: task.id,
|
|
155
|
+
taskBranch: task.branch,
|
|
156
|
+
branchCreatedByBaxian: task.branchCreatedByBaxian,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
153
159
|
function createSignalTokenExcluding(...excluded) {
|
|
154
160
|
let token = createSignalToken();
|
|
155
161
|
while (excluded.includes(token))
|
|
@@ -1701,9 +1707,7 @@ export class AgentManager {
|
|
|
1701
1707
|
}
|
|
1702
1708
|
const pendingAt = new Date().toISOString();
|
|
1703
1709
|
if (boundTask && cfg.role === 'dev') {
|
|
1704
|
-
boundTask
|
|
1705
|
-
boundTask.updatedAt = pendingAt;
|
|
1706
|
-
await this.taskStore.set(boundTask);
|
|
1710
|
+
await this.persistBranchCleanupOutcome(boundTask, agentId, { status: 'pending', reason });
|
|
1707
1711
|
}
|
|
1708
1712
|
let wrote = false;
|
|
1709
1713
|
await this.agentStore.update(agentId, (latest2) => {
|
|
@@ -1769,59 +1773,8 @@ export class AgentManager {
|
|
|
1769
1773
|
}
|
|
1770
1774
|
if (cfg.role === 'dev' && boundTask) {
|
|
1771
1775
|
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
|
-
}
|
|
1776
|
+
const cleanup = await branches.cleanupTaskBranch(releaseWorkdir, this.cleanupIdentityFor(boundTask), () => this.assertTaskGeneration(agentId, expectedTaskId, lockToken, releaseWorkdir).then(() => undefined));
|
|
1777
|
+
await this.persistBranchCleanupOutcome(boundTask, agentId, cleanup);
|
|
1825
1778
|
if (cleanup.status !== 'deleted')
|
|
1826
1779
|
await branches.parkOnDefaultDetached(releaseWorkdir);
|
|
1827
1780
|
}
|
|
@@ -1891,6 +1844,64 @@ export class AgentManager {
|
|
|
1891
1844
|
console.warn(`[AgentManager] clearing branchLocalCleaned(${taskId}) failed:`, err);
|
|
1892
1845
|
});
|
|
1893
1846
|
}
|
|
1847
|
+
cleanupIdentityFor(task) {
|
|
1848
|
+
const identity = branchCleanupIdentity(task);
|
|
1849
|
+
if (task.status === 'merged' && task.prNumber !== undefined) {
|
|
1850
|
+
identity.resolveMergedHeadSha = () => this.platformFetchPrHeadSha(task.id);
|
|
1851
|
+
}
|
|
1852
|
+
return identity;
|
|
1853
|
+
}
|
|
1854
|
+
async persistBranchCleanupOutcome(task, agentId, cleanup, guard) {
|
|
1855
|
+
await this.withTaskLock(async () => {
|
|
1856
|
+
const latest = await this.taskStore.get(task.id);
|
|
1857
|
+
if (!latest || latest.branch !== task.branch || (guard !== undefined && !guard(latest)))
|
|
1858
|
+
return;
|
|
1859
|
+
const effective = cleanup;
|
|
1860
|
+
const now = new Date().toISOString();
|
|
1861
|
+
let changed = false;
|
|
1862
|
+
if (effective.status === 'pending') {
|
|
1863
|
+
if (latest.branchCleanupPending?.agentId !== agentId
|
|
1864
|
+
|| latest.branchCleanupPending.reason !== effective.reason
|
|
1865
|
+
|| latest.branchCleanupSkipped !== undefined) {
|
|
1866
|
+
latest.branchCleanupPending = { agentId, reason: effective.reason, updatedAt: now };
|
|
1867
|
+
delete latest.branchCleanupSkipped;
|
|
1868
|
+
changed = true;
|
|
1869
|
+
}
|
|
1870
|
+
if (effective.remoteTipSha !== undefined
|
|
1871
|
+
&& latest.branchLocalCleaned?.remoteTipSha !== effective.remoteTipSha) {
|
|
1872
|
+
latest.branchLocalCleaned = { remoteTipSha: effective.remoteTipSha, updatedAt: now };
|
|
1873
|
+
changed = true;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
else if (effective.status === 'skipped') {
|
|
1877
|
+
if (latest.branchCleanupSkipped?.agentId !== agentId
|
|
1878
|
+
|| latest.branchCleanupSkipped.reason !== effective.reason
|
|
1879
|
+
|| latest.branchCleanupPending !== undefined) {
|
|
1880
|
+
latest.branchCleanupSkipped = { agentId, reason: effective.reason, updatedAt: now };
|
|
1881
|
+
delete latest.branchCleanupPending;
|
|
1882
|
+
changed = true;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
if (latest.branchCleanupPending !== undefined) {
|
|
1887
|
+
delete latest.branchCleanupPending;
|
|
1888
|
+
changed = true;
|
|
1889
|
+
}
|
|
1890
|
+
if (latest.branchCleanupSkipped !== undefined) {
|
|
1891
|
+
delete latest.branchCleanupSkipped;
|
|
1892
|
+
changed = true;
|
|
1893
|
+
}
|
|
1894
|
+
if (effective.remoteTipSha) {
|
|
1895
|
+
latest.branchLocalCleaned = { remoteTipSha: effective.remoteTipSha, updatedAt: now };
|
|
1896
|
+
changed = true;
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
if (!changed)
|
|
1900
|
+
return;
|
|
1901
|
+
latest.updatedAt = now;
|
|
1902
|
+
await this.taskStore.set(latest);
|
|
1903
|
+
});
|
|
1904
|
+
}
|
|
1894
1905
|
async releaseAgentIfBound(agentId, expectedTaskId, opts) {
|
|
1895
1906
|
const state = await this.agentStore.get(agentId);
|
|
1896
1907
|
if (state?.taskId !== expectedTaskId)
|
|
@@ -1911,50 +1922,7 @@ export class AgentManager {
|
|
|
1911
1922
|
&& task.branchCleanupSkipped === undefined)
|
|
1912
1923
|
.map(task => [task.branch, task]));
|
|
1913
1924
|
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
|
-
};
|
|
1925
|
+
const persistCleanup = (task, agentId, cleanup) => this.persistBranchCleanupOutcome(task, agentId, cleanup, latest => latest.branchCreatedByBaxian === true && TERMINAL_STATUSES.includes(latest.status));
|
|
1958
1926
|
for (const cfg of this.agentIndex.values()) {
|
|
1959
1927
|
if (cfg.role !== 'dev')
|
|
1960
1928
|
continue;
|
|
@@ -1980,7 +1948,9 @@ export class AgentManager {
|
|
|
1980
1948
|
`${err instanceof Error ? err.message : String(err)}`);
|
|
1981
1949
|
continue;
|
|
1982
1950
|
}
|
|
1983
|
-
|
|
1951
|
+
const isTarget = (task, present) => present.has(task.branch) || task.branchCleanupPending !== undefined;
|
|
1952
|
+
let present = new Set(refs.map(ref => ref.slice('refs/heads/'.length)));
|
|
1953
|
+
if (!candidateTasks.some(task => isTarget(task, present)))
|
|
1984
1954
|
continue;
|
|
1985
1955
|
if (this.isDeletionInFlight(cfg.id))
|
|
1986
1956
|
continue;
|
|
@@ -1988,6 +1958,7 @@ export class AgentManager {
|
|
|
1988
1958
|
if (!token)
|
|
1989
1959
|
continue;
|
|
1990
1960
|
const cleanupResults = [];
|
|
1961
|
+
let targets = [];
|
|
1991
1962
|
try {
|
|
1992
1963
|
if (!this.deletionGateOpen(cfg.id, genAtEntry))
|
|
1993
1964
|
continue;
|
|
@@ -1997,8 +1968,9 @@ export class AgentManager {
|
|
|
1997
1968
|
if (!(await this.lockManager.isOwner(cfg.id, owner, token)))
|
|
1998
1969
|
continue;
|
|
1999
1970
|
refs = await branches.listLocalTaskRefs(workdir);
|
|
2000
|
-
|
|
2001
|
-
|
|
1971
|
+
present = new Set(refs.map(ref => ref.slice('refs/heads/'.length)));
|
|
1972
|
+
targets = [...candidateByBranch.values()].filter(task => isTarget(task, present));
|
|
1973
|
+
if (targets.length === 0)
|
|
2002
1974
|
continue;
|
|
2003
1975
|
const tmux = new TmuxManager(runner);
|
|
2004
1976
|
if (await tmux.hasSession(cfg.id)) {
|
|
@@ -2007,16 +1979,8 @@ export class AgentManager {
|
|
|
2007
1979
|
}
|
|
2008
1980
|
if (!(await this.lockManager.isOwner(cfg.id, owner, token)))
|
|
2009
1981
|
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));
|
|
1982
|
+
for (const task of targets) {
|
|
1983
|
+
const cleanup = await branches.cleanupTaskBranch(workdir, this.cleanupIdentityFor(task), () => this.assertTaskLockOwner(cfg.id, owner, token));
|
|
2020
1984
|
cleanupResults.push({ task, cleanup });
|
|
2021
1985
|
}
|
|
2022
1986
|
}
|
|
@@ -2026,10 +1990,7 @@ export class AgentManager {
|
|
|
2026
1990
|
await this.markAwaitingHuman(cfg.id, 'dirty-workdir', message, { expectedTaskId: null });
|
|
2027
1991
|
}
|
|
2028
1992
|
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;
|
|
1993
|
+
for (const task of targets) {
|
|
2033
1994
|
cleanupResults.push({
|
|
2034
1995
|
task,
|
|
2035
1996
|
cleanup: {
|
|
@@ -5457,25 +5418,16 @@ export class AgentManager {
|
|
|
5457
5418
|
const { task, driver } = await this.platformTaskAndDriver(taskId);
|
|
5458
5419
|
if (task.prNumber === undefined)
|
|
5459
5420
|
return { kind: 'provenance-invalid', reason: 'no bound PR' };
|
|
5460
|
-
|
|
5461
|
-
if (!provenance)
|
|
5421
|
+
if (!task.passProvenance)
|
|
5462
5422
|
return { kind: 'provenance-invalid', reason: 'no accepted pass provenance' };
|
|
5463
5423
|
const scans = await this.platformScanComments(driver, task.prNumber);
|
|
5464
|
-
const recheck = recheckPassProvenance({
|
|
5465
|
-
token: provenance.token,
|
|
5466
|
-
failToken: provenance.failToken,
|
|
5467
|
-
anchorSha: provenance.anchorSha,
|
|
5468
|
-
carrier: { sourceKey: provenance.sourceKey, id: provenance.id, bodyDigest: provenance.bodyDigest },
|
|
5469
|
-
}, scans);
|
|
5470
|
-
if (!recheck.ok) {
|
|
5471
|
-
return recheck.reason === 'source-scan-incomplete'
|
|
5472
|
-
? { kind: 'retryable', reason: recheck.reason }
|
|
5473
|
-
: { kind: 'provenance-invalid', reason: `provenance ${recheck.reason}` };
|
|
5474
|
-
}
|
|
5475
5424
|
const pending = collectPendingFeedback(scans);
|
|
5476
5425
|
if (!pending.allSourcesOk)
|
|
5477
5426
|
return { kind: 'retryable', reason: 'source scan incomplete' };
|
|
5478
|
-
|
|
5427
|
+
const veto = liveVerdictVeto(task.passProvenance, scans);
|
|
5428
|
+
if (veto)
|
|
5429
|
+
return { kind: 'provenance-invalid', reason: `provenance ${veto}` };
|
|
5430
|
+
return { kind: 'valid', pending: pending.pending };
|
|
5479
5431
|
}
|
|
5480
5432
|
async platformPendingFeedback(taskId) {
|
|
5481
5433
|
const { task, driver } = await this.platformTaskAndDriver(taskId);
|
|
@@ -5508,24 +5460,14 @@ export class AgentManager {
|
|
|
5508
5460
|
if (provenance.anchorSha.toLowerCase() !== opts.expectedHeadSha.toLowerCase()) {
|
|
5509
5461
|
throw new PlatformMergeRecheckError('provenance-invalid', `pass provenance anchors ${provenance.anchorSha}, not the merge head ${opts.expectedHeadSha}`);
|
|
5510
5462
|
}
|
|
5511
|
-
const
|
|
5512
|
-
|
|
5513
|
-
token: provenance.token,
|
|
5514
|
-
failToken: provenance.failToken,
|
|
5515
|
-
anchorSha: provenance.anchorSha,
|
|
5516
|
-
carrier: { sourceKey: provenance.sourceKey, id: provenance.id, bodyDigest: provenance.bodyDigest },
|
|
5517
|
-
}, scans);
|
|
5518
|
-
if (!recheck.ok) {
|
|
5519
|
-
if (recheck.reason === 'source-scan-incomplete') {
|
|
5520
|
-
throw new Error('merge blocked: provenance source-scan-incomplete');
|
|
5521
|
-
}
|
|
5522
|
-
throw new PlatformMergeRecheckError('provenance-invalid', `provenance ${recheck.reason}`);
|
|
5523
|
-
}
|
|
5524
|
-
const pending = collectPendingFeedback(scans);
|
|
5525
|
-
if (!pending.allSourcesOk)
|
|
5463
|
+
const verified = await this.platformVerifyAcceptedPass(taskId);
|
|
5464
|
+
if (verified.kind === 'retryable')
|
|
5526
5465
|
throw new Error('merge blocked: pending-feedback scan incomplete');
|
|
5527
|
-
if (
|
|
5528
|
-
throw new PlatformMergeRecheckError('
|
|
5466
|
+
if (verified.kind === 'provenance-invalid') {
|
|
5467
|
+
throw new PlatformMergeRecheckError('provenance-invalid', verified.reason);
|
|
5468
|
+
}
|
|
5469
|
+
if (verified.pending.size > 0) {
|
|
5470
|
+
throw new PlatformMergeRecheckError('pending-feedback', `${verified.pending.size} pending feedback comment(s) without ack`);
|
|
5529
5471
|
}
|
|
5530
5472
|
}
|
|
5531
5473
|
const fresh = await this.taskStore.get(taskId);
|
|
@@ -7329,6 +7271,7 @@ export class AgentManager {
|
|
|
7329
7271
|
...(platform ? { platform } : {}),
|
|
7330
7272
|
...(signalToken ? { signalToken } : {}),
|
|
7331
7273
|
...(imagePaths.length ? { imagePaths } : {}),
|
|
7274
|
+
...(opts.pendingFeedback?.length ? { pendingFeedback: opts.pendingFeedback } : {}),
|
|
7332
7275
|
});
|
|
7333
7276
|
}
|
|
7334
7277
|
catch (err) {
|
|
@@ -8417,7 +8360,7 @@ export class AgentManager {
|
|
|
8417
8360
|
if (accepted.kind !== 'valid') {
|
|
8418
8361
|
throw new ApiError(409, `Cannot restore task ${task.id}: ${accepted.reason}`);
|
|
8419
8362
|
}
|
|
8420
|
-
if (accepted.
|
|
8363
|
+
if (accepted.pending.size > 0) {
|
|
8421
8364
|
throw new ApiError(409, `Cannot restore task ${task.id}: the accepted pass has pending feedback`);
|
|
8422
8365
|
}
|
|
8423
8366
|
const reviewedPass = task.passProvenance;
|