{"version":3,"file":"index.mjs","names":["FileLock.make"],"sources":["../../../tooling/shared/src/run.ts","../src/file-lock.ts","../src/snapshot.ts","../src/index.ts"],"sourcesContent":["import { Cause, Effect, Exit, Inspectable } from 'effect'\n\n// What pi's own tools throw for a cancelled run, so interrupted plugin runs\n// read like interrupted builtins.\nconst ABORT_MESSAGE = 'Operation aborted'\n\nexport interface RunOptions {\n  readonly signal?: AbortSignal | undefined\n}\n\n// Defects keep their stack because they are bugs, and win over failures in a\n// mixed cause.\nfunction causeMessage(cause: Cause.Cause<unknown>): string {\n  if (Cause.hasInterruptsOnly(cause)) {\n    return ABORT_MESSAGE\n  }\n  if (Cause.hasDies(cause)) {\n    return Cause.pretty(cause)\n  }\n  return cause.reasons\n    .filter(Cause.isFailReason)\n    .map(({ error }) => {\n      if (error instanceof Error) {\n        return error.message || error.name\n      }\n      return typeof error === 'string' ? error : Inspectable.toStringUnknown(error)\n    })\n    .join('\\n')\n}\n\n/**\n * Failure rejects with an `Error` whose message is what the model reads back\n * as the tool result, so it is written for the model rather than for a log.\n */\nexport async function runTool<A, E>(\n  effect: Effect.Effect<A, E>,\n  options?: RunOptions,\n): Promise<A> {\n  const exit = await Effect.runPromiseExit(effect, options)\n  if (Exit.isFailure(exit)) {\n    throw new Error(causeMessage(exit.cause))\n  }\n  return exit.value\n}\n\nexport interface RunHandlerOptions<B> {\n  readonly onError?: (message: string) => B\n}\n\n/**\n * For boundaries where pi reports rejections but expected failures should\n * degrade gracefully (event hooks, commands, background work). Expected\n * failures and interrupts never reject. Without `onError` they vanish, while\n * defects rethrow so pi's handler boundary reports the bug.\n */\nexport async function runHandler<A, E, B = undefined>(\n  effect: Effect.Effect<A, E>,\n  options?: RunHandlerOptions<B>,\n): Promise<A | B> {\n  const exit = await Effect.runPromiseExit(effect)\n  if (Exit.isSuccess(exit)) {\n    return exit.value\n  }\n  if (options?.onError) {\n    return options.onError(causeMessage(exit.cause))\n  }\n  const die = exit.cause.reasons.find(Cause.isDieReason)\n  if (die !== undefined) {\n    throw die.defect\n  }\n  return undefined as B\n}\n","import {\n  Data,\n  DateTime,\n  Duration,\n  Effect,\n  FileSystem,\n  Option,\n  Schedule,\n  Scope,\n} from 'effect'\n\n/** Failed to acquire the lock in time, or the attempt itself errored. */\nexport class FileLockError extends Data.TaggedError('FileLockError')<{\n  lockPath: string\n  cause: unknown\n}> {\n  override get message(): string {\n    return `Could not acquire file lock: ${this.lockPath}`\n  }\n}\n\n/** An inter-process lock backed by a lock directory on disk. */\nexport interface FileLock {\n  /** Runs `self` while holding the lock. */\n  withLock<A, E, R>(\n    self: Effect.Effect<A, E, R>,\n  ): Effect.Effect<A, E | FileLockError, R>\n}\n\n/**\n * Creates an inter-process `FileLock` at `lockPath`.\n *\n * Waiters fail with a `FileLockError` after 10 seconds. Abandoned locks are\n * broken: holders refresh the lock's mtime every 10 seconds, and a lock not\n * refreshed for 30 seconds is considered dead.\n */\nexport const make = Effect.fnUntraced(function* (lockPath: string) {\n  const fs = yield* FileSystem.FileSystem\n\n  /** Removes the lock if its holder stopped heartbeating (e.g. was killed). */\n  const breakIfStale = Effect.fnUntraced(function* () {\n    const { mtime } = yield* fs.stat(lockPath)\n    const now = yield* DateTime.now\n    const stale = Option.exists(mtime, (time) =>\n      Duration.isGreaterThan(\n        DateTime.distance(DateTime.fromDateUnsafe(time), now),\n        Duration.seconds(30),\n      ),\n    )\n    if (stale) {\n      yield* fs.remove(lockPath, { recursive: true })\n    }\n  }, Effect.ignore)\n\n  // Uninterruptible `makeDirectory` acquire; the polling between attempts\n  // stays interruptible.\n  const acquire = Effect.acquireRelease(fs.makeDirectory(lockPath), () =>\n    Effect.ignore(fs.remove(lockPath, { recursive: true })),\n  ).pipe(\n    Effect.tapError((error) =>\n      error.reason._tag === 'AlreadyExists' ? breakIfStale() : Effect.void,\n    ),\n    Effect.retry({\n      while: (error) => error.reason._tag === 'AlreadyExists',\n      schedule: Schedule.spaced('100 millis').pipe(\n        Schedule.upTo({ duration: '10 seconds' }),\n      ),\n    }),\n    Effect.mapError((cause) => new FileLockError({ lockPath, cause })),\n  )\n\n  /** Marks the lock as live so waiters do not break it. */\n  const heartbeat = Effect.gen(function* () {\n    const now = yield* DateTime.nowAsDate\n    yield* fs.utimes(lockPath, now, now)\n  }).pipe(Effect.ignore, Effect.repeat(Schedule.spaced('10 seconds')))\n\n  const lock: FileLock = {\n    withLock: (self) =>\n      Effect.scopedWith((scope) =>\n        Effect.gen(function* () {\n          yield* Scope.provide(scope)(acquire)\n          yield* Scope.provide(scope)(Effect.forkScoped(heartbeat))\n          return yield* self\n        }),\n      ),\n  }\n\n  return lock\n})\n","import { getAgentDir } from '@earendil-works/pi-coding-agent'\nimport {\n  Array,\n  Context,\n  Crypto,\n  Effect,\n  Encoding,\n  Fiber,\n  FileSystem,\n  Layer,\n  Path,\n  pipe,\n  Schema,\n  Stream,\n  String,\n} from 'effect'\nimport { ChildProcess, ChildProcessSpawner } from 'effect/unstable/process'\nimport * as FileLock from './file-lock'\n\nexport class SnapshotterError extends Schema.TaggedErrorClass<SnapshotterError>()(\n  'SnapshotterError',\n  {\n    kind: Schema.Literals(['GitError', 'GitTimeout', 'NotAWorktree']),\n    message: Schema.String,\n    cause: Schema.optional(Schema.Defect()),\n  },\n) {}\n\n/**\n * Snapshots the git worktree containing `cwd` into a shadow repository (a\n * separate `GIT_DIR` outside the project).\n *\n * `make` fails with a `NotAWorktree` error outside git worktrees.\n */\nexport class Snapshotter extends Context.Service<Snapshotter>()(\n  '@pi-plugins/checkpoint/Snapshotter',\n  {\n    make: Effect.fnUntraced(function* (cwd: string) {\n      const fs = yield* FileSystem.FileSystem\n      const path = yield* Path.Path\n      const crypto = yield* Crypto.Crypto\n      const spawner = yield* ChildProcessSpawner.ChildProcessSpawner\n\n      /**\n       * Runs `git args` in `dir` and returns its stdout, failing with a\n       * `GitError` on non-zero exit. Invocations are killed after a timeout\n       * so a hung git process cannot stall the agent's hooks indefinitely.\n       */\n      const git = Effect.fnUntraced(\n        function* (args: readonly string[], dir: string) {\n          const handle = yield* spawner.spawn(\n            ChildProcess.make('git', args, {\n              cwd: dir,\n              forceKillAfter: '5 seconds',\n            }),\n          )\n          const stderrFiber = yield* Effect.forkScoped(\n            Stream.mkString(Stream.decodeText(handle.stderr)),\n          )\n          const stdout = yield* Stream.mkString(Stream.decodeText(handle.stdout))\n          const exitCode = Number(yield* handle.exitCode)\n          const stderr = yield* Fiber.join(stderrFiber)\n\n          if (exitCode !== 0) {\n            return yield* new SnapshotterError({\n              kind: 'GitError',\n              message:\n                `git ${args.join(' ')} exited with ${exitCode}` +\n                (stderr.trim() ? `: ${stderr.trim()}` : ''),\n            })\n          }\n\n          return stdout\n        },\n        (effect, args) =>\n          effect.pipe(\n            Effect.scoped,\n            Effect.timeoutOrElse({\n              duration: '1 minute',\n              orElse: () =>\n                new SnapshotterError({\n                  kind: 'GitTimeout',\n                  message: `git ${args.join(' ')} timed out after 1 minute`,\n                }),\n            }),\n          ),\n      )\n\n      /**\n       * Wraps `args` with the arguments that point git at the shadow\n       * repository and additional safety configuration.\n       */\n      const shadowGit = (args: readonly string[]): readonly string[] => [\n        '-c',\n        'core.autocrlf=false',\n        '-c',\n        'core.quotepath=false',\n        '-c',\n        'core.fsmonitor=false',\n        '--git-dir',\n        gitdir,\n        '--work-tree',\n        worktree,\n        ...args,\n      ]\n\n      // Resolve the canonical repository root by which the shadow `GIT_DIR` is keyed.\n      const worktree = yield* git(['rev-parse', '--show-toplevel'], cwd).pipe(\n        Effect.map(String.trim),\n        Effect.catchTag('SnapshotterError', (error) =>\n          error.kind === 'GitError'\n            ? new SnapshotterError({\n                kind: 'NotAWorktree',\n                message: `Not a git worktree: ${cwd}`,\n              })\n            : Effect.fail(error),\n        ),\n      )\n      if (String.isEmpty(worktree)) {\n        return yield* new SnapshotterError({\n          kind: 'NotAWorktree',\n          message: `Not a git worktree: ${cwd}`,\n        })\n      }\n\n      const digest = yield* crypto.digest(\n        'SHA-256',\n        new TextEncoder().encode(worktree),\n      )\n      const gitdir = path.join(\n        getAgentDir(),\n        'checkpoints',\n        Encoding.encodeHex(digest).slice(0, 16),\n      )\n\n      yield* fs.makeDirectory(gitdir, { recursive: true })\n      const lock = yield* FileLock.make(path.join(gitdir, 'checkpoint.lock'))\n\n      yield* lock.withLock(\n        Effect.gen(function* () {\n          if (!(yield* fs.exists(path.join(gitdir, 'HEAD')))) {\n            yield* git(shadowGit(['init', '--quiet']), worktree)\n          }\n        }),\n      )\n\n      /**\n       * Lists the paths currently tracked by the shadow index.\n       */\n      const listIndexFiles = Effect.fnUntraced(function* () {\n        const out = yield* git(shadowGit(['ls-files', '-z']), worktree)\n        return pipe(out, String.split('\\0'), Array.filter(String.isNonEmpty))\n      })\n\n      /**\n       * Creates a snapshot of the current worktree state.\n       */\n      const track = Effect.fn('Snapshotter.track')(function* () {\n        yield* git(shadowGit(['add', '--all']), worktree)\n        return yield* git(shadowGit(['write-tree']), worktree).pipe(\n          Effect.map(String.trim),\n        )\n      }, lock.withLock)\n\n      /**\n       * Applies a snapshot to the worktree: checks out its files and deletes\n       * files tracked in the shadow index but absent from the snapshot.\n       * Assumes the shadow index reflects the current worktree state.\n       */\n      const applyTree = Effect.fnUntraced(function* (tree: string) {\n        const before = yield* listIndexFiles()\n        yield* git(shadowGit(['read-tree', tree]), worktree)\n        yield* git(shadowGit(['checkout-index', '--all', '--force']), worktree)\n        const after = new Set(yield* listIndexFiles())\n\n        yield* Effect.forEach(\n          before.filter((file) => !after.has(file)),\n          (file) => Effect.ignore(fs.remove(path.join(worktree, file))),\n          { discard: true, concurrency: 'unbounded' },\n        )\n      })\n\n      /**\n       * Restores the worktree to the state of a snapshot, deleting files that\n       * were present in the worktree but not in the snapshot.\n       *\n       * A failed restore is rolled back to the pre-restore state so that a\n       * partial checkout never leaves the worktree in a mixed state.\n       */\n      const restore = Effect.fn('Snapshotter.restore')(function* (tree: string) {\n        // Snapshot the current state as the rollback point.\n        yield* git(shadowGit(['add', '--all']), worktree)\n        const current = yield* git(shadowGit(['write-tree']), worktree).pipe(\n          Effect.map(String.trim),\n        )\n\n        yield* applyTree(tree).pipe(\n          Effect.tapError((error) =>\n            applyTree(current).pipe(\n              Effect.mapError(\n                (rollbackError) =>\n                  new SnapshotterError({\n                    kind: 'GitError',\n                    message: `${error.message} (rollback also failed: ${rollbackError.message})`,\n                    cause: error,\n                  }),\n              ),\n            ),\n          ),\n        )\n      }, lock.withLock)\n\n      /**\n       * Prunes all stored checkpoint objects and records the current worktree\n       * as a fresh baseline so checkpointing can continue immediately.\n       */\n      const cleanup = Effect.fn('Snapshotter.cleanup')(function* () {\n        const index = path.join(gitdir, 'index')\n        if (yield* fs.exists(index)) {\n          yield* fs.remove(index)\n        }\n        yield* git(\n          shadowGit(['reflog', 'expire', '--expire=now', '--all']),\n          worktree,\n        )\n        yield* git(shadowGit(['gc', '--prune=now', '--quiet']), worktree)\n        yield* git(shadowGit(['add', '--all']), worktree)\n        return yield* git(shadowGit(['write-tree']), worktree).pipe(\n          Effect.map(String.trim),\n        )\n      }, lock.withLock)\n\n      return { track, restore, cleanup } as const\n    }),\n  },\n) {\n  static readonly layer = (cwd: string) => Layer.effect(this, this.make(cwd))\n}\n","import type {\n  ExtensionAPI,\n  ExtensionContext,\n  SessionEntry,\n} from '@earendil-works/pi-coding-agent'\nimport * as NodeServices from '@effect/platform-node/NodeServices'\nimport { runHandler } from '@pi-plugins/shared/run'\nimport { Array, Effect, Option, pipe, Schema } from 'effect'\nimport { Snapshotter, SnapshotterError } from './snapshot'\n\n/** `customType` of the hidden session entries that carry a snapshot tree hash. */\nconst CHECKPOINT_TYPE = 'file-checkpoint'\n\n/** User-facing choices when navigating to a point with a different file state. */\nconst CHOICE_CONVERSATION = 'Conversation only (keep files as they are)'\nconst CHOICE_RESTORE = 'Conversation and files'\nconst CHOICE_CANCEL = 'Cancel navigation'\n\n/** The tree hash stored on `entry`, if it is one of our checkpoint entries. */\nfunction checkpointOf(entry: SessionEntry | undefined): string | undefined {\n  if (entry?.type !== 'custom' || entry.customType !== CHECKPOINT_TYPE) {\n    return undefined\n  }\n\n  return pipe(\n    Schema.decodeUnknownOption(Schema.Struct({ tree: Schema.String }))(entry.data),\n    Option.map((data) => data.tree),\n    Option.getOrUndefined,\n  )\n}\n\n/** Nearest checkpoint at or above `fromId` in the session tree. */\nfunction nearestCheckpoint(\n  session: ExtensionContext['sessionManager'],\n  fromId: string | null,\n): string | undefined {\n  for (let id = fromId; id !== null;) {\n    const entry = session.getEntry(id)\n    if (!entry) {\n      return undefined\n    }\n\n    const tree = checkpointOf(entry)\n    if (tree) {\n      return tree\n    }\n\n    id = entry.parentId\n  }\n\n  return undefined\n}\n\n/**\n * The file state associated with navigating to `targetId`: a checkpoint\n * directly below the target, or the nearest ancestor checkpoint.\n */\nfunction restoreTree(\n  session: ExtensionContext['sessionManager'],\n  targetId: string,\n): string | undefined {\n  return pipe(\n    session.getEntries(),\n    Array.findFirst((entry) =>\n      entry.parentId === targetId\n        ? Option.fromUndefinedOr(checkpointOf(entry))\n        : Option.none(),\n    ),\n    Option.getOrElse(() => nearestCheckpoint(session, targetId)),\n  )\n}\n\nexport default function checkpoint(pi: ExtensionAPI) {\n  let snapshotter: Snapshotter['Service'] | undefined\n\n  pi.on('session_start', async (_event, ctx) => {\n    // Only active inside git worktrees.\n    snapshotter = await runHandler(\n      Snapshotter.make(ctx.cwd).pipe(\n        Effect.provide(NodeServices.layer),\n        // Being outside a worktree is the normal way to have no snapshotter.\n        Effect.catchIf(\n          (error) =>\n            error instanceof SnapshotterError && error.kind === 'NotAWorktree',\n          () => Effect.succeed(undefined),\n        ),\n      ),\n      {\n        onError: (message) => {\n          if (ctx.hasUI) {\n            ctx.ui.notify(`Checkpoints disabled: ${message}`, 'warning')\n          }\n          return undefined\n        },\n      },\n    )\n  })\n\n  /**\n   * Records the current file state as a checkpoint at the current leaf,\n   * skipping entries when the branch already ends in an identical state.\n   * Running on both `turn_start` and `turn_end` brackets every turn with a\n   * before/after snapshot pair.\n   */\n  const recordCheckpoint = async (ctx: ExtensionContext) => {\n    if (!snapshotter) {\n      return\n    }\n    // Current worktree state as a tree hash, or undefined when tracking fails.\n    const tree = await runHandler(snapshotter.track())\n    if (!tree) {\n      return\n    }\n    const session = ctx.sessionManager\n    if (nearestCheckpoint(session, session.getLeafId()) !== tree) {\n      pi.appendEntry(CHECKPOINT_TYPE, { tree })\n    }\n  }\n\n  pi.on('turn_start', async (_event, ctx) => recordCheckpoint(ctx))\n  pi.on('turn_end', async (_event, ctx) => recordCheckpoint(ctx))\n\n  pi.registerCommand('checkpoint-cleanup', {\n    description: 'Delete stored file checkpoint history for this worktree',\n    handler: async (_args, ctx) => {\n      await ctx.waitForIdle()\n      if (!snapshotter || !ctx.hasUI) {\n        return undefined\n      }\n\n      if (\n        !(await ctx.ui.confirm(\n          'Delete file checkpoints?',\n          'This removes the stored file history for every session in this worktree. Conversation history is not affected.',\n        ))\n      ) {\n        return undefined\n      }\n\n      await runHandler(\n        snapshotter.cleanup().pipe(\n          Effect.tap((tree) =>\n            Effect.sync(() => {\n              const session = ctx.sessionManager\n              if (nearestCheckpoint(session, session.getLeafId()) !== tree) {\n                pi.appendEntry(CHECKPOINT_TYPE, { tree })\n              }\n              ctx.ui.notify('File checkpoint history cleaned up', 'info')\n            }),\n          ),\n        ),\n        {\n          onError: (message) => {\n            ctx.ui.notify(`Checkpoint cleanup failed: ${message}`, 'error')\n          },\n        },\n      )\n    },\n  })\n\n  pi.on('session_before_tree', async (event, ctx) => {\n    if (!snapshotter || !ctx.hasUI) {\n      return undefined\n    }\n    const session = ctx.sessionManager\n    const target = restoreTree(session, event.preparation.targetId)\n    if (!target) {\n      return undefined\n    }\n\n    const current = await runHandler(snapshotter.track())\n    if (!current || current === target) {\n      return undefined\n    }\n\n    const choice = await ctx.ui.select(\n      'Files changed since that point in the conversation. What should be restored?',\n      [CHOICE_CONVERSATION, CHOICE_RESTORE, CHOICE_CANCEL],\n    )\n    if (choice !== CHOICE_RESTORE) {\n      return choice === CHOICE_CANCEL ? { cancel: true } : undefined\n    }\n\n    // Preserve the abandoned state on the old branch so that navigating back\n    // to it can restore the files again (redo).\n    if (nearestCheckpoint(session, session.getLeafId()) !== current) {\n      pi.appendEntry(CHECKPOINT_TYPE, { tree: current })\n    }\n\n    return runHandler(\n      snapshotter.restore(target).pipe(\n        Effect.map(() => {\n          ctx.ui.notify('Files restored to the selected point', 'info')\n          return undefined\n        }),\n      ),\n      {\n        onError: (message) => {\n          ctx.ui.notify(`File restore failed: ${message}`, 'error')\n          return { cancel: true }\n        },\n      },\n    )\n  })\n}\n"],"mappings":"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"}