@ricsam/r5d-worker 0.0.82 → 0.0.83

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.83",
4
4
  "type": "module"
5
5
  }
@@ -15,6 +15,12 @@ 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
+ class WorkspaceRemediationAncestryError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = "WorkspaceRemediationAncestryError";
22
+ }
23
+ }
18
24
  const NON_RECURSIVE_GIT_CONFIG = [
19
25
  "-c",
20
26
  "submodule.recurse=false",
@@ -54,6 +60,29 @@ function revParse(workspacePath, revision) {
54
60
  const result = gitResult(workspacePath, ["rev-parse", "--verify", revision]);
55
61
  return result.exitCode === 0 ? result.stdout : null;
56
62
  }
63
+ function requiredWorkspaceAncestorHeads(value) {
64
+ if (value === void 0) return [];
65
+ 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) {
66
+ throw new WorkspaceRemediationAncestryError("Workspace remediation ancestor requirements are invalid");
67
+ }
68
+ return [...value];
69
+ }
70
+ function assertWorkspaceRemediationAncestry(input) {
71
+ if (input.requiredAncestorHeads.length === 0) return;
72
+ if (!input.currentHead) throw new WorkspaceRemediationAncestryError("Workspace remediation has no resolved HEAD to verify");
73
+ for (const requiredHead of input.requiredAncestorHeads) {
74
+ if (!tryGit(input.workspacePath, ["cat-file", "-e", `${requiredHead}^{commit}`]) || !tryGit(input.workspacePath, ["merge-base", "--is-ancestor", requiredHead, input.currentHead])) {
75
+ throw new WorkspaceRemediationAncestryError(
76
+ `Workspace remediation HEAD does not descend from required conflict commit ${requiredHead}`
77
+ );
78
+ }
79
+ }
80
+ if (!input.remoteHead || !tryGit(input.workspacePath, ["cat-file", "-e", `${input.remoteHead}^{commit}`]) || !tryGit(input.workspacePath, ["merge-base", "--is-ancestor", input.remoteHead, input.currentHead])) {
81
+ throw new WorkspaceRemediationAncestryError(
82
+ "Fetched workspace head is not an ancestor of the resolved remediation HEAD; merge it explicitly before synchronizing"
83
+ );
84
+ }
85
+ }
57
86
  function updateIntegratedWorkspaceHead(workspacePath, head) {
58
87
  git(workspacePath, ["update-ref", WORKSPACE_GIT_INTEGRATED_REF, head], "record integrated workspace head");
59
88
  }
@@ -923,6 +952,15 @@ function configureWorkspaceRepository(input) {
923
952
  );
924
953
  git(input.workspacePath, ["config", "--local", "core.fsyncMethod", "fsync"], "configure workspace fsync method");
925
954
  }
