@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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.82",
3
+ "version": "0.0.86",
4
4
  "type": "module"
5
5
  }
@@ -15,6 +15,12 @@ function projectBranchHasActiveWorkspaceTarget(projectId, branchName, activeTarg
15
15
  }
16
16
  return false;
17
17
  }
18
+ function projectBranchMountBusyDuringExclusiveSync(input) {
19
+ return input.executionDisabled || input.worktreeOperationInProgress || input.retainedCanonicalProcessGroup || projectBranchHasActiveWorkspaceTarget(input.projectId, input.branchName, input.activeTargets);
20
+ }
21
+ function workspacePlansMountBusyDuringExclusiveSync(activeTargets, retainedCanonicalProcessGroup = false) {
22
+ return retainedCanonicalProcessGroup || hasActiveVisibleProjectsWorkspaceTarget(activeTargets);
23
+ }
18
24
  function shouldDeferAutomaticWorkspaceSyncForPendingBranch(pendingCreatedBranches, activeTargets) {
19
25
  if (pendingCreatedBranches.length === 0) return false;
20
26
  const targets = [...activeTargets];
@@ -36,5 +42,7 @@ export {
36
42
  pendingCreatedBranchAutomaticSyncDeferral,
37
43
  pendingCreatedBranchKey,
38
44
  projectBranchHasActiveWorkspaceTarget,
39
- shouldDeferAutomaticWorkspaceSyncForPendingBranch
45
+ projectBranchMountBusyDuringExclusiveSync,
46
+ shouldDeferAutomaticWorkspaceSyncForPendingBranch,
47
+ workspacePlansMountBusyDuringExclusiveSync
40
48
  };
@@ -20,12 +20,32 @@ function shouldSerializeWorkspaceCommand(target) {
20
20
  function runWorkspaceCommand(target, coordinator, operation) {
21
21
  return shouldSerializeWorkspaceCommand(target) ? coordinator.runMutation(operation) : Promise.resolve().then(operation);
22
22
  }
23
+ async function runReservedWorkspaceCommand(target, coordinator, operation, releaseReservation) {
24
+ let released = false;
25
+ const releaseOnce = () => {
26
+ if (released) return;
27
+ released = true;
28
+ releaseReservation();
29
+ };
30
+ try {
31
+ return await runWorkspaceCommand(target, coordinator, async () => {
32
+ try {
33
+ return await operation();
34
+ } finally {
35
+ releaseOnce();
36
+ }
37
+ });
38
+ } finally {
39
+ releaseOnce();
40
+ }
41
+ }
23
42
  function acquireWorkspaceCommandMutation(target, coordinator) {
24
43
  return shouldSerializeWorkspaceCommand(target) ? coordinator.acquireMutation() : Promise.resolve(void 0);
25
44
  }
26
45
  export {
27
46
  acquireWorkspaceCommandMutation,
28
47
  reserveWorkspaceCommandAfterCurrentSync,
48
+ runReservedWorkspaceCommand,
29
49
  runWorkspaceCommand,
30
50
  shouldSerializeWorkspaceCommand
31
51
  };
@@ -15,6 +15,15 @@ const WORKSPACE_GIT_INTEGRATED_REF = "refs/r5d/workspace-local/integrated";
15
15
  const WORKSPACE_GIT_HYDRATED_RECEIPT = "r5d/workspace-hydrated-head";
16
16
  const WORKSPACE_GIT_HYDRATION_TRANSACTION = "r5d/workspace-hydration-transaction";
17
17
  const WORKSPACE_GIT_CHECKOUT_DURABILITY = "r5d/workspace-checkout-durability";
18
+ function workspaceGitMountsForRemediation(mounts) {
19
+ return mounts.map((mount) => ({ ...mount, busy: mount.busyForRecovery ?? mount.busy }));
20
+ }
21
+ class WorkspaceRemediationAncestryError extends Error {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = "WorkspaceRemediationAncestryError";
25
+ }
26
+ }
18
27
  const NON_RECURSIVE_GIT_CONFIG = [
19
28
  "-c",
20
29
  "submodule.recurse=false",
@@ -54,6 +63,29 @@ function revParse(workspacePath, revision) {
54
63
  const result = gitResult(workspacePath, ["rev-parse", "--verify", revision]);
55
64
  return result.exitCode === 0 ? result.stdout : null;
56
65
  }
66
+ function requiredWorkspaceAncestorHeads(value) {
67
+ if (value === void 0) return [];
68
+ if (!Array.isArray(value) || value.length === 0 || value.some((head) => typeof head !== "string" || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(head)) || new Set(value).size !== value.length) {
69
+ throw new WorkspaceRemediationAncestryError("Workspace remediation ancestor requirements are invalid");
70
+ }
71
+ return [...value];
72
+ }
73
+ function assertWorkspaceRemediationAncestry(input) {
74
+ if (input.requiredAncestorHeads.length === 0) return;
75
+ if (!input.currentHead) throw new WorkspaceRemediationAncestryError("Workspace remediation has no resolved HEAD to verify");
76
+ for (const requiredHead of input.requiredAncestorHeads) {
77
+ if (!tryGit(input.workspacePath, ["cat-file", "-e", `${requiredHead}^{commit}`]) || !tryGit(input.workspacePath, ["merge-base", "--is-ancestor", requiredHead, input.currentHead])) {
78
+ throw new WorkspaceRemediationAncestryError(
79
+ `Workspace remediation HEAD does not descend from required conflict commit ${requiredHead}`
80
+ );
81
+ }
82
+ }
83
+ if (!input.remoteHead || !tryGit(input.workspacePath, ["cat-file", "-e", `${input.remoteHead}^{commit}`]) || !tryGit(input.workspacePath, ["merge-base", "--is-ancestor", input.remoteHead, input.currentHead])) {
84
+ throw new WorkspaceRemediationAncestryError(
85
+ "Fetched workspace head is not an ancestor of the resolved remediation HEAD; merge it explicitly before synchronizing"
86
+ );
87
+ }
88
+ }
57
89
  function updateIntegratedWorkspaceHead(workspacePath, head) {
58
90
  git(workspacePath, ["update-ref", WORKSPACE_GIT_INTEGRATED_REF, head], "record integrated workspace head");
59
91
  }
@@ -923,6 +955,15 @@ function configureWorkspaceRepository(input) {
923
955
  );
924
956
  git(input.workspacePath, ["config", "--local", "core.fsyncMethod", "fsync"], "configure workspace fsync method");
925
957
  }
958
+ function configureExistingWorkspaceGitForRemediation(input) {
959
+ const workspacePath = path.resolve(input.workspacePath);
960
+ const workspaceStatus = lstatIfExists(workspacePath);
961
+ const gitStatus = lstatIfExists(path.join(workspacePath, ".git"));
962
+ if (!workspaceStatus?.isDirectory() || workspaceStatus.isSymbolicLink() || !gitStatus?.isDirectory() || gitStatus.isSymbolicLink()) {
963
+ throw new Error("Active workspace remediation requires a regular existing canonical synchronization checkout");
964
+ }
965
+ configureWorkspaceRepository({ ...input, workspacePath });
966
+ }
926
967
  function fetchWorkspaceHead(workspacePath, remoteUrl, credentialHelper, credentialUsername) {
927
968
  const result = gitResult(workspacePath, [
928
969
  ...gitTransportSecurityArgs(remoteUrl, credentialHelper, credentialUsername),
@@ -1261,6 +1302,9 @@ function resetWorkspaceGit(input) {
1261
1302
  const outerPath = path.join(workspacePath, ...mount.workspaceRelativePath.replace(/\\/g, "/").split("/"));
1262
1303
  return fs.existsSync(outerPath);
1263
1304
  });
1305
+ const bootstrapMountIds = new Set(bootstrapMounts.map(({ id }) => id));
1306
+ const durablyAbsentMounts = selected.skipped.filter((mount) => !bootstrapMountIds.has(mount.id) && mount.sourceMode === "all");
1307
+ const uncoveredSkippedMounts = selected.skipped.filter((mount) => !bootstrapMountIds.has(mount.id) && mount.sourceMode !== "all");
1264
1308
  const hydrationMounts = [...selected.active, ...bootstrapMounts];
1265
1309
  const requiredLiveMounts = [
1266
1310
  ...new Map([...requiredLiveMountsBeforeHydration, ...bootstrapMounts].map((mount) => [mount.id, mount])).values()
@@ -1268,7 +1312,7 @@ function resetWorkspaceGit(input) {
1268
1312
  const hydration = hydrateWorkspaceGitMountsTransactionally({
1269
1313
  workspacePath,
1270
1314
  mounts: hydrationMounts,
1271
- durabilityMounts: [...requiredLiveMounts, ...selected.tombstones],
1315
+ durabilityMounts: [...requiredLiveMounts, ...selected.tombstones, ...durablyAbsentMounts],
1272
1316
  receiptMounts: input.mounts,
1273
1317
  requiredMounts: requiredLiveMounts,
1274
1318
  recordCurrentHead: true
@@ -1284,7 +1328,7 @@ function resetWorkspaceGit(input) {
1284
1328
  remoteHead,
1285
1329
  discardedPaths,
1286
1330
  activeMountIds: hydration.hydratedMountIds,
1287
- skippedMountIds: [.../* @__PURE__ */ new Set([...selected.skipped.map(({ id }) => id), ...hydration.skippedMountIds])].sort()
1331
+ skippedMountIds: [.../* @__PURE__ */ new Set([...uncoveredSkippedMounts.map(({ id }) => id), ...hydration.skippedMountIds])].sort()
1288
1332
  };
1289
1333
  }
1290
1334
  function stagedPaths(workspacePath) {
@@ -1424,8 +1468,16 @@ async function synchronizeWorkspaceGit(input) {
1424
1468
  const maxDiffBytes = input.maxDiffBytes ?? MAX_WORKSPACE_GIT_DIFF_BYTES;
1425
1469
  const maxPushAttempts = Math.max(1, input.maxPushAttempts ?? 4);
1426
1470
  validateMounts(workspacePath, input.mounts);
1471
+ const requiredAncestorHeads = requiredWorkspaceAncestorHeads(input.requiredAncestorHeads);
1427
1472
  const preserveResolutionInProgress = input.skipMountMirror === true;
1428
1473
  const initial = ensureWorkspaceGitClone({ ...input, workspacePath, preserveResolutionInProgress });
1474
+ assertWorkspaceRemediationAncestry({
1475
+ workspacePath,
1476
+ currentHead: initial.localHead,
1477
+ remoteHead: initial.remoteHead,
1478
+ requiredAncestorHeads
1479
+ });
1480
+ const verifiedAncestorHeads = requiredAncestorHeads.length > 0 ? requiredAncestorHeads : void 0;
1429
1481
  const startingHead = initial.localHead;
1430
1482
  const receiptBeforeInitialHydration = readHydratedWorkspaceReceipt(workspacePath);
1431
1483
  const deferredMountIds = /* @__PURE__ */ new Set();
@@ -1517,6 +1569,7 @@ async function synchronizeWorkspaceGit(input) {
1517
1569
  conflictPaths: merged.conflictPaths,
1518
1570
  conflictSnapshotRefs: refs,
1519
1571
  conflictKind: "projection_merge",
1572
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1520
1573
  error: merged.error
1521
1574
  };
1522
1575
  }
@@ -1640,6 +1693,12 @@ async function synchronizeWorkspaceGit(input) {
1640
1693
  let rebaseCount = 0;
1641
1694
  let updated = false;
1642
1695
  for (let pushAttempt = 0; pushAttempt < maxPushAttempts; pushAttempt += 1) {
1696
+ assertWorkspaceRemediationAncestry({
1697
+ workspacePath,
1698
+ currentHead: revParse(workspacePath, "HEAD"),
1699
+ remoteHead,
1700
+ requiredAncestorHeads
1701
+ });
1643
1702
  const reconciled = synchronizeWithFetchedHead({ workspacePath, remoteHead, attemptId });
1644
1703
  if (reconciled.kind === "conflict") {
1645
1704
  const localHead2 = revParse(workspacePath, "HEAD");
@@ -1657,6 +1716,7 @@ async function synchronizeWorkspaceGit(input) {
1657
1716
  conflictPaths: reconciled.conflictPaths,
1658
1717
  conflictSnapshotRefs: reconciled.refs,
1659
1718
  conflictKind: "integration_rebase",
1719
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1660
1720
  error: reconciled.error
1661
1721
  };
1662
1722
  }
@@ -1676,11 +1736,13 @@ async function synchronizeWorkspaceGit(input) {
1676
1736
  diffSizeBytes: 0,
1677
1737
  affectedPaths: [],
1678
1738
  ...classifiedMounts,
1739
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1679
1740
  ...projectionWarning ? { error: projectionWarning } : {}
1680
1741
  };
1681
1742
  }
1682
1743
  const paths = changedPaths(workspacePath, remoteHead, localHead);
1683
- const size = paths.length > 0 ? await diffSizeBytes(workspacePath, remoteHead, localHead, maxDiffBytes) : 0;
1744
+ const size = paths.length > 0 ? await (input.measureDiffSize ?? diffSizeBytes)(workspacePath, remoteHead, localHead, maxDiffBytes) : 0;
1745
+ input.assertStillAdmitted?.();
1684
1746
  if (paths.length > 0 && size > maxDiffBytes && !input.allowLargeDiff) {
1685
1747
  const classifiedMounts = classifyMounts([...selected.active, ...selected.tombstones]);
1686
1748
  return {
@@ -1693,15 +1755,18 @@ async function synchronizeWorkspaceGit(input) {
1693
1755
  diffSizeBytes: size,
1694
1756
  affectedPaths: paths,
1695
1757
  ...classifiedMounts,
1758
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1696
1759
  error: `Workspace diff exceeds the ${maxDiffBytes}-byte automatic publication limit`
1697
1760
  };
1698
1761
  }
1699
1762
  if (localHead === remoteHead) {
1763
+ assertWorkspaceRemediationAncestry({ workspacePath, currentHead: localHead, remoteHead, requiredAncestorHeads });
1700
1764
  const completedMounts = completedMountSelection(Boolean(input.skipMountMirror));
1701
1765
  await input.afterWorkspacePublished?.({
1702
1766
  publishedHead: localHead,
1703
1767
  activeMountIds: completedMounts.activeMountIds
1704
1768
  });
1769
+ input.assertStillAdmitted?.();
1705
1770
  return {
1706
1771
  outcome: updated ? "updated" : "no_change",
1707
1772
  startingHead,
@@ -1712,9 +1777,11 @@ async function synchronizeWorkspaceGit(input) {
1712
1777
  diffSizeBytes: size,
1713
1778
  affectedPaths: paths,
1714
1779
  ...completedMounts,
1780
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1715
1781
  ...projectionWarning ? { error: projectionWarning } : {}
1716
1782
  };
1717
1783
  }
1784
+ assertWorkspaceRemediationAncestry({ workspacePath, currentHead: localHead, remoteHead, requiredAncestorHeads });
1718
1785
  const pushArgs = [
1719
1786
  ...gitTransportSecurityArgs(input.remoteUrl, input.credentialHelper, input.credentialUsername),
1720
1787
  "push",
@@ -1724,12 +1791,14 @@ async function synchronizeWorkspaceGit(input) {
1724
1791
  pushArgs.push("origin", `HEAD:refs/heads/${WORKSPACE_GIT_BRANCH}`);
1725
1792
  const push = gitResult(workspacePath, pushArgs);
1726
1793
  if (push.exitCode === 0) {
1794
+ assertWorkspaceRemediationAncestry({ workspacePath, currentHead: localHead, remoteHead, requiredAncestorHeads });
1727
1795
  updateIntegratedWorkspaceHead(workspacePath, localHead);
1728
1796
  const completedMounts = completedMountSelection(Boolean(input.skipMountMirror));
1729
1797
  await input.afterWorkspacePublished?.({
1730
1798
  publishedHead: localHead,
1731
1799
  activeMountIds: completedMounts.activeMountIds
1732
1800
  });
1801
+ input.assertStillAdmitted?.();
1733
1802
  return {
1734
1803
  outcome: "pushed",
1735
1804
  startingHead,
@@ -1740,6 +1809,7 @@ async function synchronizeWorkspaceGit(input) {
1740
1809
  diffSizeBytes: size,
1741
1810
  affectedPaths: paths,
1742
1811
  ...completedMounts,
1812
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1743
1813
  ...projectionWarning ? { error: projectionWarning } : {}
1744
1814
  };
1745
1815
  }
@@ -1765,11 +1835,14 @@ export {
1765
1835
  WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION,
1766
1836
  WORKSPACE_GIT_HYDRATED_RECEIPT,
1767
1837
  WORKSPACE_GIT_HYDRATION_TRANSACTION,
1838
+ WorkspaceRemediationAncestryError,
1839
+ configureExistingWorkspaceGitForRemediation,
1768
1840
  ensureWorkspaceGitClone,
1769
1841
  hydrateWorkspaceGitMounts,
1770
1842
  recoverWorkspaceGitHydration,
1771
1843
  resetWorkspaceGit,
1772
1844
  synchronizeWorkspaceGit,
1773
1845
  workspaceGitHydrationIsCurrent,
1846
+ workspaceGitMountsForRemediation,
1774
1847
  workspaceGitSyncTestHarness
1775
1848
  };
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import { Database } from "bun:sqlite";
3
+ import type { WorkerGitIdentity } from "./git-identity";
3
4
  import { configureGitHubRegistryAuthFiles, type PreparedPrivateAuthFileGeneration } from "./registry-auth";
4
5
  type WorkerProjectConfig = {
5
6
  projectId: string;
@@ -22,6 +23,53 @@ type WorkerProjectConfig = {
22
23
  baseCommitHash: string;
23
24
  }>;
24
25
  };
26
+ type WorkspaceSyncTrigger = {
27
+ type: "connect" | "inbound_head" | "write" | "edit" | "shell_inline" | "process_terminal" | "process_cancel" | "periodic" | "manual" | "remediation" | "remediation_confirm" | "remediation_reset";
28
+ sessionId?: string;
29
+ processRunId?: string;
30
+ toolCallId?: string;
31
+ projectId?: string;
32
+ branchName?: string;
33
+ detail?: string;
34
+ };
35
+ type WorkspaceSyncResult = {
36
+ type: "workspace_sync";
37
+ attemptId: string;
38
+ workerLabel: string;
39
+ trigger: WorkspaceSyncTrigger;
40
+ outcome: "no_change" | "published" | "updated" | "conflict_reset" | "large_diff_blocked" | "conflict_blocked" | "reset" | "failed";
41
+ startingHead: string | null;
42
+ localHead?: string;
43
+ publishedHead?: string;
44
+ rebaseCount: number;
45
+ diffSizeBytes: number;
46
+ gitStatus: string;
47
+ affectedProjects: string[];
48
+ affectedPaths: string[];
49
+ activeMountIds?: string[];
50
+ skippedMountIds?: string[];
51
+ activeProjectBranchPublications?: Array<{
52
+ branchId: string;
53
+ projectId: string;
54
+ branchName: string;
55
+ }>;
56
+ discardedPaths: string[];
57
+ localChangesDiscarded: boolean;
58
+ conflictPaths?: string[];
59
+ conflictSnapshotRefs?: {
60
+ local: string;
61
+ remote: string;
62
+ };
63
+ conflictKind?: "integration_rebase" | "projection_merge";
64
+ verifiedAncestorHeads?: string[];
65
+ telemetry?: {
66
+ totalMs: number;
67
+ queueMs: number;
68
+ prepareMs: number;
69
+ synchronizeMs: number;
70
+ };
71
+ error?: string;
72
+ };
25
73
  export type WorkerSessionTarget = {
26
74
  type: "project";
27
75
  projectId: string;
@@ -31,6 +79,208 @@ export type WorkerSessionTarget = {
31
79
  ownerUserId: string;
32
80
  rootProfile: "visible_projects" | "canonical_sync";
33
81
  };
82
+ type WorkerServerMessage = {
83
+ type: "connected";
84
+ label: string;
85
+ workerId: string;
86
+ } | {
87
+ type: "workspace_config";
88
+ requestId: string;
89
+ projects: WorkerProjectConfig[];
90
+ workspaceRemoteUrl: string;
91
+ gitIdentity: WorkerGitIdentity;
92
+ githubCredential: WorkerGitHubCredential | null;
93
+ deferWorkspaceSyncForIncidentId?: string;
94
+ resetToCanonical?: boolean;
95
+ } | {
96
+ type: "sync_workspace";
97
+ requestId: string;
98
+ attemptId: string;
99
+ trigger: WorkspaceSyncTrigger;
100
+ confirmedLargeDiff?: boolean;
101
+ confirmationReason?: string;
102
+ resetToCanonical?: boolean;
103
+ requiredAncestorHeads?: string[];
104
+ } | {
105
+ type: "create_project_branch";
106
+ requestId: string;
107
+ branchId: string;
108
+ projectId: string;
109
+ sourceBranch: string;
110
+ targetBranch: string;
111
+ } | {
112
+ type: "delete_project_branch";
113
+ requestId: string;
114
+ branchId: string;
115
+ projectId: string;
116
+ branchName: string;
117
+ } | {
118
+ type: "code_list";
119
+ requestId: string;
120
+ target: Extract<WorkerSessionTarget, {
121
+ type: "project";
122
+ }>;
123
+ path: string;
124
+ } | {
125
+ type: "code_read";
126
+ requestId: string;
127
+ target: Extract<WorkerSessionTarget, {
128
+ type: "project";
129
+ }>;
130
+ path: string;
131
+ } | {
132
+ type: "sync_session_artifacts";
133
+ requestId: string;
134
+ sessionId: string;
135
+ } | {
136
+ type: "workspace_incident_updated";
137
+ incidentId: string | null;
138
+ status: "remediating" | "waiting_for_worker" | "resolved" | "confirmed" | "reset" | null;
139
+ originWorkerLabel?: string;
140
+ } | {
141
+ type: "exec_terminal_ack";
142
+ runId: string;
143
+ } | {
144
+ type: "port_forward_connect";
145
+ forwardId: string;
146
+ relayConnectionId: string;
147
+ workerPort: number;
148
+ } | {
149
+ type: "update_clis";
150
+ requestId: string;
151
+ workerPackageSpec: string;
152
+ r5dctlPackageSpec: string;
153
+ targetWorkerVersion: string;
154
+ targetR5dctlVersion: string;
155
+ } | {
156
+ type: "exec";
157
+ requestId: string;
158
+ runId: string;
159
+ target: WorkerSessionTarget;
160
+ sessionId?: string;
161
+ argv: string[];
162
+ cwd?: string;
163
+ env?: Record<string, string>;
164
+ timeoutMs?: number;
165
+ workspaceEffect?: "none";
166
+ } | {
167
+ type: "exec_start";
168
+ requestId: string;
169
+ runId: string;
170
+ target: WorkerSessionTarget;
171
+ sessionId: string;
172
+ argv: string[];
173
+ command: string;
174
+ mode: "foreground" | "detached";
175
+ credentialId?: string;
176
+ cwd?: string;
177
+ env?: Record<string, string>;
178
+ timeoutMs?: number;
179
+ interactive?: boolean;
180
+ workspaceEffect?: "none";
181
+ } | {
182
+ type: "exec_stdin";
183
+ requestId: string;
184
+ runId: string;
185
+ data?: string;
186
+ eof?: boolean;
187
+ } | {
188
+ type: "pty_open";
189
+ requestId: string;
190
+ ptyId: string;
191
+ target: WorkerSessionTarget;
192
+ cols: number;
193
+ rows: number;
194
+ command?: string;
195
+ env?: Record<string, string>;
196
+ envFiles?: Array<{
197
+ path: string;
198
+ content: string;
199
+ mode?: number;
200
+ }>;
201
+ } | {
202
+ type: "pty_input";
203
+ ptyId: string;
204
+ data: string;
205
+ } | {
206
+ type: "pty_resize";
207
+ ptyId: string;
208
+ cols: number;
209
+ rows: number;
210
+ } | {
211
+ type: "pty_close";
212
+ ptyId: string;
213
+ } | {
214
+ type: "read";
215
+ requestId: string;
216
+ target: WorkerSessionTarget;
217
+ sessionId?: string;
218
+ activePlanId?: string;
219
+ filePath: string;
220
+ offset?: number;
221
+ limit?: number;
222
+ } | {
223
+ type: "write";
224
+ requestId: string;
225
+ target: WorkerSessionTarget;
226
+ sessionId?: string;
227
+ activePlanId?: string;
228
+ filePath: string;
229
+ content: string;
230
+ } | {
231
+ type: "edit";
232
+ requestId: string;
233
+ target: WorkerSessionTarget;
234
+ sessionId?: string;
235
+ activePlanId?: string;
236
+ filePath: string;
237
+ edits: Array<{
238
+ oldText: string;
239
+ newText: string;
240
+ }>;
241
+ } | {
242
+ type: "grep";
243
+ requestId: string;
244
+ target: WorkerSessionTarget;
245
+ sessionId?: string;
246
+ activePlanId?: string;
247
+ pattern: string;
248
+ path?: string;
249
+ glob?: string;
250
+ caseSensitive?: boolean;
251
+ limit?: number;
252
+ } | {
253
+ type: "find";
254
+ requestId: string;
255
+ target: WorkerSessionTarget;
256
+ sessionId?: string;
257
+ activePlanId?: string;
258
+ pattern?: string;
259
+ path?: string;
260
+ entryType?: string;
261
+ limit?: number;
262
+ } | {
263
+ type: "ls";
264
+ requestId: string;
265
+ target: WorkerSessionTarget;
266
+ sessionId?: string;
267
+ activePlanId?: string;
268
+ path?: string;
269
+ limit?: number;
270
+ } | {
271
+ type: "view_file_bytes";
272
+ requestId: string;
273
+ target: WorkerSessionTarget;
274
+ sessionId?: string;
275
+ activePlanId?: string;
276
+ filePath: string;
277
+ } | {
278
+ type: "cancel";
279
+ requestId: string;
280
+ runId: string;
281
+ } | {
282
+ type: "ping";
283
+ };
34
284
  type WorkerReadFileResult = {
35
285
  type: "read";
36
286
  kind: "text";
@@ -151,6 +401,7 @@ export declare const workerPtyTestHarness: {
151
401
  removeEnvFiles: typeof removePtyEnvFiles;
152
402
  resolveEnvFileReferences: typeof resolvePtyEnvFileReferences;
153
403
  commandHasWorkspaceEffect: typeof workerCommandHasWorkspaceEffect;
404
+ canonicalSyncTerminalHead: typeof canonicalSyncTerminalHead;
154
405
  ptyIsWorkspaceBusy: typeof workerPtyIsWorkspaceBusy;
155
406
  parseLinuxForegroundBusy: typeof parseLinuxPtyForegroundBusy;
156
407
  };
@@ -302,6 +553,64 @@ type CredentialReapContractProbe = {
302
553
  type CredentialReapContractStatus = "direct" | "systemd_unverified" | "verified_systemd";
303
554
  declare function credentialReapContractStatus(probe?: CredentialReapContractProbe): CredentialReapContractStatus;
304
555
  declare function verifiedCredentialReapContract(probe?: CredentialReapContractProbe): boolean;
556
+ declare function workspaceConfigurationIncidentDeferral(input: {
557
+ requestedIncidentId: string | undefined;
558
+ activeIncidentId: string | null;
559
+ }): {
560
+ incidentId: string | null;
561
+ error?: Error;
562
+ };
563
+ type DeferredWorkspaceConfiguration = {
564
+ incidentId: string;
565
+ serverRefreshExpected: boolean;
566
+ };
567
+ declare function workspaceIncidentTerminalClearDisposition(input: {
568
+ deferredConfiguration: DeferredWorkspaceConfiguration | null;
569
+ clearedIncidentId: string;
570
+ }): "await_server_refresh" | "reconnect_for_refresh";
571
+ declare function workspaceIncidentTerminalRefreshFence(input: {
572
+ deferredConfiguration: DeferredWorkspaceConfiguration | null;
573
+ clearedIncidentId: string;
574
+ }): {
575
+ disposition: "await_server_refresh" | "reconnect_for_refresh";
576
+ deferredConfiguration: DeferredWorkspaceConfiguration;
577
+ };
578
+ declare function workspaceAutomaticSyncIsAdmitted(input: {
579
+ workspaceConfigured: boolean;
580
+ activeIncidentId: string | null;
581
+ deferredConfiguration: DeferredWorkspaceConfiguration | null;
582
+ }): boolean;
583
+ declare function deferredWorkspaceTargetIsAllowed(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null, target: WorkerSessionTarget): boolean;
584
+ declare function deferredWorkspaceSyncTriggerIsAllowed(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null, trigger: WorkspaceSyncTrigger): boolean;
585
+ declare function workspaceOperationsAreFenced(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null): boolean;
586
+ declare function workspaceCommandTransportIsAllowed(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null, transport: "exec" | "exec_start"): boolean;
587
+ declare function deferredCredentialTransitionMustWait(input: {
588
+ incidentId: string | null;
589
+ transitionPhase: CredentialGenerationTransitionPhase;
590
+ canonicalRemediationActive: boolean;
591
+ }): boolean;
592
+ declare function workspaceConfigurationIncidentSnapshotIsCurrent(capturedIncidentId: string | null, activeIncidentId: string | null): boolean;
593
+ declare function workspaceConfigurationResetToCanonicalIsAllowed(requested: boolean | undefined, incidentId: string | null): boolean;
594
+ declare function deferredWorkspaceRefreshWatchdogIsCurrent(input: {
595
+ capturedIncidentId: string;
596
+ deferredConfiguration: DeferredWorkspaceConfiguration | null;
597
+ activeIncidentId: string | null;
598
+ }): boolean;
599
+ declare function incidentDeferredProjectCheckouts(projects: readonly WorkerProjectConfig[]): Array<{
600
+ projectId: string;
601
+ branchName: string;
602
+ }>;
603
+ declare function deferredIncidentWorkspaceConfigurationResult(input: {
604
+ attemptId: string;
605
+ workerLabel: string;
606
+ head: string | null;
607
+ skippedMountIds: string[];
608
+ }): WorkspaceSyncResult;
609
+ declare function workspaceSyncFailureHydrationIsSafe(input: {
610
+ error: unknown;
611
+ resetToCanonical: boolean;
612
+ inspectCurrentHydration: () => boolean;
613
+ }): boolean;
305
614
  declare function fenceUnsafeWorkspaceSyncFailure(input: {
306
615
  hydrationCurrent: boolean;
307
616
  invalidateExecution: () => void;
@@ -332,7 +641,24 @@ export declare const workerGitSecurityTestHarness: {
332
641
  credentialReapContractStatus: typeof credentialReapContractStatus;
333
642
  verifiedCredentialReapContract: typeof verifiedCredentialReapContract;
334
643
  fenceUnsafeWorkspaceSyncFailure: typeof fenceUnsafeWorkspaceSyncFailure;
644
+ workspaceConfigurationIncidentDeferral: typeof workspaceConfigurationIncidentDeferral;
645
+ workspaceIncidentTerminalClearDisposition: typeof workspaceIncidentTerminalClearDisposition;
646
+ workspaceIncidentTerminalRefreshFence: typeof workspaceIncidentTerminalRefreshFence;
647
+ workspaceAutomaticSyncIsAdmitted: typeof workspaceAutomaticSyncIsAdmitted;
648
+ deferredWorkspaceTargetIsAllowed: typeof deferredWorkspaceTargetIsAllowed;
649
+ deferredWorkspaceSyncTriggerIsAllowed: typeof deferredWorkspaceSyncTriggerIsAllowed;
650
+ workspaceOperationsAreFenced: typeof workspaceOperationsAreFenced;
651
+ workspaceCommandTransportIsAllowed: typeof workspaceCommandTransportIsAllowed;
652
+ deferredCredentialTransitionMustWait: typeof deferredCredentialTransitionMustWait;
653
+ workspaceConfigurationIncidentSnapshotIsCurrent: typeof workspaceConfigurationIncidentSnapshotIsCurrent;
654
+ workspaceConfigurationResetToCanonicalIsAllowed: typeof workspaceConfigurationResetToCanonicalIsAllowed;
655
+ deferredWorkspaceRefreshWatchdogIsCurrent: typeof deferredWorkspaceRefreshWatchdogIsCurrent;
656
+ incidentDeferredProjectCheckouts: typeof incidentDeferredProjectCheckouts;
657
+ deferredIncidentWorkspaceConfigurationResult: typeof deferredIncidentWorkspaceConfigurationResult;
658
+ workspaceSyncFailureHydrationIsSafe: typeof workspaceSyncFailureHydrationIsSafe;
335
659
  assertWorkerChildAdmission: typeof assertWorkerChildAdmission;
660
+ executeWriteFileOperation: typeof executeWriteFileOperation;
661
+ executeEditFileOperation: typeof executeEditFileOperation;
336
662
  terminateCredentialBearingChildren: typeof terminateCredentialBearingChildren;
337
663
  terminateCredentialBearingChildrenWithRetention: typeof terminateCredentialBearingChildrenWithRetention;
338
664
  commitCredentialGeneration(prepared: PreparedPrivateAuthFileGeneration, credential: WorkerGitHubCredential | null, children: readonly {
@@ -365,6 +691,7 @@ type ResolvedWorkerSessionTarget = {
365
691
  rootPath: string;
366
692
  config?: WorkerProjectConfig;
367
693
  };
694
+ declare function canonicalSyncTerminalHead(resolvedTarget: Pick<ResolvedWorkerSessionTarget, "target" | "rootPath">, readHead?: (rootPath: string) => string): string | undefined;
368
695
  export declare function describeWorkerSessionTarget(target: WorkerSessionTarget): string;
369
696
  export declare function resolveWorkerSessionTarget(input: {
370
697
  target: WorkerSessionTarget;
@@ -389,6 +716,28 @@ export declare function editWorkerTextFile(branchPath: string, filePath: string,
389
716
  oldText: string;
390
717
  newText: string;
391
718
  }>, builtInPaths?: WorkerBuiltInToolPaths): WorkerEditFileResult;
719
+ declare function executeWriteFileOperation(input: {
720
+ message: Extract<WorkerServerMessage, {
721
+ type: "write";
722
+ }>;
723
+ resolvedTarget: ResolvedWorkerSessionTarget;
724
+ baseUrl: string;
725
+ token: string;
726
+ artifactRoot: string;
727
+ planRoot: string;
728
+ assertAdmission: () => void;
729
+ }): Promise<WorkerWriteFileResult>;
730
+ declare function executeEditFileOperation(input: {
731
+ message: Extract<WorkerServerMessage, {
732
+ type: "edit";
733
+ }>;
734
+ resolvedTarget: ResolvedWorkerSessionTarget;
735
+ baseUrl: string;
736
+ token: string;
737
+ artifactRoot: string;
738
+ planRoot: string;
739
+ assertAdmission: () => void;
740
+ }): Promise<WorkerEditFileResult>;
392
741
  export declare function grepWorkerFiles(branchPath: string, input: {
393
742
  pattern: string;
394
743
  path?: string;