@ricsam/r5d-worker 0.0.82 → 0.0.86
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/cjs/main.cjs +470 -38
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/workspace-automatic-sync-policy.cjs +12 -2
- package/dist/cjs/workspace-command-sync-policy.cjs +21 -0
- package/dist/cjs/workspace-git-sync.cjs +79 -3
- package/dist/mjs/main.mjs +478 -40
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/workspace-automatic-sync-policy.mjs +9 -1
- package/dist/mjs/workspace-command-sync-policy.mjs +20 -0
- package/dist/mjs/workspace-git-sync.mjs +76 -3
- package/dist/types/main.d.ts +349 -0
- package/dist/types/workspace-automatic-sync-policy.d.ts +17 -0
- package/dist/types/workspace-command-sync-policy.d.ts +7 -0
- package/dist/types/workspace-git-sync.d.ts +36 -0
- package/package.json +1 -1
package/dist/mjs/main.mjs
CHANGED
|
@@ -28,11 +28,14 @@ import {
|
|
|
28
28
|
PENDING_CREATED_BRANCH_AUTOMATIC_SYNC_GRACE_MS,
|
|
29
29
|
pendingCreatedBranchAutomaticSyncDeferral,
|
|
30
30
|
pendingCreatedBranchKey,
|
|
31
|
-
projectBranchHasActiveWorkspaceTarget
|
|
31
|
+
projectBranchHasActiveWorkspaceTarget,
|
|
32
|
+
projectBranchMountBusyDuringExclusiveSync,
|
|
33
|
+
workspacePlansMountBusyDuringExclusiveSync
|
|
32
34
|
} from "./workspace-automatic-sync-policy.mjs";
|
|
33
35
|
import {
|
|
34
36
|
acquireWorkspaceCommandMutation,
|
|
35
37
|
reserveWorkspaceCommandAfterCurrentSync,
|
|
38
|
+
runReservedWorkspaceCommand,
|
|
36
39
|
runWorkspaceCommand
|
|
37
40
|
} from "./workspace-command-sync-policy.mjs";
|
|
38
41
|
import { WorkspaceMutationGate } from "./workspace-mutation-gate.mjs";
|
|
@@ -79,12 +82,15 @@ import {
|
|
|
79
82
|
pruneAuthoritativelyDesiredBranchDeletions
|
|
80
83
|
} from "./project-workspace-state.mjs";
|
|
81
84
|
import {
|
|
85
|
+
configureExistingWorkspaceGitForRemediation,
|
|
82
86
|
ensureWorkspaceGitClone,
|
|
83
87
|
hydrateWorkspaceGitMounts,
|
|
84
88
|
recoverWorkspaceGitHydration,
|
|
85
89
|
resetWorkspaceGit,
|
|
86
90
|
synchronizeWorkspaceGit,
|
|
87
|
-
workspaceGitHydrationIsCurrent
|
|
91
|
+
workspaceGitHydrationIsCurrent,
|
|
92
|
+
workspaceGitMountsForRemediation,
|
|
93
|
+
WorkspaceRemediationAncestryError
|
|
88
94
|
} from "./workspace-git-sync.mjs";
|
|
89
95
|
class ProjectWorkspaceConfigurationDeferredError extends Error {
|
|
90
96
|
}
|
|
@@ -99,6 +105,7 @@ const DEFAULT_READ_MAX_BYTES = 5e4;
|
|
|
99
105
|
const MAX_LINE_LENGTH = 2e3;
|
|
100
106
|
const WORKSPACE_GIT_QUIET_MS = 5e3;
|
|
101
107
|
const WORKSPACE_GIT_PERIODIC_MS = 6e4;
|
|
108
|
+
const WORKSPACE_INCIDENT_CONFIG_REFRESH_TIMEOUT_MS = 6e4;
|
|
102
109
|
const PTY_INPUT_BUSY_GRACE_MS = 3e3;
|
|
103
110
|
const PTY_FOREGROUND_POLL_MS = 1e3;
|
|
104
111
|
const PTY_FOREGROUND_IDLE_ENABLED = process.env.R5D_PTY_FOREGROUND_IDLE !== "0";
|
|
@@ -257,6 +264,7 @@ const workerPtyTestHarness = {
|
|
|
257
264
|
removeEnvFiles: removePtyEnvFiles,
|
|
258
265
|
resolveEnvFileReferences: resolvePtyEnvFileReferences,
|
|
259
266
|
commandHasWorkspaceEffect: workerCommandHasWorkspaceEffect,
|
|
267
|
+
canonicalSyncTerminalHead,
|
|
260
268
|
ptyIsWorkspaceBusy: workerPtyIsWorkspaceBusy,
|
|
261
269
|
parseLinuxForegroundBusy: parseLinuxPtyForegroundBusy
|
|
262
270
|
};
|
|
@@ -1518,6 +1526,99 @@ function credentialReapContractStatus(probe) {
|
|
|
1518
1526
|
function verifiedCredentialReapContract(probe) {
|
|
1519
1527
|
return credentialReapContractStatus(probe) === "verified_systemd";
|
|
1520
1528
|
}
|
|
1529
|
+
function workspaceConfigurationIncidentDeferral(input) {
|
|
1530
|
+
const incidentId = input.requestedIncidentId ?? input.activeIncidentId;
|
|
1531
|
+
if (incidentId === null) return { incidentId: null };
|
|
1532
|
+
if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(incidentId)) {
|
|
1533
|
+
return { incidentId: null, error: new Error("Workspace configuration incident deferral requires a canonical incident UUID") };
|
|
1534
|
+
}
|
|
1535
|
+
if (input.requestedIncidentId !== void 0 && input.requestedIncidentId !== input.activeIncidentId) {
|
|
1536
|
+
return {
|
|
1537
|
+
incidentId: null,
|
|
1538
|
+
error: new Error(
|
|
1539
|
+
`Workspace configuration incident deferral ${input.requestedIncidentId} does not match active incident ${input.activeIncidentId ?? "none"}`
|
|
1540
|
+
)
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
return { incidentId };
|
|
1544
|
+
}
|
|
1545
|
+
function workspaceIncidentTerminalClearDisposition(input) {
|
|
1546
|
+
if (!input.deferredConfiguration) return "await_server_refresh";
|
|
1547
|
+
if (input.deferredConfiguration.incidentId !== input.clearedIncidentId) return "reconnect_for_refresh";
|
|
1548
|
+
return input.deferredConfiguration.serverRefreshExpected ? "await_server_refresh" : "reconnect_for_refresh";
|
|
1549
|
+
}
|
|
1550
|
+
function workspaceIncidentTerminalRefreshFence(input) {
|
|
1551
|
+
const disposition = workspaceIncidentTerminalClearDisposition(input);
|
|
1552
|
+
return {
|
|
1553
|
+
disposition,
|
|
1554
|
+
deferredConfiguration: input.deferredConfiguration ?? { incidentId: input.clearedIncidentId, serverRefreshExpected: true }
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
function workspaceAutomaticSyncIsAdmitted(input) {
|
|
1558
|
+
return input.workspaceConfigured && input.activeIncidentId === null && input.deferredConfiguration === null;
|
|
1559
|
+
}
|
|
1560
|
+
function deferredWorkspaceTargetIsAllowed(deferredConfiguration, activeIncidentId, target) {
|
|
1561
|
+
const canonicalTarget = target.type === "workspace" && target.rootProfile === "canonical_sync";
|
|
1562
|
+
if (deferredConfiguration) return activeIncidentId === deferredConfiguration.incidentId && canonicalTarget;
|
|
1563
|
+
return activeIncidentId === null || canonicalTarget;
|
|
1564
|
+
}
|
|
1565
|
+
function deferredWorkspaceSyncTriggerIsAllowed(deferredConfiguration, activeIncidentId, trigger) {
|
|
1566
|
+
const incidentId = deferredConfiguration?.incidentId ?? activeIncidentId;
|
|
1567
|
+
return incidentId === null || (deferredConfiguration === null || activeIncidentId === deferredConfiguration.incidentId) && (trigger.type === "remediation" || trigger.type === "remediation_confirm" || trigger.type === "remediation_reset");
|
|
1568
|
+
}
|
|
1569
|
+
function workspaceOperationsAreFenced(deferredConfiguration, activeIncidentId) {
|
|
1570
|
+
return deferredConfiguration !== null || activeIncidentId !== null;
|
|
1571
|
+
}
|
|
1572
|
+
function workspaceCommandTransportIsAllowed(deferredConfiguration, activeIncidentId, transport) {
|
|
1573
|
+
return !workspaceOperationsAreFenced(deferredConfiguration, activeIncidentId) || transport === "exec_start";
|
|
1574
|
+
}
|
|
1575
|
+
function deferredCredentialTransitionMustWait(input) {
|
|
1576
|
+
return input.incidentId !== null && input.transitionPhase !== "current" && input.canonicalRemediationActive;
|
|
1577
|
+
}
|
|
1578
|
+
function workspaceConfigurationIncidentSnapshotIsCurrent(capturedIncidentId, activeIncidentId) {
|
|
1579
|
+
return capturedIncidentId === null ? activeIncidentId === null : activeIncidentId === null || activeIncidentId === capturedIncidentId;
|
|
1580
|
+
}
|
|
1581
|
+
function workspaceConfigurationResetToCanonicalIsAllowed(requested, incidentId) {
|
|
1582
|
+
return requested === true && incidentId === null;
|
|
1583
|
+
}
|
|
1584
|
+
function deferredWorkspaceRefreshWatchdogIsCurrent(input) {
|
|
1585
|
+
return input.deferredConfiguration?.incidentId === input.capturedIncidentId && input.activeIncidentId === null;
|
|
1586
|
+
}
|
|
1587
|
+
function incidentDeferredProjectCheckouts(projects) {
|
|
1588
|
+
return projects.flatMap(
|
|
1589
|
+
(project) => project.executionDisabled ? [] : project.branches.map(({ branchName }) => ({ projectId: project.projectId, branchName }))
|
|
1590
|
+
).sort((left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName));
|
|
1591
|
+
}
|
|
1592
|
+
function deferredIncidentWorkspaceConfigurationResult(input) {
|
|
1593
|
+
return {
|
|
1594
|
+
type: "workspace_sync",
|
|
1595
|
+
attemptId: input.attemptId,
|
|
1596
|
+
workerLabel: input.workerLabel,
|
|
1597
|
+
trigger: { type: "connect", detail: "workspace synchronization deferred for active remediation" },
|
|
1598
|
+
outcome: "no_change",
|
|
1599
|
+
startingHead: input.head,
|
|
1600
|
+
...input.head ? { localHead: input.head } : {},
|
|
1601
|
+
rebaseCount: 0,
|
|
1602
|
+
diffSizeBytes: 0,
|
|
1603
|
+
gitStatus: "",
|
|
1604
|
+
affectedProjects: [],
|
|
1605
|
+
affectedPaths: [],
|
|
1606
|
+
activeMountIds: [],
|
|
1607
|
+
skippedMountIds: [...input.skippedMountIds].sort(),
|
|
1608
|
+
activeProjectBranchPublications: [],
|
|
1609
|
+
discardedPaths: [],
|
|
1610
|
+
localChangesDiscarded: false
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
function workspaceSyncFailureHydrationIsSafe(input) {
|
|
1614
|
+
if (input.error instanceof WorkspaceRemediationAncestryError) return true;
|
|
1615
|
+
if (input.resetToCanonical) return false;
|
|
1616
|
+
try {
|
|
1617
|
+
return input.inspectCurrentHydration();
|
|
1618
|
+
} catch {
|
|
1619
|
+
return false;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1521
1622
|
function fenceUnsafeWorkspaceSyncFailure(input) {
|
|
1522
1623
|
if (input.hydrationCurrent) return false;
|
|
1523
1624
|
input.invalidateExecution();
|
|
@@ -1550,7 +1651,24 @@ const workerGitSecurityTestHarness = {
|
|
|
1550
1651
|
credentialReapContractStatus,
|
|
1551
1652
|
verifiedCredentialReapContract,
|
|
1552
1653
|
fenceUnsafeWorkspaceSyncFailure,
|
|
1654
|
+
workspaceConfigurationIncidentDeferral,
|
|
1655
|
+
workspaceIncidentTerminalClearDisposition,
|
|
1656
|
+
workspaceIncidentTerminalRefreshFence,
|
|
1657
|
+
workspaceAutomaticSyncIsAdmitted,
|
|
1658
|
+
deferredWorkspaceTargetIsAllowed,
|
|
1659
|
+
deferredWorkspaceSyncTriggerIsAllowed,
|
|
1660
|
+
workspaceOperationsAreFenced,
|
|
1661
|
+
workspaceCommandTransportIsAllowed,
|
|
1662
|
+
deferredCredentialTransitionMustWait,
|
|
1663
|
+
workspaceConfigurationIncidentSnapshotIsCurrent,
|
|
1664
|
+
workspaceConfigurationResetToCanonicalIsAllowed,
|
|
1665
|
+
deferredWorkspaceRefreshWatchdogIsCurrent,
|
|
1666
|
+
incidentDeferredProjectCheckouts,
|
|
1667
|
+
deferredIncidentWorkspaceConfigurationResult,
|
|
1668
|
+
workspaceSyncFailureHydrationIsSafe,
|
|
1553
1669
|
assertWorkerChildAdmission,
|
|
1670
|
+
executeWriteFileOperation,
|
|
1671
|
+
executeEditFileOperation,
|
|
1554
1672
|
terminateCredentialBearingChildren,
|
|
1555
1673
|
terminateCredentialBearingChildrenWithRetention,
|
|
1556
1674
|
async commitCredentialGeneration(prepared, credential, children, beforeMutation, previousGenerationFenced = false) {
|
|
@@ -1701,6 +1819,15 @@ function hasProjectWorktree(checkoutPath) {
|
|
|
1701
1819
|
return false;
|
|
1702
1820
|
}
|
|
1703
1821
|
}
|
|
1822
|
+
function canonicalSyncTerminalHead(resolvedTarget, readHead = (rootPath) => runGit(["rev-parse", "HEAD"], { cwd: rootPath })) {
|
|
1823
|
+
if (resolvedTarget.target.type !== "workspace" || resolvedTarget.target.rootProfile !== "canonical_sync") return void 0;
|
|
1824
|
+
try {
|
|
1825
|
+
const head = readHead(resolvedTarget.rootPath);
|
|
1826
|
+
return /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(head) ? head : void 0;
|
|
1827
|
+
} catch {
|
|
1828
|
+
return void 0;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1704
1831
|
function describeWorkerSessionTarget(target) {
|
|
1705
1832
|
return target.type === "project" ? `${target.projectId}/${target.branchName}` : `${target.ownerUserId}/${target.rootProfile}`;
|
|
1706
1833
|
}
|
|
@@ -2005,8 +2132,10 @@ async function executeWriteFileOperation(input) {
|
|
|
2005
2132
|
planRoot: input.planRoot,
|
|
2006
2133
|
access: "write"
|
|
2007
2134
|
});
|
|
2135
|
+
input.assertAdmission();
|
|
2008
2136
|
const resolved = resolveWorkerFilePath(input.resolvedTarget.rootPath, input.message.filePath, builtInPaths);
|
|
2009
2137
|
return withFileMutationQueue(mutationQueueKey(input.resolvedTarget.target, resolved), async () => {
|
|
2138
|
+
input.assertAdmission();
|
|
2010
2139
|
return writeWorkerTextFile(input.resolvedTarget.rootPath, input.message.filePath, input.message.content, builtInPaths);
|
|
2011
2140
|
});
|
|
2012
2141
|
}
|
|
@@ -2022,8 +2151,10 @@ async function executeEditFileOperation(input) {
|
|
|
2022
2151
|
planRoot: input.planRoot,
|
|
2023
2152
|
access: "write"
|
|
2024
2153
|
});
|
|
2154
|
+
input.assertAdmission();
|
|
2025
2155
|
const resolved = resolveWorkerFilePath(input.resolvedTarget.rootPath, input.message.filePath, builtInPaths);
|
|
2026
2156
|
return withFileMutationQueue(mutationQueueKey(input.resolvedTarget.target, resolved), async () => {
|
|
2157
|
+
input.assertAdmission();
|
|
2027
2158
|
return editWorkerTextFile(input.resolvedTarget.rootPath, input.message.filePath, input.message.edits, builtInPaths);
|
|
2028
2159
|
});
|
|
2029
2160
|
}
|
|
@@ -2599,13 +2730,15 @@ async function executeStreamingCommand(input) {
|
|
|
2599
2730
|
});
|
|
2600
2731
|
})
|
|
2601
2732
|
]);
|
|
2733
|
+
const canonicalWorkspaceHead = canonicalSyncTerminalHead(input.resolvedTarget);
|
|
2602
2734
|
const terminal = {
|
|
2603
2735
|
type: "exec_exit",
|
|
2604
2736
|
runId: input.message.runId,
|
|
2605
2737
|
exitCode,
|
|
2606
2738
|
durationMs: Date.now() - startedAt,
|
|
2607
2739
|
...timedOut ? { timedOut: true } : {},
|
|
2608
|
-
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2740
|
+
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {},
|
|
2741
|
+
...canonicalWorkspaceHead ? { canonicalWorkspaceHead } : {}
|
|
2609
2742
|
};
|
|
2610
2743
|
pendingProcessTerminals.set(input.message.runId, terminal);
|
|
2611
2744
|
sendWorkerMessage(input.ws, terminal);
|
|
@@ -2613,12 +2746,14 @@ async function executeStreamingCommand(input) {
|
|
|
2613
2746
|
if (!started && error instanceof StaleWorkerAdmissionError) throw error;
|
|
2614
2747
|
const message = error instanceof Error ? error.message : String(error);
|
|
2615
2748
|
if (started) {
|
|
2749
|
+
const canonicalWorkspaceHead = canonicalSyncTerminalHead(input.resolvedTarget);
|
|
2616
2750
|
const terminal = {
|
|
2617
2751
|
type: "exec_error",
|
|
2618
2752
|
runId: input.message.runId,
|
|
2619
2753
|
error: message,
|
|
2620
2754
|
durationMs: Date.now() - startedAt,
|
|
2621
|
-
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {}
|
|
2755
|
+
...input.message.workspaceEffect === "none" ? { workspaceEffect: "none" } : {},
|
|
2756
|
+
...canonicalWorkspaceHead ? { canonicalWorkspaceHead } : {}
|
|
2622
2757
|
};
|
|
2623
2758
|
pendingProcessTerminals.set(input.message.runId, terminal);
|
|
2624
2759
|
sendWorkerMessage(input.ws, terminal);
|
|
@@ -3273,6 +3408,9 @@ async function startWorker(options) {
|
|
|
3273
3408
|
let workspaceGitIdentity = null;
|
|
3274
3409
|
let workspaceConfigured = false;
|
|
3275
3410
|
let activeWorkspaceIncidentId = null;
|
|
3411
|
+
let deferredWorkspaceConfiguration = null;
|
|
3412
|
+
let deferredWorkspaceConfigurationRefreshTimer;
|
|
3413
|
+
let workspaceConfigurationReceiptGeneration = 0;
|
|
3276
3414
|
let workspaceAutomaticTimer;
|
|
3277
3415
|
let workspacePeriodicTimer;
|
|
3278
3416
|
let pendingAutomaticTrigger;
|
|
@@ -3284,6 +3422,11 @@ async function startWorker(options) {
|
|
|
3284
3422
|
let cliUpdateInProgress = false;
|
|
3285
3423
|
let reloadAfterClose = false;
|
|
3286
3424
|
let shutdownAfterClose = false;
|
|
3425
|
+
const automaticWorkspaceSyncIsCurrentlyAdmitted = () => workspaceAutomaticSyncIsAdmitted({
|
|
3426
|
+
workspaceConfigured,
|
|
3427
|
+
activeIncidentId: activeWorkspaceIncidentId,
|
|
3428
|
+
deferredConfiguration: deferredWorkspaceConfiguration
|
|
3429
|
+
});
|
|
3287
3430
|
const bearerAuthHeader = `Authorization: Bearer ${token}`;
|
|
3288
3431
|
const workerCredentialUsername = credentialUsernameForAuthHeader(bearerAuthHeader);
|
|
3289
3432
|
const projectBranchKey = (projectId, branchName) => `${projectId}\0${branchName}`;
|
|
@@ -3565,6 +3708,17 @@ async function startWorker(options) {
|
|
|
3565
3708
|
const activeTargets = activeWorkspaceMutationTargets();
|
|
3566
3709
|
return projectConfigById.get(projectId)?.executionDisabled === true || hasProjectWorktree(branchPath) && projectWorktreeOperationInProgress(branchPath) || canonicalWorkspaceMutationIsActive(activeTargets) || projectBranchHasActiveWorkspaceTarget(projectId, branchName, activeTargets);
|
|
3567
3710
|
};
|
|
3711
|
+
const projectBranchMountExclusiveSyncBusy = (projectId, branchName, branchPath) => {
|
|
3712
|
+
const activeTargets = activeWorkspaceMutationTargets();
|
|
3713
|
+
return projectBranchMountBusyDuringExclusiveSync({
|
|
3714
|
+
projectId,
|
|
3715
|
+
branchName,
|
|
3716
|
+
executionDisabled: projectConfigById.get(projectId)?.executionDisabled === true,
|
|
3717
|
+
worktreeOperationInProgress: hasProjectWorktree(branchPath) && projectWorktreeOperationInProgress(branchPath),
|
|
3718
|
+
retainedCanonicalProcessGroup: canonicalWorkspaceMutationIsActive([...credentialBearingProcessGroupTargets.values()]),
|
|
3719
|
+
activeTargets
|
|
3720
|
+
});
|
|
3721
|
+
};
|
|
3568
3722
|
const projectBranchMountBusy = (projectId, branchName, branchPath) => !readyProjectIds.has(projectId) || projectBranchMountActivityBusy(projectId, branchName, branchPath);
|
|
3569
3723
|
const buildWorkspaceMounts = (projects = projectConfigById.values()) => {
|
|
3570
3724
|
const mounts = [];
|
|
@@ -3597,7 +3751,7 @@ async function startWorker(options) {
|
|
|
3597
3751
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3598
3752
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3599
3753
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
3600
|
-
busyForRecovery: () =>
|
|
3754
|
+
busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
|
|
3601
3755
|
});
|
|
3602
3756
|
mounts.push({
|
|
3603
3757
|
id: projectPlanMountId(project.projectId, branch.branchName),
|
|
@@ -3611,7 +3765,7 @@ async function startWorker(options) {
|
|
|
3611
3765
|
preserveLocalOnHydrationBasisChange: preservesCheckoutPathMove,
|
|
3612
3766
|
busy: () => projectBranchMountBusy(project.projectId, branch.branchName, branchPath),
|
|
3613
3767
|
mutationToken: () => projectWorkspaceMutationToken(project.projectId, branch.branchName),
|
|
3614
|
-
busyForRecovery: () =>
|
|
3768
|
+
busyForRecovery: () => projectBranchMountExclusiveSyncBusy(project.projectId, branch.branchName, branchPath)
|
|
3615
3769
|
});
|
|
3616
3770
|
}
|
|
3617
3771
|
void projectRoot;
|
|
@@ -3634,7 +3788,10 @@ async function startWorker(options) {
|
|
|
3634
3788
|
mutationToken: () => String(visibleWorkspaceMutationEpoch),
|
|
3635
3789
|
busyForRecovery: () => {
|
|
3636
3790
|
const activeTargets = activeWorkspaceMutationTargets();
|
|
3637
|
-
return
|
|
3791
|
+
return workspacePlansMountBusyDuringExclusiveSync(
|
|
3792
|
+
activeTargets,
|
|
3793
|
+
canonicalWorkspaceMutationIsActive([...credentialBearingProcessGroupTargets.values()])
|
|
3794
|
+
);
|
|
3638
3795
|
}
|
|
3639
3796
|
});
|
|
3640
3797
|
for (const deletion of pendingMirrorDeletes.values()) {
|
|
@@ -3990,6 +4147,7 @@ async function startWorker(options) {
|
|
|
3990
4147
|
...result.conflictPaths ? { conflictPaths: result.conflictPaths } : {},
|
|
3991
4148
|
...result.conflictSnapshotRefs ? { conflictSnapshotRefs: result.conflictSnapshotRefs } : {},
|
|
3992
4149
|
...result.conflictKind ? { conflictKind: result.conflictKind } : {},
|
|
4150
|
+
...result.verifiedAncestorHeads ? { verifiedAncestorHeads: result.verifiedAncestorHeads } : {},
|
|
3993
4151
|
...result.error ? { error: result.error } : {}
|
|
3994
4152
|
};
|
|
3995
4153
|
};
|
|
@@ -4014,9 +4172,9 @@ async function startWorker(options) {
|
|
|
4014
4172
|
if (!workspaceRemoteUrl || !workspaceCredentialHelper || !workspaceCredentialUsername || !workspaceGitIdentity) {
|
|
4015
4173
|
throw new Error("Worker workspace configuration has not been received");
|
|
4016
4174
|
}
|
|
4017
|
-
const
|
|
4175
|
+
const configuredMounts = buildWorkspaceMounts();
|
|
4018
4176
|
if (input.resetToCanonical) {
|
|
4019
|
-
const resetHasBusyLiveMount =
|
|
4177
|
+
const resetHasBusyLiveMount = configuredMounts.some(
|
|
4020
4178
|
(mount) => !(mount.deleteWhenSourceMissing && !fs.existsSync(mount.sourcePath)) && mount.busy?.()
|
|
4021
4179
|
);
|
|
4022
4180
|
if (resetHasBusyLiveMount) {
|
|
@@ -4032,7 +4190,7 @@ async function startWorker(options) {
|
|
|
4032
4190
|
credentialHelper: workspaceCredentialHelper,
|
|
4033
4191
|
credentialUsername: workspaceCredentialUsername,
|
|
4034
4192
|
gitIdentity: workspaceGitIdentity,
|
|
4035
|
-
mounts
|
|
4193
|
+
mounts: configuredMounts
|
|
4036
4194
|
});
|
|
4037
4195
|
applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads2, reset.activeMountIds, true);
|
|
4038
4196
|
return {
|
|
@@ -4056,6 +4214,7 @@ async function startWorker(options) {
|
|
|
4056
4214
|
};
|
|
4057
4215
|
}
|
|
4058
4216
|
const outerRemediation = input.trigger.type === "remediation" || input.trigger.type === "remediation_confirm";
|
|
4217
|
+
const mounts = outerRemediation ? workspaceGitMountsForRemediation(configuredMounts) : configuredMounts;
|
|
4059
4218
|
const observedProjectHeads = outerRemediation ? [] : observeProjectHeadsBeforeOuterWorkspace({ allowNonFastForward: false, failClosed: false });
|
|
4060
4219
|
const inboundMoveMountIds = new Set(
|
|
4061
4220
|
observedProjectHeads.flatMap(
|
|
@@ -4075,10 +4234,13 @@ async function startWorker(options) {
|
|
|
4075
4234
|
commitDetail: input.confirmationReason ?? input.trigger.detail,
|
|
4076
4235
|
allowLargeDiff: input.confirmedLargeDiff,
|
|
4077
4236
|
skipMountMirror: outerRemediation,
|
|
4237
|
+
requiredAncestorHeads: input.requiredAncestorHeads,
|
|
4238
|
+
assertStillAdmitted: input.assertStillAdmitted,
|
|
4078
4239
|
afterWorkspacePublished: outerRemediation ? void 0 : ({ activeMountIds, publishedHead }) => {
|
|
4079
4240
|
pushChangedProjectHeads(activeMountIds, publishedHead, publicationHeads, inboundMoveMountIds);
|
|
4080
4241
|
}
|
|
4081
4242
|
});
|
|
4243
|
+
input.assertStillAdmitted?.();
|
|
4082
4244
|
if (["no_change", "updated", "pushed"].includes(result.outcome) && !outerRemediation) {
|
|
4083
4245
|
applyObservedProjectHeadsAfterInboundWorkspace(observedProjectHeads, result.activeMountIds, false);
|
|
4084
4246
|
}
|
|
@@ -4101,6 +4263,28 @@ async function startWorker(options) {
|
|
|
4101
4263
|
const runWorkspaceSync = async (input) => {
|
|
4102
4264
|
const attemptId = input.attemptId ?? crypto.randomUUID();
|
|
4103
4265
|
const requestedAt = Date.now();
|
|
4266
|
+
const admittedGeneration = workerAdmissionGeneration;
|
|
4267
|
+
const admittedActiveIncidentId = activeWorkspaceIncidentId;
|
|
4268
|
+
const admittedDeferredIncidentId = deferredWorkspaceConfiguration?.incidentId ?? null;
|
|
4269
|
+
const assertStillAdmitted = () => {
|
|
4270
|
+
if (admittedGeneration !== workerAdmissionGeneration || admittedActiveIncidentId !== activeWorkspaceIncidentId || admittedDeferredIncidentId !== (deferredWorkspaceConfiguration?.incidentId ?? null) || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN || !deferredWorkspaceSyncTriggerIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, input.trigger)) {
|
|
4271
|
+
throw new Error("Workspace synchronization admission changed while asynchronous work was in flight");
|
|
4272
|
+
}
|
|
4273
|
+
};
|
|
4274
|
+
if (!deferredWorkspaceSyncTriggerIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, input.trigger)) {
|
|
4275
|
+
const result2 = {
|
|
4276
|
+
...failedWorkspaceSyncResult(
|
|
4277
|
+
attemptId,
|
|
4278
|
+
input.trigger,
|
|
4279
|
+
new Error(
|
|
4280
|
+
`Workspace synchronization is deferred for incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId ?? "unknown"}; only remediation synchronization is allowed`
|
|
4281
|
+
)
|
|
4282
|
+
),
|
|
4283
|
+
telemetry: { totalMs: 0, queueMs: 0, prepareMs: 0, synchronizeMs: 0 }
|
|
4284
|
+
};
|
|
4285
|
+
if (input.sendResult !== false) sendWorkspaceSyncResult(input.requestId, result2);
|
|
4286
|
+
return result2;
|
|
4287
|
+
}
|
|
4104
4288
|
if (!input.requestId && input.sendResult !== false) {
|
|
4105
4289
|
if (currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
4106
4290
|
throw new Error("Cannot start autonomous workspace synchronization without an open worker control socket");
|
|
@@ -4115,23 +4299,32 @@ async function startWorker(options) {
|
|
|
4115
4299
|
result = await workspaceSyncSingleFlight.runExclusive(async () => {
|
|
4116
4300
|
queueEnteredAt = Date.now();
|
|
4117
4301
|
syncStartedAt = Date.now();
|
|
4302
|
+
if (!deferredWorkspaceSyncTriggerIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, input.trigger)) {
|
|
4303
|
+
return failedWorkspaceSyncResult(
|
|
4304
|
+
attemptId,
|
|
4305
|
+
input.trigger,
|
|
4306
|
+
new Error(
|
|
4307
|
+
`Workspace synchronization was fenced while queued by incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId ?? "unknown"}`
|
|
4308
|
+
)
|
|
4309
|
+
);
|
|
4310
|
+
}
|
|
4118
4311
|
try {
|
|
4312
|
+
assertStillAdmitted();
|
|
4119
4313
|
return await performWorkspaceSync({
|
|
4120
4314
|
attemptId,
|
|
4121
4315
|
trigger: input.trigger,
|
|
4122
4316
|
confirmedLargeDiff: input.confirmedLargeDiff,
|
|
4123
4317
|
confirmationReason: input.confirmationReason,
|
|
4124
|
-
resetToCanonical: input.resetToCanonical
|
|
4318
|
+
resetToCanonical: input.resetToCanonical,
|
|
4319
|
+
requiredAncestorHeads: input.requiredAncestorHeads,
|
|
4320
|
+
assertStillAdmitted
|
|
4125
4321
|
});
|
|
4126
4322
|
} catch (error) {
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
hydrationCurrent = false;
|
|
4133
|
-
}
|
|
4134
|
-
}
|
|
4323
|
+
const hydrationCurrent = workspaceSyncFailureHydrationIsSafe({
|
|
4324
|
+
error,
|
|
4325
|
+
resetToCanonical: input.resetToCanonical === true,
|
|
4326
|
+
inspectCurrentHydration: () => workspaceGitHydrationIsCurrent(workspaceShadowRoot, buildWorkspaceMounts())
|
|
4327
|
+
});
|
|
4135
4328
|
if (!hydrationCurrent) {
|
|
4136
4329
|
process.stderr.write(
|
|
4137
4330
|
`[r5d-worker] workspace synchronization failed with an incompletely hydrated visible tree; exiting for exact recovery: ${error instanceof Error ? error.message : String(error)}
|
|
@@ -4173,7 +4366,7 @@ async function startWorker(options) {
|
|
|
4173
4366
|
pendingCreatedBranchPublicationNotBefore
|
|
4174
4367
|
);
|
|
4175
4368
|
const initialDeferral = pendingBranchDeferral();
|
|
4176
|
-
if (!
|
|
4369
|
+
if (!automaticWorkspaceSyncIsCurrentlyAdmitted() || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN || automaticSyncInFlight || initialDeferral?.kind === "active_target") {
|
|
4177
4370
|
return;
|
|
4178
4371
|
}
|
|
4179
4372
|
if (workspaceAutomaticTimer) clearTimeout(workspaceAutomaticTimer);
|
|
@@ -4181,7 +4374,7 @@ async function startWorker(options) {
|
|
|
4181
4374
|
() => {
|
|
4182
4375
|
workspaceAutomaticTimer = void 0;
|
|
4183
4376
|
const scheduledTrigger = pendingAutomaticTrigger;
|
|
4184
|
-
if (!scheduledTrigger || !
|
|
4377
|
+
if (!scheduledTrigger || !automaticWorkspaceSyncIsCurrentlyAdmitted() || currentWorkerSocket !== ws) return;
|
|
4185
4378
|
const currentDeferral = pendingBranchDeferral();
|
|
4186
4379
|
if (currentDeferral?.kind === "active_target") return;
|
|
4187
4380
|
if (currentDeferral?.kind === "creation_grace") {
|
|
@@ -4203,7 +4396,7 @@ async function startWorker(options) {
|
|
|
4203
4396
|
}).finally(() => {
|
|
4204
4397
|
automaticSyncInFlight = false;
|
|
4205
4398
|
heartbeatBusyGrace = grantWorkerHeartbeatBusyGrace(lastServerHeartbeatAt, heartbeatBusyGrace);
|
|
4206
|
-
if (pendingAutomaticTrigger &&
|
|
4399
|
+
if (pendingAutomaticTrigger && automaticWorkspaceSyncIsCurrentlyAdmitted()) {
|
|
4207
4400
|
scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger, WORKSPACE_GIT_QUIET_MS);
|
|
4208
4401
|
}
|
|
4209
4402
|
});
|
|
@@ -4219,6 +4412,11 @@ async function startWorker(options) {
|
|
|
4219
4412
|
const targetMayMutateVisibleWorkspace = (target) => target.type === "project" || target.rootProfile === "visible_projects";
|
|
4220
4413
|
const resolveMessageTarget = (target) => {
|
|
4221
4414
|
if (!workspaceConfigured) throw new Error("Worker workspace configuration has not completed successfully");
|
|
4415
|
+
if (!deferredWorkspaceTargetIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, target)) {
|
|
4416
|
+
throw new Error(
|
|
4417
|
+
`Worker workspace configuration is fenced for incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId ?? "unknown"}; only canonical remediation commands are allowed`
|
|
4418
|
+
);
|
|
4419
|
+
}
|
|
4222
4420
|
if (target.type === "project" && !readyProjectIds.has(target.projectId)) {
|
|
4223
4421
|
throw new Error(`Project ${target.projectId} is not ready on this worker`);
|
|
4224
4422
|
}
|
|
@@ -4232,7 +4430,26 @@ async function startWorker(options) {
|
|
|
4232
4430
|
projectConfigById
|
|
4233
4431
|
});
|
|
4234
4432
|
};
|
|
4235
|
-
const configureWorkerWorkspace = async (message) => {
|
|
4433
|
+
const configureWorkerWorkspace = async (message, receiptGeneration) => {
|
|
4434
|
+
const incidentDeferral = workspaceConfigurationIncidentDeferral({
|
|
4435
|
+
requestedIncidentId: message.deferWorkspaceSyncForIncidentId,
|
|
4436
|
+
activeIncidentId: activeWorkspaceIncidentId
|
|
4437
|
+
});
|
|
4438
|
+
if (incidentDeferral.error) {
|
|
4439
|
+
workspaceConfigured = false;
|
|
4440
|
+
return {
|
|
4441
|
+
result: failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, incidentDeferral.error),
|
|
4442
|
+
pending: incidentDeferredProjectCheckouts(message.projects),
|
|
4443
|
+
aheadOfOriginBranches: []
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4446
|
+
if (incidentDeferral.incidentId) {
|
|
4447
|
+
workspaceConfigured = false;
|
|
4448
|
+
deferredWorkspaceConfiguration = {
|
|
4449
|
+
incidentId: incidentDeferral.incidentId,
|
|
4450
|
+
serverRefreshExpected: message.deferWorkspaceSyncForIncidentId === incidentDeferral.incidentId
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4236
4453
|
const incomingCredentialGenerationFingerprint = credentialGenerationFingerprint({
|
|
4237
4454
|
...message,
|
|
4238
4455
|
workerBaseUrl: baseUrl,
|
|
@@ -4243,6 +4460,22 @@ async function startWorker(options) {
|
|
|
4243
4460
|
pendingFingerprintAtProcessStart: pendingCredentialGenerationIntentAtProcessStart?.fingerprint ?? null,
|
|
4244
4461
|
incomingFingerprint: incomingCredentialGenerationFingerprint
|
|
4245
4462
|
});
|
|
4463
|
+
if (deferredCredentialTransitionMustWait({
|
|
4464
|
+
incidentId: incidentDeferral.incidentId,
|
|
4465
|
+
transitionPhase: credentialTransitionPhase,
|
|
4466
|
+
canonicalRemediationActive: canonicalWorkspaceMutationIsActive(activeWorkspaceMutationTargets())
|
|
4467
|
+
})) {
|
|
4468
|
+
return {
|
|
4469
|
+
result: failedWorkspaceSyncResult(
|
|
4470
|
+
crypto.randomUUID(),
|
|
4471
|
+
{ type: "connect" },
|
|
4472
|
+
new Error("Workspace credential rotation is deferred until the active canonical remediation command finishes")
|
|
4473
|
+
),
|
|
4474
|
+
pending: incidentDeferredProjectCheckouts(message.projects),
|
|
4475
|
+
aheadOfOriginBranches: [],
|
|
4476
|
+
...incidentDeferral.incidentId ? { deferredWorkspaceSyncForIncidentId: incidentDeferral.incidentId } : {}
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4246
4479
|
const credentialReapStatus = credentialReapContractStatus();
|
|
4247
4480
|
const verifiedCredentialReapContractNow = credentialReapStatus === "verified_systemd";
|
|
4248
4481
|
const credentialRestartIsContained = credentialGenerationRestartIsContained({
|
|
@@ -4356,6 +4589,12 @@ async function startWorker(options) {
|
|
|
4356
4589
|
workspaceSyncRequestsInFlight += 1;
|
|
4357
4590
|
try {
|
|
4358
4591
|
return await workspaceSyncSingleFlight.runExclusive(async () => {
|
|
4592
|
+
if (receiptGeneration !== workspaceConfigurationReceiptGeneration) {
|
|
4593
|
+
throw new Error("Workspace configuration was superseded by a newer server generation");
|
|
4594
|
+
}
|
|
4595
|
+
if (!workspaceConfigurationIncidentSnapshotIsCurrent(incidentDeferral.incidentId, activeWorkspaceIncidentId)) {
|
|
4596
|
+
throw new Error("Workspace configuration was superseded by a different active workspace incident");
|
|
4597
|
+
}
|
|
4359
4598
|
for (const project of message.projects) assertRepositoryTransitionState(project);
|
|
4360
4599
|
const busyConfigurationChanges = busyProjectConfigurationChangeIds({
|
|
4361
4600
|
currentProjects: [...projectConfigById.values()],
|
|
@@ -4377,10 +4616,12 @@ async function startWorker(options) {
|
|
|
4377
4616
|
const preserveOnlyBranches = message.projects.flatMap(
|
|
4378
4617
|
(project) => project.preserveOnlyBranches.map(({ branchId, branchName }) => ({ branchId, projectId: project.projectId, branchName }))
|
|
4379
4618
|
);
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4619
|
+
if (!incidentDeferral.incidentId) {
|
|
4620
|
+
projectWorkspaceState = projectWorkspaceStateStore.reconcile({
|
|
4621
|
+
desiredProjects: message.projects,
|
|
4622
|
+
preserveOnlyBranches
|
|
4623
|
+
});
|
|
4624
|
+
}
|
|
4384
4625
|
const stillPendingCreatedBranches = new Map(
|
|
4385
4626
|
projectWorkspaceState.locallyPendingCreatedBranches.map((branch) => [
|
|
4386
4627
|
pendingCreatedBranchKey(branch.projectId, branch.branchName),
|
|
@@ -4440,6 +4681,12 @@ async function startWorker(options) {
|
|
|
4440
4681
|
void 0,
|
|
4441
4682
|
credentialPublicationPreauthorized
|
|
4442
4683
|
);
|
|
4684
|
+
if (receiptGeneration !== workspaceConfigurationReceiptGeneration) {
|
|
4685
|
+
throw new Error("Workspace configuration was superseded by a newer server generation");
|
|
4686
|
+
}
|
|
4687
|
+
if (!workspaceConfigurationIncidentSnapshotIsCurrent(incidentDeferral.incidentId, activeWorkspaceIncidentId)) {
|
|
4688
|
+
throw new Error("Workspace configuration was superseded by a different active workspace incident");
|
|
4689
|
+
}
|
|
4443
4690
|
configuredCredentialGenerationFingerprint = incomingCredentialGenerationFingerprint;
|
|
4444
4691
|
pendingCredentialGenerationIntentAtProcessStart = null;
|
|
4445
4692
|
bootstrapCredentialGenerationFingerprintAtProcessStart = null;
|
|
@@ -4454,6 +4701,34 @@ async function startWorker(options) {
|
|
|
4454
4701
|
branches: project.branches.filter(({ branchName }) => !pendingMirrorDeletes.has(projectBranchKey(project.projectId, branchName)))
|
|
4455
4702
|
}));
|
|
4456
4703
|
const nextProjectConfigById = new Map(effectiveProjects.map((project) => [project.projectId, project]));
|
|
4704
|
+
if (incidentDeferral.incidentId) {
|
|
4705
|
+
configureExistingWorkspaceGitForRemediation({
|
|
4706
|
+
workspacePath: workspaceShadowRoot,
|
|
4707
|
+
remoteUrl: message.workspaceRemoteUrl,
|
|
4708
|
+
credentialHelper: nextWorkspaceCredentialHelper,
|
|
4709
|
+
credentialUsername: workerCredentialUsername,
|
|
4710
|
+
gitIdentity: message.gitIdentity
|
|
4711
|
+
});
|
|
4712
|
+
projectConfigById.clear();
|
|
4713
|
+
for (const project of effectiveProjects) projectConfigById.set(project.projectId, project);
|
|
4714
|
+
readyProjectIds.clear();
|
|
4715
|
+
reconciledProjectConfigFingerprints.clear();
|
|
4716
|
+
pendingCheckouts.clear();
|
|
4717
|
+
const pending2 = incidentDeferredProjectCheckouts(message.projects);
|
|
4718
|
+
for (const checkout of pending2) pendingCheckouts.set(projectBranchKey(checkout.projectId, checkout.branchName), checkout);
|
|
4719
|
+
workspaceConfigured = activeWorkspaceIncidentId === incidentDeferral.incidentId;
|
|
4720
|
+
return {
|
|
4721
|
+
result: deferredIncidentWorkspaceConfigurationResult({
|
|
4722
|
+
attemptId: crypto.randomUUID(),
|
|
4723
|
+
workerLabel: label,
|
|
4724
|
+
head: workspaceLocalHead(),
|
|
4725
|
+
skippedMountIds: buildWorkspaceMounts().map(({ id }) => id)
|
|
4726
|
+
}),
|
|
4727
|
+
pending: pending2,
|
|
4728
|
+
aheadOfOriginBranches: [],
|
|
4729
|
+
deferredWorkspaceSyncForIncidentId: incidentDeferral.incidentId
|
|
4730
|
+
};
|
|
4731
|
+
}
|
|
4457
4732
|
ensureWorkspaceGitClone({
|
|
4458
4733
|
workspacePath: workspaceShadowRoot,
|
|
4459
4734
|
remoteUrl: message.workspaceRemoteUrl,
|
|
@@ -4521,10 +4796,25 @@ async function startWorker(options) {
|
|
|
4521
4796
|
{ ignoreBusy: true }
|
|
4522
4797
|
);
|
|
4523
4798
|
ensureConfiguredProjects();
|
|
4799
|
+
const configurationSyncAdmissionGeneration = workerAdmissionGeneration;
|
|
4800
|
+
const assertConfigurationSyncStillAdmitted = () => {
|
|
4801
|
+
if (receiptGeneration !== workspaceConfigurationReceiptGeneration || configurationSyncAdmissionGeneration !== workerAdmissionGeneration || !workspaceConfigurationIncidentSnapshotIsCurrent(incidentDeferral.incidentId, activeWorkspaceIncidentId) || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
4802
|
+
throw new Error("Workspace configuration synchronization admission changed while asynchronous work was in flight");
|
|
4803
|
+
}
|
|
4804
|
+
};
|
|
4524
4805
|
const result = await performWorkspaceSync({
|
|
4525
4806
|
attemptId: crypto.randomUUID(),
|
|
4526
|
-
trigger: { type: "connect" }
|
|
4807
|
+
trigger: { type: "connect" },
|
|
4808
|
+
resetToCanonical: workspaceConfigurationResetToCanonicalIsAllowed(message.resetToCanonical, incidentDeferral.incidentId),
|
|
4809
|
+
assertStillAdmitted: assertConfigurationSyncStillAdmitted
|
|
4527
4810
|
});
|
|
4811
|
+
assertConfigurationSyncStillAdmitted();
|
|
4812
|
+
if (receiptGeneration !== workspaceConfigurationReceiptGeneration) {
|
|
4813
|
+
throw new Error("Workspace configuration was superseded by a newer server generation");
|
|
4814
|
+
}
|
|
4815
|
+
if (!workspaceConfigurationIncidentSnapshotIsCurrent(incidentDeferral.incidentId, activeWorkspaceIncidentId)) {
|
|
4816
|
+
throw new Error("Workspace configuration was superseded by a different active workspace incident");
|
|
4817
|
+
}
|
|
4528
4818
|
const publishedHead = result.publishedHead ?? result.localHead ?? result.startingHead;
|
|
4529
4819
|
if (publishedHead && ["no_change", "published", "updated", "conflict_reset", "reset"].includes(result.outcome)) {
|
|
4530
4820
|
const activeMountIds = new Set(result.activeMountIds ?? []);
|
|
@@ -4541,6 +4831,11 @@ async function startWorker(options) {
|
|
|
4541
4831
|
});
|
|
4542
4832
|
}
|
|
4543
4833
|
}
|
|
4834
|
+
if (deferredWorkspaceConfigurationRefreshTimer) {
|
|
4835
|
+
clearTimeout(deferredWorkspaceConfigurationRefreshTimer);
|
|
4836
|
+
deferredWorkspaceConfigurationRefreshTimer = void 0;
|
|
4837
|
+
}
|
|
4838
|
+
deferredWorkspaceConfiguration = null;
|
|
4544
4839
|
workspaceConfigured = true;
|
|
4545
4840
|
const pending = [...pendingCheckouts.values()].sort(
|
|
4546
4841
|
(left, right) => left.projectId.localeCompare(right.projectId) || left.branchName.localeCompare(right.branchName)
|
|
@@ -4548,6 +4843,13 @@ async function startWorker(options) {
|
|
|
4548
4843
|
return { result, pending, aheadOfOriginBranches: collectAheadOfOriginBranches() };
|
|
4549
4844
|
});
|
|
4550
4845
|
} catch (error) {
|
|
4846
|
+
if (receiptGeneration !== workspaceConfigurationReceiptGeneration) {
|
|
4847
|
+
return {
|
|
4848
|
+
result: failedWorkspaceSyncResult(crypto.randomUUID(), { type: "connect" }, error),
|
|
4849
|
+
pending: incidentDeferredProjectCheckouts(message.projects),
|
|
4850
|
+
aheadOfOriginBranches: []
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4551
4853
|
if (error instanceof RegistryAuthConfigurationError) {
|
|
4552
4854
|
workspaceConfigured = false;
|
|
4553
4855
|
githubCredential = null;
|
|
@@ -4602,7 +4904,7 @@ async function startWorker(options) {
|
|
|
4602
4904
|
workspaceAutomaticTimer = void 0;
|
|
4603
4905
|
}
|
|
4604
4906
|
void (async () => {
|
|
4605
|
-
if (
|
|
4907
|
+
if (automaticWorkspaceSyncIsCurrentlyAdmitted()) {
|
|
4606
4908
|
await runWorkspaceSync({
|
|
4607
4909
|
trigger: { type: "manual", detail: "graceful worker shutdown" }
|
|
4608
4910
|
});
|
|
@@ -4651,7 +4953,10 @@ async function startWorker(options) {
|
|
|
4651
4953
|
updateClis: true,
|
|
4652
4954
|
browserPortForwarding: true,
|
|
4653
4955
|
execStdinV1: true,
|
|
4654
|
-
ptyEnvFilesV1: true
|
|
4956
|
+
ptyEnvFilesV1: true,
|
|
4957
|
+
workspaceRemediationAncestorGuardV1: true,
|
|
4958
|
+
workspaceIncidentConfigDeferralV1: true,
|
|
4959
|
+
workspaceConfigResetToCanonicalV1: true
|
|
4655
4960
|
},
|
|
4656
4961
|
projectRoot: projectsRoot,
|
|
4657
4962
|
artifactRoot,
|
|
@@ -4687,26 +4992,35 @@ async function startWorker(options) {
|
|
|
4687
4992
|
return;
|
|
4688
4993
|
}
|
|
4689
4994
|
if (message.type === "workspace_config") {
|
|
4995
|
+
const receiptGeneration = ++workspaceConfigurationReceiptGeneration;
|
|
4996
|
+
const refreshesDeferredConfiguration = deferredWorkspaceConfiguration !== null && activeWorkspaceIncidentId === null && message.deferWorkspaceSyncForIncidentId === void 0;
|
|
4690
4997
|
advanceWorkerAdmissionGeneration();
|
|
4691
|
-
const configured = await configureWorkerWorkspace(message);
|
|
4998
|
+
const configured = await configureWorkerWorkspace(message, receiptGeneration);
|
|
4692
4999
|
sendWorkerMessageFromCurrentSource(ws, {
|
|
4693
5000
|
type: "workspace_configured",
|
|
4694
5001
|
requestId: message.requestId,
|
|
4695
5002
|
result: configured.result,
|
|
4696
5003
|
pendingCheckouts: configured.pending,
|
|
4697
|
-
aheadOfOriginBranches: configured.aheadOfOriginBranches
|
|
5004
|
+
aheadOfOriginBranches: configured.aheadOfOriginBranches,
|
|
5005
|
+
...configured.deferredWorkspaceSyncForIncidentId ? { deferredWorkspaceSyncForIncidentId: configured.deferredWorkspaceSyncForIncidentId } : {}
|
|
4698
5006
|
});
|
|
4699
5007
|
process.stdout.write(
|
|
4700
5008
|
`[r5d-worker] workspace configured: ${message.projects.length} project(s), ${configured.pending.length} pending checkout(s)
|
|
4701
5009
|
`
|
|
4702
5010
|
);
|
|
5011
|
+
if (refreshesDeferredConfiguration && configured.result.outcome === "failed" && deferredWorkspaceConfiguration !== null && currentWorkerSocket === ws) {
|
|
5012
|
+
workspaceConfigured = false;
|
|
5013
|
+
advanceWorkerAdmissionGeneration();
|
|
5014
|
+
ws.close(1012, "Deferred workspace configuration refresh failed");
|
|
5015
|
+
return;
|
|
5016
|
+
}
|
|
4703
5017
|
if (!workspacePeriodicTimer) {
|
|
4704
5018
|
workspacePeriodicTimer = setInterval(() => {
|
|
4705
5019
|
scheduleAutomaticWorkspaceSync({ type: "periodic", detail: "periodic workspace reconciliation" }, 0);
|
|
4706
5020
|
}, WORKSPACE_GIT_PERIODIC_MS);
|
|
4707
5021
|
workspacePeriodicTimer.unref();
|
|
4708
5022
|
}
|
|
4709
|
-
if (pendingAutomaticTrigger &&
|
|
5023
|
+
if (pendingAutomaticTrigger && automaticWorkspaceSyncIsCurrentlyAdmitted()) {
|
|
4710
5024
|
scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger, 0);
|
|
4711
5025
|
}
|
|
4712
5026
|
return;
|
|
@@ -4718,11 +5032,20 @@ async function startWorker(options) {
|
|
|
4718
5032
|
trigger: message.trigger,
|
|
4719
5033
|
confirmedLargeDiff: message.confirmedLargeDiff,
|
|
4720
5034
|
confirmationReason: message.confirmationReason,
|
|
4721
|
-
resetToCanonical: message.resetToCanonical
|
|
5035
|
+
resetToCanonical: message.resetToCanonical,
|
|
5036
|
+
requiredAncestorHeads: message.requiredAncestorHeads
|
|
4722
5037
|
});
|
|
4723
5038
|
return;
|
|
4724
5039
|
}
|
|
4725
5040
|
if (message.type === "create_project_branch") {
|
|
5041
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5042
|
+
sendWorkerMessage(ws, {
|
|
5043
|
+
type: "operation_result",
|
|
5044
|
+
requestId: message.requestId,
|
|
5045
|
+
error: `Project operations are deferred for workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5046
|
+
});
|
|
5047
|
+
return;
|
|
5048
|
+
}
|
|
4726
5049
|
try {
|
|
4727
5050
|
const pendingBranch = {
|
|
4728
5051
|
branchId: message.branchId,
|
|
@@ -4730,6 +5053,11 @@ async function startWorker(options) {
|
|
|
4730
5053
|
branchName: message.targetBranch
|
|
4731
5054
|
};
|
|
4732
5055
|
const created = await workspaceSyncSingleFlight.runMutation(() => {
|
|
5056
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5057
|
+
throw new Error(
|
|
5058
|
+
`Project branch creation was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5059
|
+
);
|
|
5060
|
+
}
|
|
4733
5061
|
const project = projectConfigById.get(message.projectId);
|
|
4734
5062
|
if (!project) throw new Error(`Project ${message.projectId} is missing from the worker workspace configuration`);
|
|
4735
5063
|
assertRepositoryExecutionEnabled(project);
|
|
@@ -4813,9 +5141,22 @@ async function startWorker(options) {
|
|
|
4813
5141
|
return;
|
|
4814
5142
|
}
|
|
4815
5143
|
if (message.type === "delete_project_branch") {
|
|
5144
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5145
|
+
sendWorkerMessage(ws, {
|
|
5146
|
+
type: "operation_result",
|
|
5147
|
+
requestId: message.requestId,
|
|
5148
|
+
error: `Project operations are deferred for workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5149
|
+
});
|
|
5150
|
+
return;
|
|
5151
|
+
}
|
|
4816
5152
|
try {
|
|
4817
5153
|
const deletionKey = projectBranchKey(message.projectId, message.branchName);
|
|
4818
5154
|
const deletionNeedsSync = await workspaceSyncSingleFlight.runMutation(() => {
|
|
5155
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5156
|
+
throw new Error(
|
|
5157
|
+
`Project branch deletion was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5158
|
+
);
|
|
5159
|
+
}
|
|
4819
5160
|
const project = projectConfigById.get(message.projectId);
|
|
4820
5161
|
if (!project) throw new Error(`Project ${message.projectId} is missing from the worker workspace configuration`);
|
|
4821
5162
|
assertRepositoryExecutionEnabled(project);
|
|
@@ -4915,9 +5256,37 @@ async function startWorker(options) {
|
|
|
4915
5256
|
}
|
|
4916
5257
|
if (message.type === "workspace_incident_updated") {
|
|
4917
5258
|
const previousIncidentId = activeWorkspaceIncidentId;
|
|
4918
|
-
|
|
5259
|
+
const nextIncidentId = applyWorkspaceIncidentUpdate(activeWorkspaceIncidentId, message);
|
|
5260
|
+
if (nextIncidentId !== previousIncidentId) advanceWorkerAdmissionGeneration();
|
|
5261
|
+
activeWorkspaceIncidentId = nextIncidentId;
|
|
4919
5262
|
if (previousIncidentId && !activeWorkspaceIncidentId && (message.status === "resolved" || message.status === "confirmed" || message.status === "reset")) {
|
|
4920
|
-
|
|
5263
|
+
const refresh = workspaceIncidentTerminalRefreshFence({
|
|
5264
|
+
deferredConfiguration: deferredWorkspaceConfiguration,
|
|
5265
|
+
clearedIncidentId: previousIncidentId
|
|
5266
|
+
});
|
|
5267
|
+
deferredWorkspaceConfiguration = refresh.deferredConfiguration;
|
|
5268
|
+
pendingAutomaticTrigger ??= { type: "periodic", detail: "resume after workspace incident" };
|
|
5269
|
+
workspaceConfigured = false;
|
|
5270
|
+
advanceWorkerAdmissionGeneration();
|
|
5271
|
+
if (refresh.disposition === "reconnect_for_refresh") {
|
|
5272
|
+
ws.close(1012, "Refreshing deferred workspace configuration");
|
|
5273
|
+
} else {
|
|
5274
|
+
if (deferredWorkspaceConfigurationRefreshTimer) clearTimeout(deferredWorkspaceConfigurationRefreshTimer);
|
|
5275
|
+
const deferredIncidentId = deferredWorkspaceConfiguration.incidentId;
|
|
5276
|
+
deferredWorkspaceConfigurationRefreshTimer = setTimeout(() => {
|
|
5277
|
+
deferredWorkspaceConfigurationRefreshTimer = void 0;
|
|
5278
|
+
if (!deferredWorkspaceRefreshWatchdogIsCurrent({
|
|
5279
|
+
capturedIncidentId: deferredIncidentId,
|
|
5280
|
+
deferredConfiguration: deferredWorkspaceConfiguration,
|
|
5281
|
+
activeIncidentId: activeWorkspaceIncidentId
|
|
5282
|
+
}) || currentWorkerSocket !== ws || ws.readyState !== WebSocket.OPEN) {
|
|
5283
|
+
return;
|
|
5284
|
+
}
|
|
5285
|
+
ws.close(1012, "Timed out waiting for deferred workspace configuration refresh");
|
|
5286
|
+
}, WORKSPACE_INCIDENT_CONFIG_REFRESH_TIMEOUT_MS);
|
|
5287
|
+
deferredWorkspaceConfigurationRefreshTimer.unref();
|
|
5288
|
+
}
|
|
5289
|
+
return;
|
|
4921
5290
|
}
|
|
4922
5291
|
return;
|
|
4923
5292
|
}
|
|
@@ -5020,6 +5389,12 @@ async function startWorker(options) {
|
|
|
5020
5389
|
sendAck({ error: `Process run ${message.runId} is not active on this worker` });
|
|
5021
5390
|
return;
|
|
5022
5391
|
}
|
|
5392
|
+
if (!deferredWorkspaceTargetIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, active.target)) {
|
|
5393
|
+
sendAck({
|
|
5394
|
+
error: `Process input is deferred for workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId ?? "unknown"}`
|
|
5395
|
+
});
|
|
5396
|
+
return;
|
|
5397
|
+
}
|
|
5023
5398
|
if (!active.interactive || !active.stdin) {
|
|
5024
5399
|
sendAck({
|
|
5025
5400
|
error: `Process run ${message.runId} has no open stdin. Start a new shell command with "interactive": true to write to its stdin.`
|
|
@@ -5065,6 +5440,15 @@ async function startWorker(options) {
|
|
|
5065
5440
|
});
|
|
5066
5441
|
return;
|
|
5067
5442
|
}
|
|
5443
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5444
|
+
sendWorkerMessage(ws, {
|
|
5445
|
+
type: "pty_error",
|
|
5446
|
+
requestId: message.requestId,
|
|
5447
|
+
ptyId: message.ptyId,
|
|
5448
|
+
error: `Shells are deferred for workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5449
|
+
});
|
|
5450
|
+
return;
|
|
5451
|
+
}
|
|
5068
5452
|
await reserveWorkspaceCommandAfterCurrentSync(message.target, workspaceSyncSingleFlight, () => {
|
|
5069
5453
|
workspaceSyncPriorityPtyTargets.set(message.ptyId, message.target);
|
|
5070
5454
|
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
@@ -5073,6 +5457,11 @@ async function startWorker(options) {
|
|
|
5073
5457
|
let mutationLeaseTransferred = false;
|
|
5074
5458
|
try {
|
|
5075
5459
|
releaseWorkspaceMutation = await acquireWorkspaceCommandMutation(message.target, workspaceSyncSingleFlight);
|
|
5460
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5461
|
+
throw new Error(
|
|
5462
|
+
`Shell opening was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5463
|
+
);
|
|
5464
|
+
}
|
|
5076
5465
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
5077
5466
|
process.stdout.write(`[r5d-worker] pty ${message.ptyId}: ${describeWorkerSessionTarget(message.target)}
|
|
5078
5467
|
`);
|
|
@@ -5105,6 +5494,14 @@ async function startWorker(options) {
|
|
|
5105
5494
|
}
|
|
5106
5495
|
if (message.type === "pty_input") {
|
|
5107
5496
|
const activePty = activePtys.get(message.ptyId);
|
|
5497
|
+
if (activePty && workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5498
|
+
sendWorkerMessage(ws, {
|
|
5499
|
+
type: "pty_error",
|
|
5500
|
+
ptyId: message.ptyId,
|
|
5501
|
+
error: `Shell input is deferred for workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId ?? "unknown"}`
|
|
5502
|
+
});
|
|
5503
|
+
return;
|
|
5504
|
+
}
|
|
5108
5505
|
if (activePty && targetMayMutateVisibleWorkspace(activePty.target)) {
|
|
5109
5506
|
markWorkspaceDirty({ type: "shell_inline", detail: `pty ${message.ptyId}` }, false, activePty.target);
|
|
5110
5507
|
}
|
|
@@ -5134,6 +5531,17 @@ async function startWorker(options) {
|
|
|
5134
5531
|
});
|
|
5135
5532
|
return;
|
|
5136
5533
|
}
|
|
5534
|
+
if (!workspaceCommandTransportIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, "exec")) {
|
|
5535
|
+
sendWorkerMessage(ws, {
|
|
5536
|
+
type: "exec_result",
|
|
5537
|
+
requestId: message.requestId,
|
|
5538
|
+
stdout: "",
|
|
5539
|
+
stderr: "",
|
|
5540
|
+
exitCode: 1,
|
|
5541
|
+
error: `One-shot commands are deferred for workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5542
|
+
});
|
|
5543
|
+
return;
|
|
5544
|
+
}
|
|
5137
5545
|
let result;
|
|
5138
5546
|
let targetReserved = false;
|
|
5139
5547
|
const hasWorkspaceEffect = workerCommandHasWorkspaceEffect(message);
|
|
@@ -5146,6 +5554,11 @@ async function startWorker(options) {
|
|
|
5146
5554
|
});
|
|
5147
5555
|
}
|
|
5148
5556
|
const runCommand = async () => {
|
|
5557
|
+
if (!workspaceCommandTransportIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, "exec")) {
|
|
5558
|
+
throw new Error(
|
|
5559
|
+
`One-shot command was fenced while queued by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5560
|
+
);
|
|
5561
|
+
}
|
|
5149
5562
|
const resolvedTarget = resolveMessageTarget(message.target);
|
|
5150
5563
|
process.stdout.write(`[r5d-worker] exec ${message.runId}: ${message.argv.join(" ")}
|
|
5151
5564
|
`);
|
|
@@ -5156,7 +5569,14 @@ async function startWorker(options) {
|
|
|
5156
5569
|
token,
|
|
5157
5570
|
artifactRoot,
|
|
5158
5571
|
planRoot,
|
|
5159
|
-
assertAdmission:
|
|
5572
|
+
assertAdmission: () => {
|
|
5573
|
+
if (!workspaceCommandTransportIsAllowed(deferredWorkspaceConfiguration, activeWorkspaceIncidentId, "exec")) {
|
|
5574
|
+
throw new Error(
|
|
5575
|
+
`One-shot command was fenced before spawn by workspace incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}`
|
|
5576
|
+
);
|
|
5577
|
+
}
|
|
5578
|
+
assertMessageAdmission();
|
|
5579
|
+
}
|
|
5160
5580
|
});
|
|
5161
5581
|
};
|
|
5162
5582
|
result = await runWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand);
|
|
@@ -5239,7 +5659,12 @@ async function startWorker(options) {
|
|
|
5239
5659
|
if (targetMayMutateVisibleWorkspace(message.target)) recordVisibleWorkspaceMutation(message.target);
|
|
5240
5660
|
});
|
|
5241
5661
|
}
|
|
5242
|
-
await
|
|
5662
|
+
await runReservedWorkspaceCommand(message.target, workspaceSyncSingleFlight, runCommand, () => {
|
|
5663
|
+
if (targetReserved) {
|
|
5664
|
+
workspaceSyncPriorityProcessTargets.delete(message.runId);
|
|
5665
|
+
targetReserved = false;
|
|
5666
|
+
}
|
|
5667
|
+
});
|
|
5243
5668
|
} finally {
|
|
5244
5669
|
if (targetReserved) workspaceSyncPriorityProcessTargets.delete(message.runId);
|
|
5245
5670
|
}
|
|
@@ -5256,6 +5681,14 @@ async function startWorker(options) {
|
|
|
5256
5681
|
return;
|
|
5257
5682
|
}
|
|
5258
5683
|
if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes" || message.type === "code_list" || message.type === "code_read") {
|
|
5684
|
+
if (workspaceOperationsAreFenced(deferredWorkspaceConfiguration, activeWorkspaceIncidentId)) {
|
|
5685
|
+
sendWorkerMessage(ws, {
|
|
5686
|
+
type: "operation_result",
|
|
5687
|
+
requestId: message.requestId,
|
|
5688
|
+
error: `Workspace operations are deferred for incident ${deferredWorkspaceConfiguration?.incidentId ?? activeWorkspaceIncidentId}; use a canonical remediation shell`
|
|
5689
|
+
});
|
|
5690
|
+
return;
|
|
5691
|
+
}
|
|
5259
5692
|
const reservesVisibleWorkspace = targetMayMutateVisibleWorkspace(message.target);
|
|
5260
5693
|
const mutatesVisibleWorkspace = (message.type === "write" || message.type === "edit") && targetMayMutateVisibleWorkspace(message.target);
|
|
5261
5694
|
if (reservesVisibleWorkspace) {
|
|
@@ -5274,7 +5707,8 @@ async function startWorker(options) {
|
|
|
5274
5707
|
baseUrl,
|
|
5275
5708
|
token,
|
|
5276
5709
|
artifactRoot,
|
|
5277
|
-
planRoot
|
|
5710
|
+
planRoot,
|
|
5711
|
+
assertAdmission: assertMessageAdmission
|
|
5278
5712
|
});
|
|
5279
5713
|
});
|
|
5280
5714
|
ws.send(
|
|
@@ -5304,7 +5738,7 @@ async function startWorker(options) {
|
|
|
5304
5738
|
} finally {
|
|
5305
5739
|
if (reservesVisibleWorkspace) {
|
|
5306
5740
|
workspaceSyncPriorityOperationTargets.delete(message.requestId);
|
|
5307
|
-
if (pendingAutomaticTrigger &&
|
|
5741
|
+
if (pendingAutomaticTrigger && automaticWorkspaceSyncIsCurrentlyAdmitted()) {
|
|
5308
5742
|
scheduleAutomaticWorkspaceSync(pendingAutomaticTrigger, dirtyTrigger ? WORKSPACE_GIT_QUIET_MS : 0);
|
|
5309
5743
|
}
|
|
5310
5744
|
}
|
|
@@ -5340,6 +5774,10 @@ async function startWorker(options) {
|
|
|
5340
5774
|
clearInterval(workspacePeriodicTimer);
|
|
5341
5775
|
workspacePeriodicTimer = void 0;
|
|
5342
5776
|
}
|
|
5777
|
+
if (deferredWorkspaceConfigurationRefreshTimer) {
|
|
5778
|
+
clearTimeout(deferredWorkspaceConfigurationRefreshTimer);
|
|
5779
|
+
deferredWorkspaceConfigurationRefreshTimer = void 0;
|
|
5780
|
+
}
|
|
5343
5781
|
if (terminalReplayTimer) {
|
|
5344
5782
|
clearInterval(terminalReplayTimer);
|
|
5345
5783
|
terminalReplayTimer = void 0;
|