harnery 0.44.0 → 0.45.1

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.
Files changed (138) hide show
  1. package/dist/commands/workflow.d.ts.map +1 -1
  2. package/dist/commands/workflow.js +44 -0
  3. package/dist/core/agents/coord-client.d.ts.map +1 -1
  4. package/dist/core/agents/coord-client.js +18 -0
  5. package/dist/core/agents/live-authority-v3.d.ts +0 -1
  6. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  7. package/dist/core/agents/live-authority-v3.js +0 -11
  8. package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
  9. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  10. package/dist/core/agents/live-lifecycle-v3.js +7 -28
  11. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  12. package/dist/core/agents/state/heartbeat-writer.js +14 -25
  13. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  14. package/dist/core/agents/state/live-coordination-writer.js +0 -5
  15. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  16. package/dist/core/agents/state/pidmap.js +0 -5
  17. package/dist/core/agents/state/proc-start.d.ts +5 -6
  18. package/dist/core/agents/state/proc-start.d.ts.map +1 -1
  19. package/dist/core/agents/state/proc-start.js +13 -9
  20. package/dist/core/agents/state/process-handle.d.ts +26 -0
  21. package/dist/core/agents/state/process-handle.d.ts.map +1 -0
  22. package/dist/core/agents/state/process-handle.js +121 -0
  23. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  24. package/dist/core/agents/state/stale-sweep.js +70 -180
  25. package/dist/core/artifacts/activity.d.ts +4 -1
  26. package/dist/core/artifacts/activity.d.ts.map +1 -1
  27. package/dist/core/artifacts/activity.js +8 -2
  28. package/dist/core/artifacts/index.d.ts.map +1 -1
  29. package/dist/core/artifacts/index.js +38 -12
  30. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  31. package/dist/core/events/v3/authority-outbox.js +0 -29
  32. package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
  33. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  34. package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
  35. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  36. package/dist/core/events/v3/producers/recorder.js +64 -77
  37. package/dist/core/hooks/cli.js +78 -48
  38. package/dist/core/resources/service.d.ts +2 -16
  39. package/dist/core/resources/service.d.ts.map +1 -1
  40. package/dist/core/resources/service.js +1 -78
  41. package/dist/core/semantic/service.d.ts.map +1 -1
  42. package/dist/core/semantic/service.js +2 -9
  43. package/dist/core/servers/index.d.ts +4 -0
  44. package/dist/core/servers/index.d.ts.map +1 -1
  45. package/dist/core/servers/index.js +21 -3
  46. package/dist/core/storage/durable-history.js +56 -22
  47. package/dist/core/storage/segments.d.ts.map +1 -1
  48. package/dist/core/storage/segments.js +73 -17
  49. package/dist/core/supervisor/service.d.ts +2 -4
  50. package/dist/core/supervisor/service.d.ts.map +1 -1
  51. package/dist/core/work/state.js +7 -7
  52. package/dist/core/workflow/engine.d.ts.map +1 -1
  53. package/dist/core/workflow/engine.js +21 -65
  54. package/dist/core/workflow/index.d.ts +4 -2
  55. package/dist/core/workflow/index.d.ts.map +1 -1
  56. package/dist/core/workflow/index.js +2 -1
  57. package/dist/core/workflow/live-session-v3.d.ts +0 -6
  58. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  59. package/dist/core/workflow/live-session-v3.js +5 -126
  60. package/dist/core/workflow/proof.d.ts +4 -4
  61. package/dist/core/workflow/proof.d.ts.map +1 -1
  62. package/dist/core/workflow/proof.js +4 -12
  63. package/dist/core/workflow/recovery-audit.d.ts +30 -0
  64. package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
  65. package/dist/core/workflow/recovery-audit.js +116 -0
  66. package/dist/core/workflow/run-state.d.ts +19 -2
  67. package/dist/core/workflow/run-state.d.ts.map +1 -1
  68. package/dist/core/workflow/run-state.js +113 -8
  69. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  70. package/dist/core/workflow/spawn-claude.js +0 -19
  71. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  72. package/dist/core/workflow/spawn-codex.js +0 -13
  73. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  74. package/dist/core/workflow/spawn-cursor.js +0 -19
  75. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  76. package/dist/core/workflow/spawn-opencode.js +0 -14
  77. package/dist/core/workflow/types.d.ts +11 -54
  78. package/dist/core/workflow/types.d.ts.map +1 -1
  79. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  80. package/dist/core/workflow/workspaces/local-git.js +70 -14
  81. package/dist/core/workflow/workspaces/reclaim.d.ts +6 -1
  82. package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
  83. package/dist/core/workflow/workspaces/recovery.d.ts +2 -0
  84. package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
  85. package/dist/core/workflow/workspaces/recovery.js +24 -0
  86. package/dist/lib/identities/assume.d.ts.map +1 -1
  87. package/dist/lib/identities/assume.js +0 -4
  88. package/package.json +1 -1
  89. package/src/commands/workflow.ts +59 -0
  90. package/src/core/agents/coord-client.ts +20 -0
  91. package/src/core/agents/live-authority-v3.ts +0 -17
  92. package/src/core/agents/live-lifecycle-v3.ts +14 -52
  93. package/src/core/agents/state/heartbeat-writer.ts +12 -29
  94. package/src/core/agents/state/live-coordination-writer.ts +0 -29
  95. package/src/core/agents/state/pidmap.ts +0 -13
  96. package/src/core/agents/state/proc-start.ts +14 -11
  97. package/src/core/agents/state/process-handle.ts +165 -0
  98. package/src/core/agents/state/stale-sweep.ts +82 -247
  99. package/src/core/artifacts/activity.ts +9 -2
  100. package/src/core/artifacts/index.ts +45 -12
  101. package/src/core/events/v3/authority-outbox.ts +0 -56
  102. package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
  103. package/src/core/events/v3/producers/recorder.ts +68 -94
  104. package/src/core/hooks/cli.ts +85 -47
  105. package/src/core/resources/service.ts +3 -88
  106. package/src/core/semantic/service.ts +2 -8
  107. package/src/core/servers/index.ts +30 -3
  108. package/src/core/storage/durable-history.ts +52 -20
  109. package/src/core/storage/segments.ts +79 -17
  110. package/src/core/supervisor/service.ts +2 -3
  111. package/src/core/work/state.ts +7 -7
  112. package/src/core/workflow/engine.ts +21 -73
  113. package/src/core/workflow/index.ts +12 -1
  114. package/src/core/workflow/live-session-v3.ts +5 -146
  115. package/src/core/workflow/proof.ts +5 -21
  116. package/src/core/workflow/recovery-audit.ts +181 -0
  117. package/src/core/workflow/run-state.ts +137 -8
  118. package/src/core/workflow/spawn-claude.ts +0 -18
  119. package/src/core/workflow/spawn-codex.ts +0 -13
  120. package/src/core/workflow/spawn-cursor.ts +0 -18
  121. package/src/core/workflow/spawn-opencode.ts +0 -14
  122. package/src/core/workflow/types.ts +11 -41
  123. package/src/core/workflow/workspaces/local-git.ts +86 -15
  124. package/src/core/workflow/workspaces/reclaim.ts +4 -0
  125. package/src/core/workflow/workspaces/recovery.ts +23 -0
  126. package/src/lib/identities/assume.ts +0 -4
  127. package/dist/core/agents/state/unchanged-file.d.ts +0 -44
  128. package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
  129. package/dist/core/agents/state/unchanged-file.js +0 -390
  130. package/dist/core/events/v3/generation-custody.d.ts +0 -5
  131. package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
  132. package/dist/core/events/v3/generation-custody.js +0 -50
  133. package/dist/core/workflow/failure-evidence.d.ts +0 -16
  134. package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
  135. package/dist/core/workflow/failure-evidence.js +0 -114
  136. package/src/core/agents/state/unchanged-file.ts +0 -456
  137. package/src/core/events/v3/generation-custody.ts +0 -59
  138. package/src/core/workflow/failure-evidence.ts +0 -141