955
+ function configureExistingWorkspaceGitForRemediation(input) {
956
+ const workspacePath = path.resolve(input.workspacePath);
957
+ const workspaceStatus = lstatIfExists(workspacePath);
958
+ const gitStatus = lstatIfExists(path.join(workspacePath, ".git"));
959
+ if (!workspaceStatus?.isDirectory() || workspaceStatus.isSymbolicLink() || !gitStatus?.isDirectory() || gitStatus.isSymbolicLink()) {
960
+ throw new Error("Active workspace remediation requires a regular existing canonical synchronization checkout");
961
+ }
962
+ configureWorkspaceRepository({ ...input, workspacePath });
963
+ }
926
964
  function fetchWorkspaceHead(workspacePath, remoteUrl, credentialHelper, credentialUsername) {
927
965
  const result = gitResult(workspacePath, [
928
966
  ...gitTransportSecurityArgs(remoteUrl, credentialHelper, credentialUsername),
@@ -1424,8 +1462,16 @@ async function synchronizeWorkspaceGit(input) {
1424
1462
  const maxDiffBytes = input.maxDiffBytes ?? MAX_WORKSPACE_GIT_DIFF_BYTES;
1425
1463
  const maxPushAttempts = Math.max(1, input.maxPushAttempts ?? 4);
1426
1464
  validateMounts(workspacePath, input.mounts);
1465
+ const requiredAncestorHeads = requiredWorkspaceAncestorHeads(input.requiredAncestorHeads);
1427
1466
  const preserveResolutionInProgress = input.skipMountMirror === true;
1428
1467
  const initial = ensureWorkspaceGitClone({ ...input, workspacePath, preserveResolutionInProgress });
1468
+ assertWorkspaceRemediationAncestry({
1469
+ workspacePath,
1470
+ currentHead: initial.localHead,
1471
+ remoteHead: initial.remoteHead,
1472
+ requiredAncestorHeads
1473
+ });
1474
+ const verifiedAncestorHeads = requiredAncestorHeads.length > 0 ? requiredAncestorHeads : void 0;
1429
1475
  const startingHead = initial.localHead;
1430
1476
  const receiptBeforeInitialHydration = readHydratedWorkspaceReceipt(workspacePath);
1431
1477
  const deferredMountIds = /* @__PURE__ */ new Set();
@@ -1517,6 +1563,7 @@ async function synchronizeWorkspaceGit(input) {
1517
1563
  conflictPaths: merged.conflictPaths,
1518
1564
  conflictSnapshotRefs: refs,
1519
1565
  conflictKind: "projection_merge",
1566
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1520
1567
  error: merged.error
1521
1568
  };
1522
1569
  }
@@ -1640,6 +1687,12 @@ async function synchronizeWorkspaceGit(input) {
1640
1687
  let rebaseCount = 0;
1641
1688
  let updated = false;
1642
1689
  for (let pushAttempt = 0; pushAttempt < maxPushAttempts; pushAttempt += 1) {
1690
+ assertWorkspaceRemediationAncestry({
1691
+ workspacePath,
1692
+ currentHead: revParse(workspacePath, "HEAD"),
1693
+ remoteHead,
1694
+ requiredAncestorHeads
1695
+ });
1643
1696
  const reconciled = synchronizeWithFetchedHead({ workspacePath, remoteHead, attemptId });
1644
1697
  if (reconciled.kind === "conflict") {
1645
1698
  const localHead2 = revParse(workspacePath, "HEAD");
@@ -1657,6 +1710,7 @@ async function synchronizeWorkspaceGit(input) {
1657
1710
  conflictPaths: reconciled.conflictPaths,
1658
1711
  conflictSnapshotRefs: reconciled.refs,
1659
1712
  conflictKind: "integration_rebase",
1713
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1660
1714
  error: reconciled.error
1661
1715
  };
1662
1716
  }
@@ -1676,11 +1730,13 @@ async function synchronizeWorkspaceGit(input) {
1676
1730
  diffSizeBytes: 0,
1677
1731
  affectedPaths: [],
1678
1732
  ...classifiedMounts,
1733
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1679
1734
  ...projectionWarning ? { error: projectionWarning } : {}
1680
1735
  };
1681
1736
  }
1682
1737
  const paths = changedPaths(workspacePath, remoteHead, localHead);
1683
- const size = paths.length > 0 ? await diffSizeBytes(workspacePath, remoteHead, localHead, maxDiffBytes) : 0;
1738
+ const size = paths.length > 0 ? await (input.measureDiffSize ?? diffSizeBytes)(workspacePath, remoteHead, localHead, maxDiffBytes) : 0;
1739
+ input.assertStillAdmitted?.();
1684
1740
  if (paths.length > 0 && size > maxDiffBytes && !input.allowLargeDiff) {
1685
1741
  const classifiedMounts = classifyMounts([...selected.active, ...selected.tombstones]);
1686
1742
  return {
@@ -1693,15 +1749,18 @@ async function synchronizeWorkspaceGit(input) {
1693
1749
  diffSizeBytes: size,
1694
1750
  affectedPaths: paths,
1695
1751
  ...classifiedMounts,
1752
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1696
1753
  error: `Workspace diff exceeds the ${maxDiffBytes}-byte automatic publication limit`
1697
1754
  };
1698
1755
  }
1699
1756
  if (localHead === remoteHead) {
1757
+ assertWorkspaceRemediationAncestry({ workspacePath, currentHead: localHead, remoteHead, requiredAncestorHeads });
1700
1758
  const completedMounts = completedMountSelection(Boolean(input.skipMountMirror));
1701
1759
  await input.afterWorkspacePublished?.({
1702
1760
  publishedHead: localHead,
1703
1761
  activeMountIds: completedMounts.activeMountIds
1704
1762
  });
1763
+ input.assertStillAdmitted?.();
1705
1764
  return {
1706
1765
  outcome: updated ? "updated" : "no_change",
1707
1766
  startingHead,
@@ -1712,9 +1771,11 @@ async function synchronizeWorkspaceGit(input) {
1712
1771
  diffSizeBytes: size,
1713
1772
  affectedPaths: paths,
1714
1773
  ...completedMounts,
1774
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1715
1775
  ...projectionWarning ? { error: projectionWarning } : {}
1716
1776
  };
1717
1777
  }
1778
+ assertWorkspaceRemediationAncestry({ workspacePath, currentHead: localHead, remoteHead, requiredAncestorHeads });
1718
1779
  const pushArgs = [
1719
1780
  ...gitTransportSecurityArgs(input.remoteUrl, input.credentialHelper, input.credentialUsername),
1720
1781
  "push",
@@ -1724,12 +1785,14 @@ async function synchronizeWorkspaceGit(input) {
1724
1785
  pushArgs.push("origin", `HEAD:refs/heads/${WORKSPACE_GIT_BRANCH}`);
1725
1786
  const push = gitResult(workspacePath, pushArgs);
1726
1787
  if (push.exitCode === 0) {
1788
+ assertWorkspaceRemediationAncestry({ workspacePath, currentHead: localHead, remoteHead, requiredAncestorHeads });
1727
1789
  updateIntegratedWorkspaceHead(workspacePath, localHead);
1728
1790
  const completedMounts = completedMountSelection(Boolean(input.skipMountMirror));
1729
1791
  await input.afterWorkspacePublished?.({
1730
1792
  publishedHead: localHead,
1731
1793
  activeMountIds: completedMounts.activeMountIds
1732
1794
  });
1795
+ input.assertStillAdmitted?.();
1733
1796
  return {
1734
1797
  outcome: "pushed",
1735
1798
  startingHead,
@@ -1740,6 +1803,7 @@ async function synchronizeWorkspaceGit(input) {
1740
1803
  diffSizeBytes: size,
1741
1804
  affectedPaths: paths,
1742
1805
  ...completedMounts,
1806
+ ...verifiedAncestorHeads ? { verifiedAncestorHeads } : {},
1743
1807
  ...projectionWarning ? { error: projectionWarning } : {}
1744
1808
  };
1745
1809
  }
@@ -1765,6 +1829,8 @@ export {
1765
1829
  WORKSPACE_GIT_CONFIRMED_LARGE_DIFF_PUSH_OPTION,
1766
1830
  WORKSPACE_GIT_HYDRATED_RECEIPT,
1767
1831
  WORKSPACE_GIT_HYDRATION_TRANSACTION,
1832
+ WorkspaceRemediationAncestryError,
1833
+ configureExistingWorkspaceGitForRemediation,
1768
1834
  ensureWorkspaceGitClone,
1769
1835
  hydrateWorkspaceGitMounts,
1770
1836
  recoverWorkspaceGitHydration,
@@ -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,207 @@ 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
+ } | {
95
+ type: "sync_workspace";
96
+ requestId: string;
97
+ attemptId: string;
98
+ trigger: WorkspaceSyncTrigger;
99
+ confirmedLargeDiff?: boolean;
100
+ confirmationReason?: string;
101
+ resetToCanonical?: boolean;
102
+ requiredAncestorHeads?: string[];
103
+ } | {
104
+ type: "create_project_branch";
105
+ requestId: string;
106
+ branchId: string;
107
+ projectId: string;
108
+ sourceBranch: string;
109
+ targetBranch: string;
110
+ } | {
111
+ type: "delete_project_branch";
112
+ requestId: string;
113
+ branchId: string;
114
+ projectId: string;
115
+ branchName: string;
116
+ } | {
117
+ type: "code_list";
118
+ requestId: string;
119
+ target: Extract<WorkerSessionTarget, {
120
+ type: "project";
121
+ }>;
122
+ path: string;
123
+ } | {
124
+ type: "code_read";
125
+ requestId: string;
126
+ target: Extract<WorkerSessionTarget, {
127
+ type: "project";
128
+ }>;
129
+ path: string;
130
+ } | {
131
+ type: "sync_session_artifacts";
132
+ requestId: string;
133
+ sessionId: string;
134
+ } | {
135
+ type: "workspace_incident_updated";
136
+ incidentId: string | null;
137
+ status: "remediating" | "waiting_for_worker" | "resolved" | "confirmed" | "reset" | null;
138
+ originWorkerLabel?: string;
139
+ } | {
140
+ type: "exec_terminal_ack";
141
+ runId: string;
142
+ } | {
143
+ type: "port_forward_connect";
144
+ forwardId: string;
145
+ relayConnectionId: string;
146
+ workerPort: number;
147
+ } | {
148
+ type: "update_clis";
149
+ requestId: string;
150
+ workerPackageSpec: string;
151
+ r5dctlPackageSpec: string;
152
+ targetWorkerVersion: string;
153
+ targetR5dctlVersion: string;
154
+ } | {
155
+ type: "exec";
156
+ requestId: string;
157
+ runId: string;
158
+ target: WorkerSessionTarget;
159
+ sessionId?: string;
160
+ argv: string[];
161
+ cwd?: string;
162
+ env?: Record<string, string>;
163
+ timeoutMs?: number;
164
+ workspaceEffect?: "none";
165
+ } | {
166
+ type: "exec_start";
167
+ requestId: string;
168
+ runId: string;
169
+ target: WorkerSessionTarget;
170
+ sessionId: string;
171
+ argv: string[];
172
+ command: string;
173
+ mode: "foreground" | "detached";
174
+ credentialId?: string;
175
+ cwd?: string;
176
+ env?: Record<string, string>;
177
+ timeoutMs?: number;
178
+ interactive?: boolean;
179
+ workspaceEffect?: "none";
180
+ } | {
181
+ type: "exec_stdin";
182
+ requestId: string;
183
+ runId: string;
184
+ data?: string;
185
+ eof?: boolean;
186
+ } | {
187
+ type: "pty_open";
188
+ requestId: string;
189
+ ptyId: string;
190
+ target: WorkerSessionTarget;
191
+ cols: number;
192
+ rows: number;
193
+ command?: string;
194
+ env?: Record<string, string>;
195
+ envFiles?: Array<{
196
+ path: string;
197
+ content: string;
198
+ mode?: number;
199
+ }>;
200
+ } | {
201
+ type: "pty_input";
202
+ ptyId: string;
203
+ data: string;
204
+ } | {
205
+ type: "pty_resize";
206
+ ptyId: string;
207
+ cols: number;
208
+ rows: number;
209
+ } | {
210
+ type: "pty_close";
211
+ ptyId: string;
212
+ } | {
213
+ type: "read";
214
+ requestId: string;
215
+ target: WorkerSessionTarget;
216
+ sessionId?: string;
217
+ activePlanId?: string;
218
+ filePath: string;
219
+ offset?: number;
220
+ limit?: number;
221
+ } | {
222
+ type: "write";
223
+ requestId: string;
224
+ target: WorkerSessionTarget;
225
+ sessionId?: string;
226
+ activePlanId?: string;
227
+ filePath: string;
228
+ content: string;
229
+ } | {
230
+ type: "edit";
231
+ requestId: string;
232
+ target: WorkerSessionTarget;
233
+ sessionId?: string;
234
+ activePlanId?: string;
235
+ filePath: string;
236
+ edits: Array<{
237
+ oldText: string;
238
+ newText: string;
239
+ }>;
240
+ } | {
241
+ type: "grep";
242
+ requestId: string;
243
+ target: WorkerSessionTarget;
244
+ sessionId?: string;
245
+ activePlanId?: string;
246
+ pattern: string;
247
+ path?: string;
248
+ glob?: string;
249
+ caseSensitive?: boolean;
250
+ limit?: number;
251
+ } | {
252
+ type: "find";
253
+ requestId: string;
254
+ target: WorkerSessionTarget;
255
+ sessionId?: string;
256
+ activePlanId?: string;
257
+ pattern?: string;
258
+ path?: string;
259
+ entryType?: string;
260
+ limit?: number;
261
+ } | {
262
+ type: "ls";
263
+ requestId: string;
264
+ target: WorkerSessionTarget;
265
+ sessionId?: string;
266
+ activePlanId?: string;
267
+ path?: string;
268
+ limit?: number;
269
+ } | {
270
+ type: "view_file_bytes";
271
+ requestId: string;
272
+ target: WorkerSessionTarget;
273
+ sessionId?: string;
274
+ activePlanId?: string;
275
+ filePath: string;
276
+ } | {
277
+ type: "cancel";
278
+ requestId: string;
279
+ runId: string;
280
+ } | {
281
+ type: "ping";
282
+ };
34
283
  type WorkerReadFileResult = {
35
284
  type: "read";
36
285
  kind: "text";
@@ -151,6 +400,7 @@ export declare const workerPtyTestHarness: {
151
400
  removeEnvFiles: typeof removePtyEnvFiles;
152
401
  resolveEnvFileReferences: typeof resolvePtyEnvFileReferences;
153
402
  commandHasWorkspaceEffect: typeof workerCommandHasWorkspaceEffect;
403
+ canonicalSyncTerminalHead: typeof canonicalSyncTerminalHead;
154
404
  ptyIsWorkspaceBusy: typeof workerPtyIsWorkspaceBusy;
155
405
  parseLinuxForegroundBusy: typeof parseLinuxPtyForegroundBusy;
156
406
  };
@@ -302,6 +552,51 @@ type CredentialReapContractProbe = {
302
552
  type CredentialReapContractStatus = "direct" | "systemd_unverified" | "verified_systemd";
303
553
  declare function credentialReapContractStatus(probe?: CredentialReapContractProbe): CredentialReapContractStatus;
304
554
  declare function verifiedCredentialReapContract(probe?: CredentialReapContractProbe): boolean;
555
+ declare function workspaceConfigurationIncidentDeferral(input: {
556
+ requestedIncidentId: string | undefined;
557
+ activeIncidentId: string | null;
558
+ }): {
559
+ incidentId: string | null;
560
+ error?: Error;
561
+ };
562
+ type DeferredWorkspaceConfiguration = {
563
+ incidentId: string;
564
+ serverRefreshExpected: boolean;
565
+ };
566
+ declare function workspaceIncidentTerminalClearDisposition(input: {
567
+ deferredConfiguration: DeferredWorkspaceConfiguration | null;
568
+ clearedIncidentId: string | null;
569
+ }): "ordinary_resume" | "await_server_refresh" | "reconnect_for_refresh";
570
+ declare function deferredWorkspaceTargetIsAllowed(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null, target: WorkerSessionTarget): boolean;
571
+ declare function deferredWorkspaceSyncTriggerIsAllowed(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null, trigger: WorkspaceSyncTrigger): boolean;
572
+ declare function workspaceOperationsAreFenced(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null): boolean;
573
+ declare function workspaceCommandTransportIsAllowed(deferredConfiguration: DeferredWorkspaceConfiguration | null, activeIncidentId: string | null, transport: "exec" | "exec_start"): boolean;
574
+ declare function deferredCredentialTransitionMustWait(input: {
575
+ incidentId: string | null;
576
+ transitionPhase: CredentialGenerationTransitionPhase;
577
+ canonicalRemediationActive: boolean;
578
+ }): boolean;
579
+ declare function workspaceConfigurationIncidentSnapshotIsCurrent(capturedIncidentId: string | null, activeIncidentId: string | null): boolean;
580
+ declare function deferredWorkspaceRefreshWatchdogIsCurrent(input: {
581
+ capturedIncidentId: string;
582
+ deferredConfiguration: DeferredWorkspaceConfiguration | null;
583
+ activeIncidentId: string | null;
584
+ }): boolean;
585
+ declare function incidentDeferredProjectCheckouts(projects: readonly WorkerProjectConfig[]): Array<{
586
+ projectId: string;
587
+ branchName: string;
588
+ }>;
589
+ declare function deferredIncidentWorkspaceConfigurationResult(input: {
590
+ attemptId: string;
591
+ workerLabel: string;
592
+ head: string | null;
593
+ skippedMountIds: string[];
594
+ }): WorkspaceSyncResult;
595
+ declare function workspaceSyncFailureHydrationIsSafe(input: {
596
+ error: unknown;
597
+ resetToCanonical: boolean;
598
+ inspectCurrentHydration: () => boolean;
599
+ }): boolean;
305
600
  declare function fenceUnsafeWorkspaceSyncFailure(input: {
306
601
  hydrationCurrent: boolean;
307
602
  invalidateExecution: () => void;
@@ -332,7 +627,21 @@ export declare const workerGitSecurityTestHarness: {
332
627
  credentialReapContractStatus: typeof credentialReapContractStatus;
333
628
  verifiedCredentialReapContract: typeof verifiedCredentialReapContract;
334
629
  fenceUnsafeWorkspaceSyncFailure: typeof fenceUnsafeWorkspaceSyncFailure;
630
+ workspaceConfigurationIncidentDeferral: typeof workspaceConfigurationIncidentDeferral;
631
+ workspaceIncidentTerminalClearDisposition: typeof workspaceIncidentTerminalClearDisposition;
632
+ deferredWorkspaceTargetIsAllowed: typeof deferredWorkspaceTargetIsAllowed;
633
+ deferredWorkspaceSyncTriggerIsAllowed: typeof deferredWorkspaceSyncTriggerIsAllowed;
634
+ workspaceOperationsAreFenced: typeof workspaceOperationsAreFenced;
635
+ workspaceCommandTransportIsAllowed: typeof workspaceCommandTransportIsAllowed;
636
+ deferredCredentialTransitionMustWait: typeof deferredCredentialTransitionMustWait;
637
+ workspaceConfigurationIncidentSnapshotIsCurrent: typeof workspaceConfigurationIncidentSnapshotIsCurrent;
638
+ deferredWorkspaceRefreshWatchdogIsCurrent: typeof deferredWorkspaceRefreshWatchdogIsCurrent;
639
+ incidentDeferredProjectCheckouts: typeof incidentDeferredProjectCheckouts;
640
+ deferredIncidentWorkspaceConfigurationResult: typeof deferredIncidentWorkspaceConfigurationResult;
641
+ workspaceSyncFailureHydrationIsSafe: typeof workspaceSyncFailureHydrationIsSafe;
335
642
  assertWorkerChildAdmission: typeof assertWorkerChildAdmission;
643
+ executeWriteFileOperation: typeof executeWriteFileOperation;
644
+ executeEditFileOperation: typeof executeEditFileOperation;
336
645
  terminateCredentialBearingChildren: typeof terminateCredentialBearingChildren;
337
646
  terminateCredentialBearingChildrenWithRetention: typeof terminateCredentialBearingChildrenWithRetention;
338
647
  commitCredentialGeneration(prepared: PreparedPrivateAuthFileGeneration, credential: WorkerGitHubCredential | null, children: readonly {
@@ -365,6 +674,7 @@ type ResolvedWorkerSessionTarget = {
365
674
  rootPath: string;
366
675
  config?: WorkerProjectConfig;
367
676
  };
677
+ declare function canonicalSyncTerminalHead(resolvedTarget: Pick<ResolvedWorkerSessionTarget, "target" | "rootPath">, readHead?: (rootPath: string) => string): string | undefined;
368
678
  export declare function describeWorkerSessionTarget(target: WorkerSessionTarget): string;
369
679
  export declare function resolveWorkerSessionTarget(input: {
370
680
  target: WorkerSessionTarget;
@@ -389,6 +699,28 @@ export declare function editWorkerTextFile(branchPath: string, filePath: string,
389
699
  oldText: string;
390
700
  newText: string;
391
701
  }>, builtInPaths?: WorkerBuiltInToolPaths): WorkerEditFileResult;
702
+ declare function executeWriteFileOperation(input: {
703
+ message: Extract<WorkerServerMessage, {
704
+ type: "write";
705
+ }>;
706
+ resolvedTarget: ResolvedWorkerSessionTarget;
707
+ baseUrl: string;
708
+ token: string;
709
+ artifactRoot: string;
710
+ planRoot: string;
711
+ assertAdmission: () => void;
712
+ }): Promise<WorkerWriteFileResult>;
713
+ declare function executeEditFileOperation(input: {
714
+ message: Extract<WorkerServerMessage, {
715
+ type: "edit";
716
+ }>;
717
+ resolvedTarget: ResolvedWorkerSessionTarget;
718
+ baseUrl: string;
719
+ token: string;
720
+ artifactRoot: string;
721
+ planRoot: string;
722
+ assertAdmission: () => void;
723
+ }): Promise<WorkerEditFileResult>;
392
724
  export declare function grepWorkerFiles(branchPath: string, input: {
393
725
  pattern: string;
394
726
  path?: string;
@@ -53,8 +53,13 @@ export type WorkspaceGitSyncResult = {
53
53
  remote: string;
54
54
  };
55
55
  conflictKind?: "integration_rebase" | "projection_merge";
56
+ verifiedAncestorHeads?: string[];
56
57
  error?: string;
57
58
  };
59
+ /** A guarded remediation was rejected before it was allowed to hydrate or publish. */
60
+ export declare class WorkspaceRemediationAncestryError extends Error {
61
+ constructor(message: string);
62
+ }
58
63
  declare function gitCommandArgs(args: string[]): string[];
59
64
  declare function workspaceCloneCommandArgs(args: string[], remoteUrl: string, credentialHelper?: string | null, credentialUsername?: string | null): string[];
60
65
  export declare function workspaceGitHydrationIsCurrent(workspacePath: string, mounts?: readonly WorkspaceGitMount[]): boolean;
@@ -68,6 +73,22 @@ declare function configureWorkspaceRepository(input: {
68
73
  email: string;
69
74
  };
70
75
  }): void;
76
+ /**
77
+ * Rebind only the existing canonical checkout's local Git configuration while
78
+ * an incident owns its HEAD. In particular, this deliberately does not fetch,
79
+ * recover durability, move refs, reset the index/worktree, or inspect/abort an
80
+ * in-progress merge or rebase.
81
+ */
82
+ export declare function configureExistingWorkspaceGitForRemediation(input: {
83
+ workspacePath: string;
84
+ remoteUrl: string;
85
+ credentialHelper?: string | null;
86
+ credentialUsername?: string | null;
87
+ gitIdentity: {
88
+ name: string;
89
+ email: string;
90
+ };
91
+ }): void;
71
92
  export declare function ensureWorkspaceGitClone(input: {
72
93
  workspacePath: string;
73
94
  remoteUrl: string;
@@ -113,6 +134,7 @@ export declare function resetWorkspaceGit(input: {
113
134
  activeMountIds: string[];
114
135
  skippedMountIds: string[];
115
136
  };
137
+ declare function diffSizeBytes(workspacePath: string, baseRevision: string | null, headRevision: string, limit: number): Promise<number>;
116
138
  export declare function synchronizeWorkspaceGit(input: {
117
139
  attemptId?: string;
118
140
  workerLabel: string;
@@ -131,6 +153,12 @@ export declare function synchronizeWorkspaceGit(input: {
131
153
  maxPushAttempts?: number;
132
154
  /** Publish edits already made in the outer clone without first projecting visible mounts over them. */
133
155
  skipMountMirror?: boolean;
156
+ /** Positive ancestry proof required before a remediation may integrate, publish, or hydrate. */
157
+ requiredAncestorHeads?: readonly string[];
158
+ /** Revalidate the worker/config/incident generation after every async boundary. */
159
+ assertStillAdmitted?: () => void;
160
+ /** Test seam for exercising an incident transition during asynchronous diff measurement. */
161
+ measureDiffSize?: typeof diffSizeBytes;
134
162
  afterWorkspacePublished?: (context: {
135
163
  publishedHead: string;
136
164
  activeMountIds: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.82",
3
+ "version": "0.0.83",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",