harnery 0.45.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 (128) hide show
  1. package/dist/commander.js +0 -1
  2. package/dist/commands/files.d.ts +2 -2
  3. package/dist/commands/files.d.ts.map +1 -1
  4. package/dist/commands/files.js +4 -7
  5. package/dist/commands/servers.d.ts.map +1 -1
  6. package/dist/commands/servers.js +17 -11
  7. package/dist/commands/tunnel.d.ts +0 -15
  8. package/dist/commands/tunnel.d.ts.map +1 -1
  9. package/dist/commands/tunnel.js +153 -453
  10. package/dist/core/agents/live-authority-v3.d.ts +0 -1
  11. package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
  12. package/dist/core/agents/live-authority-v3.js +0 -11
  13. package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
  14. package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
  15. package/dist/core/agents/live-lifecycle-v3.js +7 -28
  16. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  17. package/dist/core/agents/state/heartbeat-writer.js +14 -25
  18. package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
  19. package/dist/core/agents/state/live-coordination-writer.js +0 -5
  20. package/dist/core/agents/state/pidmap.d.ts.map +1 -1
  21. package/dist/core/agents/state/pidmap.js +0 -5
  22. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  23. package/dist/core/agents/state/stale-sweep.js +70 -180
  24. package/dist/core/artifacts/activity.d.ts +4 -1
  25. package/dist/core/artifacts/activity.d.ts.map +1 -1
  26. package/dist/core/artifacts/activity.js +8 -2
  27. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  28. package/dist/core/artifacts/delivery-card.js +1 -2
  29. package/dist/core/artifacts/index.d.ts.map +1 -1
  30. package/dist/core/artifacts/index.js +38 -12
  31. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  32. package/dist/core/events/v3/authority-outbox.js +0 -29
  33. package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
  34. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  35. package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
  36. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  37. package/dist/core/events/v3/producers/recorder.js +64 -77
  38. package/dist/core/hooks/cli.js +78 -48
  39. package/dist/core/servers/index.d.ts +13 -10
  40. package/dist/core/servers/index.d.ts.map +1 -1
  41. package/dist/core/servers/index.js +97 -192
  42. package/dist/core/servers/tunnels.d.ts +3 -5
  43. package/dist/core/servers/tunnels.d.ts.map +1 -1
  44. package/dist/core/servers/tunnels.js +2 -45
  45. package/dist/core/storage/durable-history.js +56 -22
  46. package/dist/core/storage/segments.d.ts.map +1 -1
  47. package/dist/core/storage/segments.js +73 -17
  48. package/dist/core/work/state.js +7 -7
  49. package/dist/core/workflow/engine.d.ts.map +1 -1
  50. package/dist/core/workflow/engine.js +21 -65
  51. package/dist/core/workflow/live-session-v3.d.ts +0 -6
  52. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  53. package/dist/core/workflow/live-session-v3.js +5 -126
  54. package/dist/core/workflow/proof.d.ts +4 -4
  55. package/dist/core/workflow/proof.d.ts.map +1 -1
  56. package/dist/core/workflow/proof.js +4 -12
  57. package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
  58. package/dist/core/workflow/spawn-claude.js +0 -19
  59. package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
  60. package/dist/core/workflow/spawn-codex.js +0 -13
  61. package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
  62. package/dist/core/workflow/spawn-cursor.js +0 -19
  63. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
  64. package/dist/core/workflow/spawn-opencode.js +0 -14
  65. package/dist/core/workflow/types.d.ts +11 -54
  66. package/dist/core/workflow/types.d.ts.map +1 -1
  67. package/dist/lib/identities/assume.d.ts.map +1 -1
  68. package/dist/lib/identities/assume.js +0 -4
  69. package/dist/lib/tunnel/state.d.ts +6 -13
  70. package/dist/lib/tunnel/state.d.ts.map +1 -1
  71. package/dist/lib/tunnel/state.js +12 -58
  72. package/package.json +1 -6
  73. package/src/commander.ts +0 -10
  74. package/src/commands/files.ts +5 -13
  75. package/src/commands/servers.ts +23 -12
  76. package/src/commands/tunnel.ts +166 -533
  77. package/src/core/agents/live-authority-v3.ts +0 -17
  78. package/src/core/agents/live-lifecycle-v3.ts +14 -52
  79. package/src/core/agents/state/heartbeat-writer.ts +12 -29
  80. package/src/core/agents/state/live-coordination-writer.ts +0 -29
  81. package/src/core/agents/state/pidmap.ts +0 -13
  82. package/src/core/agents/state/stale-sweep.ts +82 -247
  83. package/src/core/artifacts/activity.ts +9 -2
  84. package/src/core/artifacts/delivery-card.ts +1 -7
  85. package/src/core/artifacts/index.ts +45 -12
  86. package/src/core/events/v3/authority-outbox.ts +0 -56
  87. package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
  88. package/src/core/events/v3/producers/recorder.ts +68 -94
  89. package/src/core/hooks/cli.ts +85 -47
  90. package/src/core/servers/index.ts +106 -228
  91. package/src/core/servers/tunnels.ts +3 -55
  92. package/src/core/storage/durable-history.ts +52 -20
  93. package/src/core/storage/segments.ts +79 -17
  94. package/src/core/work/state.ts +7 -7
  95. package/src/core/workflow/engine.ts +21 -73
  96. package/src/core/workflow/live-session-v3.ts +5 -146
  97. package/src/core/workflow/proof.ts +5 -21
  98. package/src/core/workflow/spawn-claude.ts +0 -18
  99. package/src/core/workflow/spawn-codex.ts +0 -13
  100. package/src/core/workflow/spawn-cursor.ts +0 -18
  101. package/src/core/workflow/spawn-opencode.ts +0 -14
  102. package/src/core/workflow/types.ts +11 -41
  103. package/src/lib/identities/assume.ts +0 -4
  104. package/src/lib/tunnel/state.ts +15 -70
  105. package/dist/commands/turn-rollback.d.ts +0 -5
  106. package/dist/commands/turn-rollback.d.ts.map +0 -1
  107. package/dist/commands/turn-rollback.js +0 -76
  108. package/dist/core/agents/state/unchanged-file.d.ts +0 -44
  109. package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
  110. package/dist/core/agents/state/unchanged-file.js +0 -390
  111. package/dist/core/events/v3/generation-custody.d.ts +0 -5
  112. package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
  113. package/dist/core/events/v3/generation-custody.js +0 -50
  114. package/dist/core/turn-rollback/content.d.ts +0 -53
  115. package/dist/core/turn-rollback/content.d.ts.map +0 -1
  116. package/dist/core/turn-rollback/content.js +0 -396
  117. package/dist/core/turn-rollback/index.d.ts +0 -72
  118. package/dist/core/turn-rollback/index.d.ts.map +0 -1
  119. package/dist/core/turn-rollback/index.js +0 -443
  120. package/dist/core/workflow/failure-evidence.d.ts +0 -16
  121. package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
  122. package/dist/core/workflow/failure-evidence.js +0 -114
  123. package/src/commands/turn-rollback.ts +0 -111
  124. package/src/core/agents/state/unchanged-file.ts +0 -456
  125. package/src/core/events/v3/generation-custody.ts +0 -59
  126. package/src/core/turn-rollback/content.ts +0 -499
  127. package/src/core/turn-rollback/index.ts +0 -580
  128. package/src/core/workflow/failure-evidence.ts +0 -141