@@ -9,6 +9,7 @@ import {
9
9
  } from "node:fs";
10
10
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
11
11
  import { stateDirMode, stateFileMode } from "../../storage/modes.ts";
12
+ import { beginWorkflowRecovery, validateWorkflowRecoveryAuthorization } from "../recovery-audit.ts";
12
13
  import { acquireWorkflowResumeLease } from "../run-state.ts";
13
14
  import { WorkspaceAttestationError } from "./attestation-error.ts";
14
15
  import { cleanupWorkspace } from "./cleanup.ts";
@@ -53,7 +54,7 @@ import {
53
54
  type WorkspaceReclaimPreview,
54
55
  type WorkspaceReclaimResult,
55
56
  } from "./reclaim.ts";
56
- import { readWorkflowLiveness } from "./recovery.ts";
57
+ import { hasActiveWorkflowEvidence, readWorkflowLiveness } from "./recovery.ts";
57
58
  import {
58
59
  appendWorkspaceEvent,
59
60
  fileSha256,
@@ -820,6 +821,7 @@ function reclaimPreview(
820
821
  run_id: binding.run_id,
821
822
  mode,
822
823
  liveness: readWorkflowLiveness(coordRoot, binding.run_id),
824
+ evidence_sha256: reclaimEvidenceDigest(coordRoot, binding),
823
825
  ownership: "unproven",
824
826
  dirty_paths: null,
825
827
  lease_holder: reclaimLeaseHolders(coordRoot, binding),
@@ -839,14 +841,45 @@ function reclaimPreview(
839
841
  return preview;
840
842
  }
841
843
 
842
- function assertReclaimLiveness(coordRoot: string, binding: WorkspaceBinding): void {
844
+ function reclaimEvidenceDigest(coordRoot: string, binding: WorkspaceBinding): string {
845
+ const evidence = (name: string): string | null => {
846
+ try {
847
+ return readFileSync(join(coordRoot, ".harnery", "workflows", binding.run_id, name), "utf8");
848
+ } catch (error) {
849
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
850
+ throw error;
851
+ }
852
+ };
853
+ return stableDigest({
854
+ binding,
855
+ proof: evidence("proof.json"),
856
+ transcript: evidence("transcript.jsonl"),
857
+ });
858
+ }
859
+
860
+ function assertReclaimLiveness(
861
+ coordRoot: string,
862
+ binding: WorkspaceBinding,
863
+ recovery?: WorkspaceReclaimOptions["unknownLiveness"],
864
+ ): void {
843
865
  const liveness = readWorkflowLiveness(coordRoot, binding.run_id);
866
+ if (liveness === "active" || (recovery && hasActiveWorkflowEvidence(coordRoot, binding.run_id))) {
867
+ throw new WorkflowReclaimError(
868
+ "workflow_reclaim_run_live",
869
+ `run ${binding.run_id} has active evidence; its workspace was left untouched`,
870
+ );
871
+ }
872
+ if (recovery && reclaimEvidenceDigest(coordRoot, binding) !== recovery.evidenceSha256) {
873
+ throw new WorkflowReclaimError(
874
+ "workflow_reclaim_recovery_mismatch",
875
+ "workflow reclaim requires the exact current preview evidence digest",
876
+ );
877
+ }
844
878
  if (liveness === "inactive") return;
879
+ if (liveness === "unknown" && recovery) return;
845
880
  throw new WorkflowReclaimError(
846
- liveness === "active" ? "workflow_reclaim_run_live" : "workflow_reclaim_liveness_unknown",
847
- liveness === "active"
848
- ? `run ${binding.run_id} is active; its workspace was left untouched`
849
- : `run ${binding.run_id} has missing, corrupt, or ambiguous liveness evidence; its workspace was left untouched`,
881
+ "workflow_reclaim_liveness_unknown",
882
+ `run ${binding.run_id} has missing, corrupt, or ambiguous liveness evidence; its workspace was left untouched`,
850
883
  );
851
884
  }
852
885
 
@@ -892,10 +925,11 @@ function reclaimAuthorityGuard(
892
925
  binding: WorkspaceBinding,
893
926
  claim: WorkspaceClaim,
894
927
  reservation: WorkspaceReclaimReservation,
928
+ recovery?: WorkspaceReclaimOptions["unknownLiveness"],
895
929
  ): void {
896
930
  try {
897
931
  reservation.assertCurrent();
898
- assertReclaimLiveness(coordRoot, binding);
932
+ assertReclaimLiveness(coordRoot, binding, recovery);
899
933
  assertReclaimOwnership(coordRoot, binding);
900
934
  assertPeerWorkspaceAuthority(coordRoot, claim);
901
935
  } catch (error) {
@@ -914,10 +948,11 @@ async function reclaim(
914
948
  throw new Error("unsupported workspace reclaim mode");
915
949
  }
916
950
  if (!options.yes) return reclaimPreview(coordRoot, binding, options.mode);
951
+ if (options.unknownLiveness) validateWorkflowRecoveryAuthorization(options.unknownLiveness);
917
952
 
918
953
  // Liveness comes before terminal-proof cleanup helpers. A live run may not
919
954
  // have a proof yet, and a missing proof must never be discovered after reset.
920
- assertReclaimLiveness(coordRoot, binding);
955
+ assertReclaimLiveness(coordRoot, binding, options.unknownLiveness);
921
956
  const claim = assertReclaimOwnership(coordRoot, binding);
922
957
  let reservation: WorkspaceReclaimReservation;
923
958
  try {
@@ -926,7 +961,7 @@ async function reclaim(
926
961
  throw new WorkflowReclaimError("workflow_reclaim_lease_held", (error as Error).message);
927
962
  }
928
963
  try {
929
- reclaimAuthorityGuard(coordRoot, binding, claim, reservation);
964
+ reclaimAuthorityGuard(coordRoot, binding, claim, reservation, options.unknownLiveness);
930
965
  let releaseResume: () => void;
931
966
  try {
932
967
  releaseResume = acquireWorkflowResumeLease(coordRoot, binding.run_id);
@@ -953,9 +988,24 @@ async function reclaim(
953
988
  throw new WorkflowReclaimError("workflow_reclaim_lease_held", (error as Error).message);
954
989
  }
955
990
  try {
956
- assertReclaimLiveness(coordRoot, binding);
991
+ assertReclaimLiveness(coordRoot, binding, options.unknownLiveness);
957
992
  assertReclaimOwnership(coordRoot, binding);
958
993
  const preview = reclaimPreview(coordRoot, binding, options.mode);
994
+ const audit = options.unknownLiveness
995
+ ? beginWorkflowRecovery(coordRoot, {
996
+ run_id: binding.run_id,
997
+ action: "reclaim_unknown_liveness",
998
+ actor: options.unknownLiveness.actor,
999
+ reason: options.unknownLiveness.reason,
1000
+ target_sha256: options.unknownLiveness.evidenceSha256,
1001
+ target: {
1002
+ binding_id: binding.binding_id,
1003
+ workspace_root: binding.workspace_root,
1004
+ mode: options.mode,
1005
+ liveness: preview.liveness,
1006
+ },
1007
+ })
1008
+ : undefined;
959
1009
  let preparation: ReclaimPreparation | undefined;
960
1010
  // Keep ordinary cleanup's terminal-evidence checks, intent and receipts.
961
1011
  // Its provider callback uses the leases already held here instead of
@@ -967,12 +1017,28 @@ async function reclaim(
967
1017
  provider: {
968
1018
  ...provider,
969
1019
  cleanup: async (candidate, intent) => {
970
- assertReclaimLiveness(coordRoot, candidate);
1020
+ assertReclaimLiveness(coordRoot, candidate, options.unknownLiveness);
971
1021
  assertReclaimOwnership(coordRoot, candidate);
972
1022
  return cleanupUnderLease(coordRoot, candidate, claim, intent, {
973
1023
  mode: options.mode,
974
- beforeEffect: () =>
975
- reclaimAuthorityGuard(coordRoot, candidate, claim, reservation),
1024
+ recovery: options.unknownLiveness,
1025
+ beforeEffect: () => {
1026
+ audit?.assertCurrent();
1027
+ reclaimAuthorityGuard(
1028
+ coordRoot,
1029
+ candidate,
1030
+ claim,
1031
+ reservation,
1032
+ options.unknownLiveness,
1033
+ );
1034
+ },
1035
+ beforeRefEffect: () => {
1036
+ audit?.assertCurrent();
1037
+ reservation.assertCurrent();
1038
+ assertReclaimLiveness(coordRoot, candidate, options.unknownLiveness);
1039
+ requiredClaim(coordRoot, candidate);
1040
+ assertPeerWorkspaceAuthority(coordRoot, claim);
1041
+ },
976
1042
  prepared: (value) => {
977
1043
  preparation = value;
978
1044
  },
@@ -991,6 +1057,8 @@ async function reclaim(
991
1057
  "workspace cleanup preconditions could not be proved",
992
1058
  );
993
1059
  }
1060
+ if (cleanupResult.status === "released" || cleanupResult.status === "already_released")
1061
+ audit?.complete();
994
1062
  return {
995
1063
  ...preview,
996
1064
  action: "reclaimed",
@@ -1104,7 +1172,9 @@ function cleanupUnderLease(
1104
1172
  intent: WorkspaceCleanupIntent,
1105
1173
  reclaimRequest?: {
1106
1174
  mode: ReclaimMode;
1175
+ recovery?: WorkspaceReclaimOptions["unknownLiveness"];
1107
1176
  beforeEffect: () => void;
1177
+ beforeRefEffect: () => void;
1108
1178
  prepared: (value: ReclaimPreparation) => void;
1109
1179
  },
1110
1180
  beforeRemoval?: () => void,
@@ -1152,7 +1222,7 @@ function cleanupUnderLease(
1152
1222
  };
1153
1223
  }
1154
1224
  const liveness = readWorkflowLiveness(coordRoot, binding.run_id);
1155
- if (liveness !== "inactive") {
1225
+ if (liveness !== "inactive" && !(liveness === "unknown" && reclaimRequest?.recovery)) {
1156
1226
  return {
1157
1227
  ...base,
1158
1228
  status: "blocked",
@@ -1245,7 +1315,7 @@ function cleanupUnderLease(
1245
1315
  }
1246
1316
 
1247
1317
  if (reclaimRequest) {
1248
- assertReclaimLiveness(coordRoot, binding);
1318
+ assertReclaimLiveness(coordRoot, binding, reclaimRequest.recovery);
1249
1319
  assertReclaimOwnership(coordRoot, binding);
1250
1320
  revalidateClaimResources(claim, root);
1251
1321
  reclaimRequest.prepared(
@@ -1293,6 +1363,7 @@ function cleanupUnderLease(
1293
1363
  return { ...base, status: "blocked", reason: "workspace ref changed during cleanup" };
1294
1364
  }
1295
1365
  revalidateClaimResources(claim, root);
1366
+ reclaimRequest?.beforeRefEffect();
1296
1367
  const deleted = gitMaybe(repository.source_root.realpath, [
1297
1368
  "update-ref",
1298
1369
  "-d",
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  import { existsSync } from "node:fs";
19
+ import type { WorkflowRecoveryAuthorization } from "../recovery-audit.ts";
19
20
  import { git, gitMaybe } from "./git.ts";
20
21
  import type { WorkflowLiveness } from "./recovery.ts";
21
22
  import type { WorkspaceCleanupAttempt, WorkspaceCleanupReceipt } from "./types.ts";
@@ -25,6 +26,7 @@ export type ReclaimMode = "salvage" | "discard";
25
26
  export type WorkflowReclaimRefusalCode =
26
27
  | "workflow_reclaim_run_live"
27
28
  | "workflow_reclaim_liveness_unknown"
29
+ | "workflow_reclaim_recovery_mismatch"
28
30
  | "workflow_reclaim_ownership_unproven"
29
31
  | "workflow_reclaim_lease_held";
30
32
 
@@ -41,6 +43,7 @@ export class WorkflowReclaimError extends Error {
41
43
  export interface WorkspaceReclaimOptions {
42
44
  mode: ReclaimMode;
43
45
  yes?: boolean;
46
+ unknownLiveness?: WorkflowRecoveryAuthorization & { evidenceSha256: string };
44
47
  }
45
48
 
46
49
  export interface ReclaimLeaseHolder {
@@ -56,6 +59,7 @@ export interface WorkspaceReclaimPreview {
56
59
  run_id: string;
57
60
  mode: ReclaimMode;
58
61
  liveness: WorkflowLiveness;
62
+ evidence_sha256: string;
59
63
  ownership: "ok" | "unproven";
60
64
  ownership_detail?: string;
61
65
  dirty_paths: string[] | null;
@@ -4,6 +4,29 @@ import { readWorkflowProof } from "../proof.ts";
4
4
 
5
5
  export type WorkflowLiveness = "active" | "inactive" | "unknown";
6
6
 
7
+ /** Corruption must not hide a readable active marker from an audited override. */
8
+ export function hasActiveWorkflowEvidence(coordRoot: string, runId: string): boolean {
9
+ const path = join(resolve(coordRoot), ".harnery", "workflows", runId, "transcript.jsonl");
10
+ if (!existsSync(path)) return false;
11
+ let active = false;
12
+ for (const line of readFileSync(path, "utf8").split("\n")) {
13
+ try {
14
+ const event = JSON.parse(line);
15
+ if (event?.run_id !== runId) continue;
16
+ if (["run.start", "run.resume", "agent.start"].includes(event.event)) active = true;
17
+ if (
18
+ event.event === "run.parked" ||
19
+ (event.event === "run.end" && typeof event.ok === "boolean") ||
20
+ (event.event === "workspace.reattach.failed" && ["blocked", "lost"].includes(event.status))
21
+ )
22
+ active = false;
23
+ } catch {
24
+ /* Unreadable evidence cannot establish inactivity. */
25
+ }
26
+ }
27
+ return active;
28
+ }
29
+
7
30
  export function readWorkflowLiveness(coordRoot: string, runId: string): WorkflowLiveness {
8
31
  const root = resolve(coordRoot);
9
32
  const transcriptPath = join(root, ".harnery", "workflows", runId, "transcript.jsonl");
@@ -140,7 +140,6 @@ export function reclaimAbandonedLocalConflict(
140
140
  let sessionId = conflict.instance_id;
141
141
  let ageSecs: number | undefined;
142
142
  const liveRow = readLiveCoordinationRow(coordRoot, conflict.instance_id);
143
- let expectedGenerationId = liveRow?.v3_generation_id ?? null;
144
143
  if (liveRow) {
145
144
  adapter = adapterOf(liveRow.platform);
146
145
  sessionId = liveRow.session_id || conflict.instance_id;
@@ -154,9 +153,7 @@ export function reclaimAbandonedLocalConflict(
154
153
  // It is required to join the exact producer state when recording the sweep.
155
154
  adapter = adapterOf(hb.platform);
156
155
  sessionId = hb.session_id || conflict.instance_id;
157
- expectedGenerationId = hb.v3_generation_id ?? null;
158
156
  } catch {
159
- expectedGenerationId = null;
160
157
  /* content unreadable — still reclaim the file */
161
158
  }
162
159
  try {
@@ -171,7 +168,6 @@ export function reclaimAbandonedLocalConflict(
171
168
  owner: conflict.instance_id,
172
169
  nativeSessionId: sessionId,
173
170
  adapter,
174
- expectedGenerationId,
175
171
  observation: "stale_heartbeat",
176
172
  ageMs: Math.max(0, (ageSecs ?? 0) * 1_000),
177
173
  });
@@ -1,44 +0,0 @@
1
- export interface FileRevision {
2
- readonly bytes: Buffer;
3
- readonly dev: bigint;
4
- readonly ino: bigint;
5
- readonly size: bigint;
6
- readonly mtimeNs: bigint;
7
- readonly ctimeNs: bigint;
8
- }
9
- export type DeleteFileResult = {
10
- state: "deleted" | "changed" | "missing" | "rejected";
11
- } | {
12
- state: "recovery-required";
13
- recoveryPath: string;
14
- error?: string;
15
- } | {
16
- state: "error";
17
- recoveryPath?: string;
18
- error: string;
19
- };
20
- export interface FileRecoveryResult {
21
- state: "restored" | "retained" | "error";
22
- recoveryPath: string;
23
- sourcePath?: string;
24
- error?: string;
25
- }
26
- /** A stable read of one regular file, including its exact bytes and revision. */
27
- export declare function captureFileRevision(filePath: string): FileRevision | null;
28
- export declare function fileRevisionMatches(filePath: string, expected: FileRevision): boolean;
29
- /**
30
- * Writers must publish by atomic rename, never by an open descriptor to the old
31
- * inode. The private directory is outside their publication namespace. Moving
32
- * the row gives deletion custody over that inode without owning its live name.
33
- * The caller owns the state parent and keeps its topology stable. Its platform
34
- * permissions define which principals can modify it; this protocol does not
35
- * exclude principals already authorized to change that parent.
36
- */
37
- export declare function deleteFileIfUnchanged(filePath: string, expected: FileRevision, validate: (pinned: FileRevision) => boolean): DeleteFileResult;
38
- /**
39
- * Recover same-host/platform/PID-namespace pins after process termination. File fsync
40
- * protects manifest contents; directory entries are not fsynced, so this does
41
- * not promise recovery after an OS crash or power loss.
42
- */
43
- export declare function recoverInterruptedFileDeletions(directoryPath: string, validate: (sourcePath: string) => boolean): FileRecoveryResult[];
44
- //# sourceMappingURL=unchanged-file.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"unchanged-file.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/unchanged-file.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAA;CAAE,GACzD;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAoCzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAGrF;AAsGD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,GAC1C,gBAAgB,CAmFlB;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,GACxC,kBAAkB,EAAE,CA+FtB"}