@@ -22,7 +22,6 @@ import { builtinAdapterProfile, validateAdapterEffort } from "../adapters/profil
22
22
  import type { AdapterInvocation, AdapterRawResult } from "../adapters/types.ts";
23
23
  import { notFoundError } from "./adapters.ts";
24
24
  import { buildChildEnv } from "./child-env.ts";
25
- import { observeSpawnFailure } from "./failure-evidence.ts";
26
25
  import { resolveFullAccessArgv, resolveSandboxProjection } from "./sandbox-projection.ts";
27
26
  import { isUpstreamFailureText, vendorFailureText } from "./spawn-failure.ts";
28
27
  import type { Spawner, SpawnRequest, SpawnResult } from "./types.ts";
@@ -89,19 +88,6 @@ export function buildOpenCodeInvocation(req: SpawnRequest): AdapterInvocation {
89
88
  }
90
89
 
91
90
  export function normalizeOpenCodeResult(raw: AdapterRawResult): SpawnResult {
92
- const result = normalizeOpenCodeTransportResult(raw);
93
- if (result.ok) return result;
94
- const parsed = parseOpenCodeStream(raw.stdout);
95
- const text = parsed.text || raw.stdout.trim();
96
- return {
97
- ...result,
98
- text,
99
- sessionId: parsed.sessionId,
100
- failureEvidence: observeSpawnFailure(raw, text, parsed.text ? "vendor_envelope" : "stdout"),
101
- };
102
- }
103
-
104
- function normalizeOpenCodeTransportResult(raw: AdapterRawResult): SpawnResult {
105
91
  if (raw.timedOut) {
106
92
  return {
107
93
  ok: false,
@@ -137,12 +137,11 @@ export interface WorkflowAgentProof {
137
137
  session_id?: string;
138
138
  result?: ResultDigest;
139
139
  error?: string;
140
- /** Failed-spawn transport class. It is retained even when a script swallows
141
- * the rejection; charging additionally requires validated failure evidence. */
140
+ /** Set on a failed agent whose spawn was classified uninformative about the
141
+ * work (ADR 0046): environment (binary absent) or upstream (vendor refused).
142
+ * Absent ⇒ a work failure. Recorded even when a script's parallel() swallows
143
+ * the rejection, so the run-level class can still be derived from proof. */
142
144
  class?: SpawnFailureClass;
143
- /** Failure observations carried from the adapter. Missing observations are
144
- * unknown and cannot authorize an uncharged attempt. */
145
- failure_evidence?: SpawnFailureEvidence;
146
145
  /** Which vendor sandbox mode this agent launched with and why (ADR 0192).
147
146
  * Absent on a cached agent, which launched nothing. */
148
147
  access?: WorkerAccessDecision;
@@ -363,16 +362,15 @@ export interface WorkflowSpecialistProfile {
363
362
  }
364
363
 
365
364
  /**
366
- * The positively identified transport cause of a failed spawn (ADR 0046).
367
- * This class alone proves neither absence of effects nor retry eligibility.
365
+ * Why a failed run was uninformative about the work (ADR 0046). Absent means
366
+ * the attempt produced information about the work (or succeeded), which is the
367
+ * default and is charged against the attempt budget exactly as before.
368
368
  *
369
369
  * - `environment`: the run never started — a precondition was missing (the
370
- * vendor binary was absent). With positive no-effect/output evidence it is
371
- * uncharged and not retried: retrying an unchanged
370
+ * vendor binary was absent). Uncharged AND not retried: retrying an unchanged
372
371
  * environment cannot help, so the work item stops and names the precondition.
373
372
  * - `upstream`: the vendor was reached and refused (5xx, 429, circuit open).
374
- * Retry/free-attempt eligibility also requires positive no-effect evidence,
375
- * observed absent output, and no refusal. Unsupported evidence stays unknown.
373
+ * Uncharged, but retry stays available (bounded by max_uncharged_attempts).
376
374
  */
377
375
  export type SpawnFailureClass = "environment" | "upstream";
378
376
 
@@ -394,30 +392,6 @@ export type SpawnFailureClass = "environment" | "upstream";
394
392
  */
395
393
  export type RunFailureClass = SpawnFailureClass | "decision";
396
394
 
397
- /** Positive claims name the observation that supports them. A transport error
398
- * or an empty reply is never evidence that no effects occurred. */
399
- export type SpawnEffectEvidence =
400
- | { state: "unknown" }
401
- | { state: "none"; source: "spawn_errno"; reference: "ENOENT" }
402
- | { state: "none" | "observed"; source: "adapter_evidence"; reference: string };
403
-
404
- export type SpawnRefusalEvidence =
405
- | { state: "unknown" }
406
- | { state: "refused"; source: "vendor_envelope"; reference: string };
407
-
408
- export type SpawnOutputSource = "final_message" | "vendor_envelope" | "stdout";
409
-
410
- export type SpawnPartialOutputEvidence =
411
- | { state: "unknown" }
412
- | { state: "absent"; source: SpawnOutputSource }
413
- | { state: "present"; source: SpawnOutputSource; bytes: number; sha256: string };
414
-
415
- export interface SpawnFailureEvidence {
416
- effect: SpawnEffectEvidence;
417
- refusal: SpawnRefusalEvidence;
418
- partial_output: SpawnPartialOutputEvidence;
419
- }
420
-
421
395
  /** What a spawn adapter returns for one subagent run. */
422
396
  export interface SpawnResult {
423
397
  ok: boolean;
@@ -429,13 +403,9 @@ export interface SpawnResult {
429
403
  durationMs: number;
430
404
  /** Populated when ok=false. */
431
405
  error?: string;
432
- /** Transport classification when ok=false; never sufficient by itself to
433
- * grant free work or automatic redispatch. */
406
+ /** Set when ok=false and the failure was positively identified as
407
+ * uninformative about the work. Absent ⇒ a work failure (charged). */
434
408
  class?: SpawnFailureClass;
435
- /** Transport classification alone grants neither a retry nor free work.
436
- * Absent evidence is explicitly unknown. Positive adapter_evidence claims
437
- * require a documented producer observation, never an HTTP/text heuristic. */
438
- failureEvidence?: SpawnFailureEvidence;
439
409
  }
440
410
 
441
411
  export interface SpawnRequest {
@@ -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
  });
@@ -2,7 +2,6 @@
2
2
  // <cwd>/.cache/tunnel/; gitignored, so the allowlist is per-machine.
3
3
 
4
4
  import { spawnSync } from "node:child_process";
5
- import { randomUUID } from "node:crypto";
6
5
  import {
7
6
  accessSync,
8
7
  chmodSync,
@@ -11,7 +10,6 @@ import {
11
10
  mkdirSync,
12
11
  readdirSync,
13
12
  readFileSync,
14
- renameSync,
15
13
  statSync,
16
14
  unlinkSync,
17
15
  writeFileSync,
@@ -106,12 +104,10 @@ export interface TunnelState {
106
104
  provider: TunnelProvider;
107
105
  url: string;
108
106
  gate_pid: number;
109
- gate_start_token?: string;
110
- cloudflared_start_token?: string;
111
- registration_id?: string;
112
- provider_state?: "pending" | "ready";
113
107
  /** Present for Cloudflare quick tunnels; absent for Tailscale Serve/Funnel. */
114
108
  cloudflared_pid?: number;
109
+ /** Optional provider-side process when a provider owns one. */
110
+ provider_pid?: number;
115
111
  started_at: string;
116
112
  target: string;
117
113
  vhost: string;
@@ -172,15 +168,7 @@ export function readState(
172
168
  }
173
169
 
174
170
  export function writeState(state: TunnelState, root: string = process.cwd()): void {
175
- const path = cachePath(stateFile(state.name), root);
176
- const temporary = `${path}.${randomUUID()}.tmp`;
177
- try {
178
- writeFileSync(temporary, JSON.stringify(state, null, 2), { flag: "wx", mode: 0o600 });
179
- renameSync(temporary, path);
180
- } catch (error) {
181
- if (existsSync(temporary)) unlinkSync(temporary);
182
- throw error;
183
- }
171
+ writeFileSync(cachePath(stateFile(state.name), root), JSON.stringify(state, null, 2));
184
172
  }
185
173
 
186
174
  export function clearState(name: string = DEFAULT_INSTANCE, root: string = process.cwd()): void {
@@ -214,60 +202,17 @@ export function isProcessAlive(pid: number): boolean {
214
202
  }
215
203
  }
216
204
 
217
- export type TunnelReadiness = "ready" | "dead" | "unknown";
218
- export type TunnelProcessObservation = "same" | "absent" | "different" | "unknown";
219
-
220
- export interface TunnelReadinessVerifier {
221
- observeProcess(pid: number, originalToken: string): TunnelProcessObservation;
222
- withRegistration(state: TunnelState, inspect: () => TunnelReadiness): TunnelReadiness;
223
- }
205
+ export type ProcessAliveCheck = (pid: number) => boolean;
224
206
 
225
- /** Classify saved original witnesses under the verifier's registration custody. */
226
- export function tunnelReadiness(
207
+ /** True when both the local gate and its provider-side process are live. */
208
+ export function isTunnelStateLive(
227
209
  state: TunnelState,
228
- verifier: TunnelReadinessVerifier,
229
- ): TunnelReadiness {
230
- if (
231
- !state ||
232
- typeof state !== "object" ||
233
- Object.hasOwn(state, "provider_pid") ||
234
- Object.hasOwn(state, "provider_start_token") ||
235
- typeof state.registration_id !== "string" ||
236
- !state.registration_id ||
237
- !Number.isSafeInteger(state.gate_pid) ||
238
- state.gate_pid < 1 ||
239
- typeof state.gate_start_token !== "string" ||
240
- !state.gate_start_token
241
- )
242
- return "unknown";
243
- const targets = [{ pid: state.gate_pid, token: state.gate_start_token }];
244
- if (state.provider === "tailscale") {
245
- if (
246
- state.provider_state !== "ready" ||
247
- state.cloudflared_pid !== undefined ||
248
- state.cloudflared_start_token !== undefined
249
- )
250
- return "unknown";
251
- } else if (state.provider === "cloudflare") {
252
- if (
253
- !Number.isSafeInteger(state.cloudflared_pid) ||
254
- state.cloudflared_pid! < 1 ||
255
- typeof state.cloudflared_start_token !== "string" ||
256
- !state.cloudflared_start_token
257
- )
258
- return "unknown";
259
- targets.push({ pid: state.cloudflared_pid!, token: state.cloudflared_start_token });
260
- } else return "unknown";
261
- try {
262
- return verifier.withRegistration(state, () => {
263
- const observed = targets.map(({ pid, token }) => verifier.observeProcess(pid, token));
264
- if (observed.every((value) => value === "absent")) return "dead";
265
- if (observed.every((value) => value === "same")) return "ready";
266
- return "unknown";
267
- });
268
- } catch {
269
- return "unknown";
270
- }
210
+ processAlive: ProcessAliveCheck = isProcessAlive,
211
+ ): boolean {
212
+ if (!processAlive(state.gate_pid)) return false;
213
+ if (state.provider === "tailscale") return true;
214
+ const providerPid = state.cloudflared_pid ?? state.provider_pid;
215
+ return typeof providerPid === "number" && processAlive(providerPid);
271
216
  }
272
217
 
273
218
  export function tunnelTargetPort(state: TunnelState): number | null {
@@ -297,15 +242,15 @@ export function tunnelTargetPort(state: TunnelState): number | null {
297
242
  export function findLiveTunnelForOrigin(
298
243
  port: number,
299
244
  vhost: string,
300
- root: string,
301
- verifier: TunnelReadinessVerifier,
245
+ root: string = process.cwd(),
246
+ processAlive: ProcessAliveCheck = isProcessAlive,
302
247
  ): TunnelState | null {
303
248
  return (
304
249
  listStates(root).find(
305
250
  (state) =>
306
251
  tunnelTargetPort(state) === port &&
307
252
  state.vhost === vhost &&
308
- tunnelReadiness(state, verifier) === "ready",
253
+ isTunnelStateLive(state, processAlive),
309
254
  ) ?? null
310
255
  );
311
256
  }
@@ -1,5 +0,0 @@
1
- import type { Command } from "commander";
2
- import type { EmitContext, HarneryProgramContext } from "../commander.js";
3
- /** Explicit turn boundaries; hooks cannot infer provider ownership or path scope. */
4
- export declare function registerTurnRollbackCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
5
- //# sourceMappingURL=turn-rollback.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"turn-rollback.d.ts","sourceRoot":"","sources":["../../src/commands/turn-rollback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAa1E,qFAAqF;AACrF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA2FN"}
@@ -1,76 +0,0 @@
1
- import { monorepoRoot } from "../core/agents/index.js";
2
- import { applyTurnRollback, beginTurnRollback, inspectTurnRollback, prepareTurnRollback, sealTurnRollback, } from "../core/turn-rollback/index.js";
3
- import { readJsonRecord } from "../core/workflow/durable-record.js";
4
- import { readWorkspaceBinding } from "../core/workflow/workspaces/state.js";
5
- /** Explicit turn boundaries; hooks cannot infer provider ownership or path scope. */
6
- export function registerTurnRollbackCommand(program, emit, context) {
7
- const command = program
8
- .command("turn-rollback")
9
- .description("Capture and review exact-path content rollback in an owned workspace");
10
- const run = (operation) => async (options) => {
11
- try {
12
- const coordRoot = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? monorepoRoot();
13
- if (!coordRoot)
14
- throw new Error("project coordination root is unavailable");
15
- emit.config({ format: "json" });
16
- const result = await operation(coordRoot, options);
17
- emit.data(result);
18
- if (result &&
19
- typeof result === "object" &&
20
- "status" in result &&
21
- result.status === "unknown")
22
- emit.setExitCode(1);
23
- }
24
- catch (error) {
25
- emit.error({ code: "turn_rollback_refused", message: String(error) });
26
- emit.setExitCode(1);
27
- }
28
- };
29
- const binding = (root, options) => {
30
- const value = readWorkspaceBinding(root, "local-git-worktree", options.binding);
31
- if (!value)
32
- throw new Error("immutable workspace binding was not found");
33
- return value;
34
- };
35
- command
36
- .command("begin")
37
- .description("Capture exact literal paths before a turn")
38
- .requiredOption("--binding <id>", "Owned workspace binding ID")
39
- .requiredOption("--turn <id>", "Caller turn identity")
40
- .requiredOption("--generation <number>", "Increasing turn generation")
41
- .requiredOption("--path <file...>", "Exact relative file paths, including planned new files")
42
- .action(run((root, options) => beginTurnRollback(root, {
43
- binding: binding(root, options),
44
- turn: { turn_id: options.turn, generation: Number(options.generation) },
45
- paths: options.path,
46
- })));
47
- for (const operation of ["seal", "prepare", "show"]) {
48
- command
49
- .command(`${operation} <capture-id>`)
50
- .description(`${operation} an exact turn capture`)
51
- .requiredOption("--binding <id>", "Owned workspace binding ID")
52
- .action((id, options) => run((root, opts) => {
53
- const input = { binding: binding(root, opts), capture_id: id };
54
- return operation === "seal"
55
- ? sealTurnRollback(root, input)
56
- : operation === "prepare"
57
- ? prepareTurnRollback(root, input)
58
- : inspectTurnRollback(root, input);
59
- })(options));
60
- }
61
- command
62
- .command("apply")
63
- .description("Preview a saved proposal, or apply it after exact confirmation")
64
- .requiredOption("--binding <id>", "Owned workspace binding ID")
65
- .requiredOption("--plan <file>", "Saved JSON proposal returned by prepare")
66
- .option("--yes", "Apply this exact proposal; preview is the default")
67
- .option("--actor <name>", "Who authorized the restore")
68
- .option("--reason <text>", "Why this turn is being restored")
69
- .action(run((root, options) => applyTurnRollback(root, {
70
- binding: binding(root, options),
71
- plan: readJsonRecord(options.plan, "rollback proposal"),
72
- yes: options.yes,
73
- actor: options.actor,
74
- reason: options.reason,
75
- })));
76
- }
@@ -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"}