@amistio/cli 0.1.55 → 0.1.56

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +952 -683
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
5
- import { writeFile as writeFile12 } from "node:fs/promises";
6
- import os9 from "node:os";
7
- import path19 from "node:path";
4
+ import { createHash as createHash9, randomUUID as randomUUID4 } from "node:crypto";
5
+ import { writeFile as writeFile13 } from "node:fs/promises";
6
+ import os10 from "node:os";
7
+ import path20 from "node:path";
8
8
  import { Command } from "commander";
9
9
 
10
10
  // ../shared/src/schemas.ts
@@ -3302,8 +3302,8 @@ var toolSessionMutationSchema = z3.object({
3302
3302
  });
3303
3303
  function resolveApiUrl(apiUrl, urlPath) {
3304
3304
  const base = apiUrl.endsWith("/") ? apiUrl.slice(0, -1) : apiUrl;
3305
- const path20 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
3306
- return new URL(`${base}${path20}`);
3305
+ const path21 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
3306
+ return new URL(`${base}${path21}`);
3307
3307
  }
3308
3308
 
3309
3309
  // src/orchestrator.ts
@@ -6258,6 +6258,191 @@ async function checkRunnerWatchGitPreflight(options) {
6258
6258
  return { status: "blocked", exitCode: 1, message, readiness: readiness2 };
6259
6259
  }
6260
6260
 
6261
+ // src/runner-active-claims.ts
6262
+ import { randomUUID as randomUUID3 } from "node:crypto";
6263
+ import { chmod as chmod3, mkdir as mkdir9, readFile as readFile9, rm as rm3, stat as stat4, writeFile as writeFile9 } from "node:fs/promises";
6264
+ import os6 from "node:os";
6265
+ import path11 from "node:path";
6266
+ var activeClaimLockStaleMs = 3e4;
6267
+ var activeClaimLockRetryMs = 20;
6268
+ var activeClaimLockAttempts = 50;
6269
+ var maxStoredActiveClaims = 100;
6270
+ var LocalRunnerActiveClaimStore = class {
6271
+ constructor(filePath = defaultActiveClaimStorePath()) {
6272
+ this.filePath = filePath;
6273
+ }
6274
+ filePath;
6275
+ operation = Promise.resolve();
6276
+ async reserve(input) {
6277
+ return this.withLockedStore(async () => {
6278
+ const now = /* @__PURE__ */ new Date();
6279
+ const data = normalizeActiveClaimFile(await this.read());
6280
+ data.activeClaims = pruneExpiredClaims(data.activeClaims, now.getTime());
6281
+ const conflict = data.activeClaims.find((record2) => activeClaimConflicts(record2, input));
6282
+ if (conflict) {
6283
+ return { status: "blocked", reason: activeClaimConflictReason(conflict, input), existing: conflict };
6284
+ }
6285
+ const record = {
6286
+ ...input,
6287
+ reservationId: `runner_claim_${randomUUID3()}`,
6288
+ processId: process.pid,
6289
+ reservedAt: now.toISOString(),
6290
+ updatedAt: now.toISOString()
6291
+ };
6292
+ data.activeClaims = [...data.activeClaims, record].slice(-maxStoredActiveClaims);
6293
+ await this.write(data);
6294
+ return { status: "reserved", record };
6295
+ });
6296
+ }
6297
+ async release(reservationId) {
6298
+ await this.withLockedStore(async () => {
6299
+ const data = normalizeActiveClaimFile(await this.read());
6300
+ const activeClaims = data.activeClaims.filter((record) => record.reservationId !== reservationId);
6301
+ if (activeClaims.length === data.activeClaims.length) {
6302
+ return;
6303
+ }
6304
+ await this.write({ activeClaims });
6305
+ });
6306
+ }
6307
+ async listActive(now = Date.now()) {
6308
+ return this.withLockedStore(async () => {
6309
+ const data = normalizeActiveClaimFile(await this.read());
6310
+ const activeClaims = pruneExpiredClaims(data.activeClaims, now);
6311
+ if (activeClaims.length !== data.activeClaims.length) {
6312
+ await this.write({ activeClaims });
6313
+ }
6314
+ return activeClaims;
6315
+ });
6316
+ }
6317
+ async withLockedStore(operation) {
6318
+ return this.withProcessLock(async () => {
6319
+ const release = await this.acquireFileLock();
6320
+ try {
6321
+ return await operation();
6322
+ } finally {
6323
+ await release();
6324
+ }
6325
+ });
6326
+ }
6327
+ async withProcessLock(operation) {
6328
+ const previous = this.operation;
6329
+ let releaseProcessLock = () => void 0;
6330
+ this.operation = new Promise((resolve) => {
6331
+ releaseProcessLock = resolve;
6332
+ });
6333
+ await previous.catch(() => void 0);
6334
+ try {
6335
+ return await operation();
6336
+ } finally {
6337
+ releaseProcessLock();
6338
+ }
6339
+ }
6340
+ async acquireFileLock() {
6341
+ const lockPath = `${this.filePath}.lock`;
6342
+ await mkdir9(path11.dirname(this.filePath), { recursive: true });
6343
+ for (let attempt = 0; attempt < activeClaimLockAttempts; attempt += 1) {
6344
+ try {
6345
+ await mkdir9(lockPath);
6346
+ return async () => {
6347
+ await rm3(lockPath, { recursive: true, force: true });
6348
+ };
6349
+ } catch (error) {
6350
+ if (!isNodeErrorCode(error, "EEXIST")) {
6351
+ throw error;
6352
+ }
6353
+ await removeStaleLock(lockPath, Date.now());
6354
+ await delay(activeClaimLockRetryMs);
6355
+ }
6356
+ }
6357
+ throw new Error("Could not acquire local runner active-claim lock.");
6358
+ }
6359
+ async read() {
6360
+ try {
6361
+ return JSON.parse(await readFile9(this.filePath, "utf8"));
6362
+ } catch {
6363
+ return { activeClaims: [] };
6364
+ }
6365
+ }
6366
+ async write(data) {
6367
+ await mkdir9(path11.dirname(this.filePath), { recursive: true });
6368
+ await writeFile9(this.filePath, JSON.stringify(data, null, 2), { encoding: "utf8", mode: 384 });
6369
+ await chmod3(this.filePath, 384);
6370
+ }
6371
+ };
6372
+ var defaultRunnerActiveClaimStore = new LocalRunnerActiveClaimStore();
6373
+ function activeClaimConflictMessage(result) {
6374
+ if (result.reason === "sameLane") {
6375
+ return `Runner lane ${result.existing.claimLaneId} is already working on ${result.existing.workItemId}.`;
6376
+ }
6377
+ if (result.reason === "sameWorktree") {
6378
+ return `Runner worktree ${result.existing.worktreeKey} is already reserved by lane ${result.existing.claimLaneId}.`;
6379
+ }
6380
+ return `Runner work item ${result.existing.workItemId} is already reserved by lane ${result.existing.claimLaneId}.`;
6381
+ }
6382
+ function defaultActiveClaimStorePath() {
6383
+ return path11.join(os6.homedir(), ".config", "amistio", "active-runner-claims.json");
6384
+ }
6385
+ function normalizeActiveClaimFile(value) {
6386
+ if (!value || typeof value !== "object" || !("activeClaims" in value)) {
6387
+ return { activeClaims: [] };
6388
+ }
6389
+ const activeClaims = value.activeClaims;
6390
+ if (!Array.isArray(activeClaims)) {
6391
+ return { activeClaims: [] };
6392
+ }
6393
+ return {
6394
+ activeClaims: activeClaims.filter(isRunnerActiveClaimRecord)
6395
+ };
6396
+ }
6397
+ function isRunnerActiveClaimRecord(value) {
6398
+ if (!value || typeof value !== "object") return false;
6399
+ const record = value;
6400
+ return typeof record.reservationId === "string" && typeof record.accountId === "string" && typeof record.projectId === "string" && typeof record.repositoryLinkId === "string" && typeof record.runnerId === "string" && typeof record.claimLaneId === "string" && typeof record.workItemId === "string" && typeof record.attempt === "number" && typeof record.expiresAt === "string" && typeof record.reservedAt === "string" && typeof record.updatedAt === "string" && typeof record.processId === "number";
6401
+ }
6402
+ function pruneExpiredClaims(records, now) {
6403
+ return records.filter((record) => {
6404
+ const expiresAt = Date.parse(record.expiresAt);
6405
+ return Number.isFinite(expiresAt) && expiresAt > now;
6406
+ });
6407
+ }
6408
+ function activeClaimConflicts(record, input) {
6409
+ if (record.accountId !== input.accountId || record.projectId !== input.projectId || record.repositoryLinkId !== input.repositoryLinkId) {
6410
+ return false;
6411
+ }
6412
+ if (record.runnerId === input.runnerId && record.claimLaneId === input.claimLaneId) {
6413
+ return true;
6414
+ }
6415
+ if (record.workItemId === input.workItemId) {
6416
+ return true;
6417
+ }
6418
+ return Boolean(record.worktreeKey && input.worktreeKey && record.worktreeKey === input.worktreeKey);
6419
+ }
6420
+ function activeClaimConflictReason(record, input) {
6421
+ if (record.runnerId === input.runnerId && record.claimLaneId === input.claimLaneId) {
6422
+ return "sameLane";
6423
+ }
6424
+ if (record.workItemId === input.workItemId) {
6425
+ return "sameWorkItem";
6426
+ }
6427
+ return "sameWorktree";
6428
+ }
6429
+ async function removeStaleLock(lockPath, now) {
6430
+ try {
6431
+ const lockStat = await stat4(lockPath);
6432
+ if (now - lockStat.mtimeMs > activeClaimLockStaleMs) {
6433
+ await rm3(lockPath, { recursive: true, force: true });
6434
+ }
6435
+ } catch {
6436
+ return;
6437
+ }
6438
+ }
6439
+ function isNodeErrorCode(error, code) {
6440
+ return typeof error === "object" && error !== null && error.code === code;
6441
+ }
6442
+ function delay(ms) {
6443
+ return new Promise((resolve) => setTimeout(resolve, ms));
6444
+ }
6445
+
6261
6446
  // src/runner-patch-drift.ts
6262
6447
  var patchDriftPatterns = [
6263
6448
  { signature: "applyPatchVerificationFailed", pattern: /apply_patch\s+verification\s+failed/i },
@@ -6320,9 +6505,9 @@ ${options.detail}`);
6320
6505
  // src/runner-service.ts
6321
6506
  import { spawn as spawn3 } from "node:child_process";
6322
6507
  import { createHash as createHash5 } from "node:crypto";
6323
- import { mkdir as mkdir9, readFile as readFile9, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
6324
- import os6 from "node:os";
6325
- import path11 from "node:path";
6508
+ import { mkdir as mkdir10, readFile as readFile10, rm as rm4, writeFile as writeFile10 } from "node:fs/promises";
6509
+ import os7 from "node:os";
6510
+ import path12 from "node:path";
6326
6511
  function detectRunnerServicePlatform(platform = process.platform) {
6327
6512
  if (platform === "darwin") return "launchd";
6328
6513
  if (platform === "linux") return "systemd";
@@ -6333,19 +6518,19 @@ function createRunnerServiceDescriptor(input) {
6333
6518
  if (platform === "unsupported") {
6334
6519
  throw new Error("Startup services are supported for user-level launchd on macOS and systemd user services on Linux.");
6335
6520
  }
6336
- const homeDir = input.homeDir ?? os6.homedir();
6521
+ const homeDir = input.homeDir ?? os7.homedir();
6337
6522
  const serviceName = runnerServiceName(input);
6338
6523
  const serviceFilePath = runnerServiceFilePath(platform, serviceName, homeDir);
6339
6524
  const now = (/* @__PURE__ */ new Date()).toISOString();
6340
6525
  const command = [input.executablePath ?? process.execPath, input.scriptPath ?? process.argv[1], ...input.args];
6341
- const logPath = path11.join(input.metadataDir ?? defaultRunnerMetadataDir(), `${runnerServiceKey(input)}.service.log`);
6526
+ const logPath = path12.join(input.metadataDir ?? defaultRunnerMetadataDir(), `${runnerServiceKey(input)}.service.log`);
6342
6527
  const metadata = {
6343
6528
  schemaVersion: 1,
6344
6529
  accountId: input.accountId,
6345
6530
  projectId: input.projectId,
6346
6531
  repositoryLinkId: input.repositoryLinkId,
6347
6532
  runnerId: input.runnerId,
6348
- rootDir: path11.resolve(input.rootDir),
6533
+ rootDir: path12.resolve(input.rootDir),
6349
6534
  apiUrl: input.apiUrl,
6350
6535
  serviceName,
6351
6536
  serviceFilePath,
@@ -6362,9 +6547,9 @@ function createRunnerServiceDescriptor(input) {
6362
6547
  }
6363
6548
  async function installRunnerService(input, options = {}) {
6364
6549
  const descriptor = createRunnerServiceDescriptor(input);
6365
- await mkdir9(path11.dirname(descriptor.metadata.serviceFilePath), { recursive: true });
6366
- await mkdir9(input.metadataDir ?? defaultRunnerMetadataDir(), { recursive: true });
6367
- await writeFile9(descriptor.metadata.serviceFilePath, descriptor.content, { encoding: "utf8", mode: 384 });
6550
+ await mkdir10(path12.dirname(descriptor.metadata.serviceFilePath), { recursive: true });
6551
+ await mkdir10(input.metadataDir ?? defaultRunnerMetadataDir(), { recursive: true });
6552
+ await writeFile10(descriptor.metadata.serviceFilePath, descriptor.content, { encoding: "utf8", mode: 384 });
6368
6553
  await writeRunnerServiceMetadata(descriptor.metadata, input.metadataDir);
6369
6554
  if (options.activate !== false) {
6370
6555
  const activation = await activateRunnerService(descriptor.metadata);
@@ -6380,13 +6565,13 @@ async function removeRunnerService(input) {
6380
6565
  return void 0;
6381
6566
  }
6382
6567
  await deactivateRunnerService(metadata).catch(() => void 0);
6383
- await rm3(metadata.serviceFilePath, { force: true });
6384
- await rm3(runnerServiceMetadataPath(input, input.metadataDir ?? defaultRunnerMetadataDir()), { force: true });
6568
+ await rm4(metadata.serviceFilePath, { force: true });
6569
+ await rm4(runnerServiceMetadataPath(input, input.metadataDir ?? defaultRunnerMetadataDir()), { force: true });
6385
6570
  return { ...metadata, status: "removed", updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
6386
6571
  }
6387
6572
  async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetadataDir()) {
6388
6573
  try {
6389
- const parsed = JSON.parse(await readFile9(runnerServiceMetadataPath(input, metadataDir), "utf8"));
6574
+ const parsed = JSON.parse(await readFile10(runnerServiceMetadataPath(input, metadataDir), "utf8"));
6390
6575
  if (parsed.schemaVersion !== 1 || !parsed.serviceName || !parsed.serviceFilePath) {
6391
6576
  return void 0;
6392
6577
  }
@@ -6396,8 +6581,8 @@ async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetad
6396
6581
  }
6397
6582
  }
6398
6583
  async function writeRunnerServiceMetadata(metadata, metadataDir = defaultRunnerMetadataDir()) {
6399
- await mkdir9(metadataDir, { recursive: true });
6400
- await writeFile9(runnerServiceMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
6584
+ await mkdir10(metadataDir, { recursive: true });
6585
+ await writeFile10(runnerServiceMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
6401
6586
  }
6402
6587
  async function runnerServiceRuntimeStatus(metadata) {
6403
6588
  if (metadata.platform === "launchd") {
@@ -6480,12 +6665,12 @@ WantedBy=default.target
6480
6665
  }
6481
6666
  function runnerServiceFilePath(platform, serviceName, homeDir) {
6482
6667
  if (platform === "launchd") {
6483
- return path11.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
6668
+ return path12.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
6484
6669
  }
6485
- return path11.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
6670
+ return path12.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
6486
6671
  }
6487
6672
  function runnerServiceMetadataPath(input, metadataDir) {
6488
- return path11.join(metadataDir, `${runnerServiceKey(input)}.service.json`);
6673
+ return path12.join(metadataDir, `${runnerServiceKey(input)}.service.json`);
6489
6674
  }
6490
6675
  function runnerServiceName(input) {
6491
6676
  return `com.amistio.runner.${runnerServiceKey(input).slice(0, 20)}`;
@@ -6820,8 +7005,8 @@ function createSmokeSession({ now, status, lastActivityAt }) {
6820
7005
  // src/sync.ts
6821
7006
  import { execFile as execFile3 } from "node:child_process";
6822
7007
  import { createHash as createHash6 } from "node:crypto";
6823
- import { mkdir as mkdir10, readdir as readdir5, readFile as readFile10, stat as stat4, writeFile as writeFile10 } from "node:fs/promises";
6824
- import path12 from "node:path";
7008
+ import { mkdir as mkdir11, readdir as readdir5, readFile as readFile11, stat as stat5, writeFile as writeFile11 } from "node:fs/promises";
7009
+ import path13 from "node:path";
6825
7010
  import { promisify as promisify3 } from "node:util";
6826
7011
  var execFileAsync3 = promisify3(execFile3);
6827
7012
  var legacySyncRoots = ["architecture", "context", "decisions", "features", "memory", "plans", "prompts", "workflows"];
@@ -6917,7 +7102,7 @@ async function readLocalSyncedDocuments(rootDir) {
6917
7102
  const documentFiles = await findBrainDocumentFiles(rootDir);
6918
7103
  const documents = [];
6919
7104
  for (const fullPath of documentFiles) {
6920
- const raw = await readFile10(fullPath, "utf8");
7105
+ const raw = await readFile11(fullPath, "utf8");
6921
7106
  const repoPath = toRepoPath(rootDir, fullPath);
6922
7107
  const parsed = parseSyncedDocument(raw, repoPath);
6923
7108
  if (!parsed) {
@@ -6967,8 +7152,8 @@ async function materializeBrainDocuments(rootDir, documents, options = {}) {
6967
7152
  result.skipped.push(document.repoPath);
6968
7153
  continue;
6969
7154
  }
6970
- await mkdir10(path12.dirname(fullPath), { recursive: true });
6971
- await writeFile10(fullPath, createSyncedDocumentContent(document), "utf8");
7155
+ await mkdir11(path13.dirname(fullPath), { recursive: true });
7156
+ await writeFile11(fullPath, createSyncedDocumentContent(document), "utf8");
6972
7157
  result.written.push(document.repoPath);
6973
7158
  }
6974
7159
  return result;
@@ -7097,7 +7282,7 @@ function parseSyncedHtml(content) {
7097
7282
  }
7098
7283
  async function readExistingSyncedDocument(fullPath) {
7099
7284
  try {
7100
- const raw = await readFile10(fullPath, "utf8");
7285
+ const raw = await readFile11(fullPath, "utf8");
7101
7286
  const parsed = parseSyncedDocument(raw, fullPath);
7102
7287
  if (!parsed) {
7103
7288
  return { exists: true };
@@ -7122,7 +7307,7 @@ async function readExistingSyncedDocument(fullPath) {
7122
7307
  async function findBrainDocumentFiles(rootDir) {
7123
7308
  const files = [];
7124
7309
  for (const syncRoot of [...syncRoots, htmlSyncRoot]) {
7125
- const fullRoot = path12.join(rootDir, syncRoot);
7310
+ const fullRoot = path13.join(rootDir, syncRoot);
7126
7311
  if (!await exists2(fullRoot)) {
7127
7312
  continue;
7128
7313
  }
@@ -7132,7 +7317,7 @@ async function findBrainDocumentFiles(rootDir) {
7132
7317
  }
7133
7318
  async function walkBrainDocumentFiles(directory, files) {
7134
7319
  for (const entry of await readdir5(directory, { withFileTypes: true })) {
7135
- const fullPath = path12.join(directory, entry.name);
7320
+ const fullPath = path13.join(directory, entry.name);
7136
7321
  if (entry.isDirectory()) {
7137
7322
  await walkBrainDocumentFiles(fullPath, files);
7138
7323
  } else if (entry.isFile() && /\.(md|mdx|html?)$/i.test(entry.name)) {
@@ -7141,23 +7326,23 @@ async function walkBrainDocumentFiles(directory, files) {
7141
7326
  }
7142
7327
  }
7143
7328
  function safeRepoPath(rootDir, repoPath) {
7144
- if (path12.isAbsolute(repoPath)) {
7329
+ if (path13.isAbsolute(repoPath)) {
7145
7330
  throw new Error(`Refusing to use absolute repo path: ${repoPath}`);
7146
7331
  }
7147
- const normalized = path12.normalize(repoPath);
7148
- if (normalized === ".." || normalized.startsWith(`..${path12.sep}`)) {
7332
+ const normalized = path13.normalize(repoPath);
7333
+ if (normalized === ".." || normalized.startsWith(`..${path13.sep}`)) {
7149
7334
  throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
7150
7335
  }
7151
- const root = path12.resolve(rootDir);
7152
- const fullPath = path12.resolve(root, normalized);
7153
- if (!fullPath.startsWith(`${root}${path12.sep}`)) {
7336
+ const root = path13.resolve(rootDir);
7337
+ const fullPath = path13.resolve(root, normalized);
7338
+ if (!fullPath.startsWith(`${root}${path13.sep}`)) {
7154
7339
  throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
7155
7340
  }
7156
7341
  return fullPath;
7157
7342
  }
7158
7343
  function isControlPlanePath(repoPath) {
7159
- const normalized = path12.normalize(repoPath);
7160
- return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${path12.sep}`)) || normalized === htmlSyncRoot || normalized.startsWith(`${htmlSyncRoot}${path12.sep}`);
7344
+ const normalized = path13.normalize(repoPath);
7345
+ return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${path13.sep}`)) || normalized === htmlSyncRoot || normalized.startsWith(`${htmlSyncRoot}${path13.sep}`);
7161
7346
  }
7162
7347
  function canonicalControlPlaneRepoPath(repoPath) {
7163
7348
  const normalized = repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -7168,16 +7353,16 @@ function canonicalControlPlaneRepoPath(repoPath) {
7168
7353
  return normalized;
7169
7354
  }
7170
7355
  function toRepoPath(rootDir, fullPath) {
7171
- return path12.relative(rootDir, fullPath).split(path12.sep).join("/");
7356
+ return path13.relative(rootDir, fullPath).split(path13.sep).join("/");
7172
7357
  }
7173
7358
  function inferTitle(content, repoPath) {
7174
7359
  const heading = content.split("\n").find((line) => line.startsWith("# "))?.replace(/^#\s+/, "").trim();
7175
7360
  if (heading) return heading;
7176
7361
  const htmlHeading = content.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
7177
- return htmlHeading || path12.basename(repoPath, path12.extname(repoPath));
7362
+ return htmlHeading || path13.basename(repoPath, path13.extname(repoPath));
7178
7363
  }
7179
7364
  async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options) {
7180
- const root = path12.resolve(rootDir);
7365
+ const root = path13.resolve(rootDir);
7181
7366
  const maxBytes = (options.maxFileKb ?? defaultAutoSyncMaxFileKb) * 1024;
7182
7367
  const syncedAt = options.syncedAt ?? (/* @__PURE__ */ new Date()).toISOString();
7183
7368
  const existingById = new Map(existingDocuments.map((document) => [document.documentId, document]));
@@ -7193,7 +7378,7 @@ async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingD
7193
7378
  continue;
7194
7379
  }
7195
7380
  const fullPath = safeRepoPath(root, normalizedRepoPath);
7196
- const fileStat = await stat4(fullPath).catch(() => void 0);
7381
+ const fileStat = await stat5(fullPath).catch(() => void 0);
7197
7382
  if (!fileStat?.isFile()) {
7198
7383
  skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
7199
7384
  continue;
@@ -7202,7 +7387,7 @@ async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingD
7202
7387
  skipped.push({ repoPath: normalizedRepoPath, reason: "tooLarge" });
7203
7388
  continue;
7204
7389
  }
7205
- const content = await readFile10(fullPath, "utf8").catch(() => void 0);
7390
+ const content = await readFile11(fullPath, "utf8").catch(() => void 0);
7206
7391
  if (content === void 0) {
7207
7392
  skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
7208
7393
  continue;
@@ -7267,7 +7452,7 @@ async function listAutoSyncCandidatePaths(rootDir) {
7267
7452
  }
7268
7453
  const files = [];
7269
7454
  for (const syncRoot of [...syncRoots, htmlSyncRoot, ...legacySyncRoots]) {
7270
- const fullRoot = path12.join(rootDir, syncRoot);
7455
+ const fullRoot = path13.join(rootDir, syncRoot);
7271
7456
  if (await exists2(fullRoot)) {
7272
7457
  await walkAutoSyncFiles(rootDir, fullRoot, files);
7273
7458
  }
@@ -7276,8 +7461,8 @@ async function listAutoSyncCandidatePaths(rootDir) {
7276
7461
  }
7277
7462
  async function walkAutoSyncFiles(rootDir, directory, files) {
7278
7463
  for (const entry of await readdir5(directory, { withFileTypes: true }).catch(() => [])) {
7279
- const fullPath = path12.join(directory, entry.name);
7280
- const repoPath = normalizeRepoPath3(path12.relative(rootDir, fullPath));
7464
+ const fullPath = path13.join(directory, entry.name);
7465
+ const repoPath = normalizeRepoPath3(path13.relative(rootDir, fullPath));
7281
7466
  if (entry.isDirectory()) {
7282
7467
  if (!autoSyncExcludedDirectoryNames.has(entry.name)) {
7283
7468
  await walkAutoSyncFiles(rootDir, fullPath, files);
@@ -7331,7 +7516,7 @@ function parseFrontmatterFromSyncedDocument(frontmatter) {
7331
7516
  }
7332
7517
  async function exists2(filePath) {
7333
7518
  try {
7334
- await stat4(filePath);
7519
+ await stat5(filePath);
7335
7520
  return true;
7336
7521
  } catch {
7337
7522
  return false;
@@ -7339,9 +7524,9 @@ async function exists2(filePath) {
7339
7524
  }
7340
7525
 
7341
7526
  // src/tool-session-store.ts
7342
- import { mkdir as mkdir11, readFile as readFile11, writeFile as writeFile11 } from "node:fs/promises";
7343
- import os7 from "node:os";
7344
- import path13 from "node:path";
7527
+ import { mkdir as mkdir12, readFile as readFile12, writeFile as writeFile12 } from "node:fs/promises";
7528
+ import os8 from "node:os";
7529
+ import path14 from "node:path";
7345
7530
  var LocalToolSessionStore = class {
7346
7531
  constructor(filePath = defaultSessionStorePath()) {
7347
7532
  this.filePath = filePath;
@@ -7355,12 +7540,12 @@ var LocalToolSessionStore = class {
7355
7540
  async setProviderSessionId(toolSessionId, toolName, providerSessionId) {
7356
7541
  const data = await this.read();
7357
7542
  data[toolSessionId] = { toolName, providerSessionId, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
7358
- await mkdir11(path13.dirname(this.filePath), { recursive: true });
7359
- await writeFile11(this.filePath, JSON.stringify(data, null, 2), "utf8");
7543
+ await mkdir12(path14.dirname(this.filePath), { recursive: true });
7544
+ await writeFile12(this.filePath, JSON.stringify(data, null, 2), "utf8");
7360
7545
  }
7361
7546
  async read() {
7362
7547
  try {
7363
- return JSON.parse(await readFile11(this.filePath, "utf8"));
7548
+ return JSON.parse(await readFile12(this.filePath, "utf8"));
7364
7549
  } catch {
7365
7550
  return {};
7366
7551
  }
@@ -7368,16 +7553,16 @@ var LocalToolSessionStore = class {
7368
7553
  };
7369
7554
  function defaultSessionStorePath() {
7370
7555
  if (process.platform === "darwin") {
7371
- return path13.join(os7.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
7556
+ return path14.join(os8.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
7372
7557
  }
7373
7558
  if (process.platform === "win32") {
7374
- return path13.join(process.env.APPDATA ?? os7.homedir(), "Amistio", "tool-sessions.json");
7559
+ return path14.join(process.env.APPDATA ?? os8.homedir(), "Amistio", "tool-sessions.json");
7375
7560
  }
7376
- return path13.join(process.env.XDG_STATE_HOME ?? path13.join(os7.homedir(), ".local", "state"), "amistio", "tool-sessions.json");
7561
+ return path14.join(process.env.XDG_STATE_HOME ?? path14.join(os8.homedir(), ".local", "state"), "amistio", "tool-sessions.json");
7377
7562
  }
7378
7563
 
7379
7564
  // src/work-runner.ts
7380
- import path14 from "node:path";
7565
+ import path15 from "node:path";
7381
7566
  var generationResultStart = "AMISTIO_BRAIN_GENERATION_RESULT_START";
7382
7567
  var generationResultEnd = "AMISTIO_BRAIN_GENERATION_RESULT_END";
7383
7568
  var assistantAnswerStart = "AMISTIO_ASSISTANT_ANSWER_START";
@@ -8971,15 +9156,15 @@ function normalizeProjectContextRepoPath(value, options) {
8971
9156
  if (!trimmed || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) || /^file:/i.test(trimmed) || /^[A-Za-z]:($|[^\\/])/.test(trimmed)) {
8972
9157
  throwUnsafeProjectContextPath();
8973
9158
  }
8974
- const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path14.isAbsolute(trimmed) || path14.win32.isAbsolute(trimmed);
9159
+ const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path15.isAbsolute(trimmed) || path15.win32.isAbsolute(trimmed);
8975
9160
  if (!absolute) {
8976
9161
  return normalizeRelativeProjectContextPath(trimmed);
8977
9162
  }
8978
9163
  if (!options.repositoryRoot) {
8979
9164
  throwUnsafeProjectContextPath();
8980
9165
  }
8981
- const useWindowsPathRules = path14.win32.isAbsolute(trimmed) && !trimmed.startsWith("/");
8982
- const relativePath = useWindowsPathRules ? path14.win32.relative(path14.win32.resolve(options.repositoryRoot), path14.win32.resolve(trimmed)) : path14.relative(path14.resolve(options.repositoryRoot), path14.resolve(trimmed));
9166
+ const useWindowsPathRules = path15.win32.isAbsolute(trimmed) && !trimmed.startsWith("/");
9167
+ const relativePath = useWindowsPathRules ? path15.win32.relative(path15.win32.resolve(options.repositoryRoot), path15.win32.resolve(trimmed)) : path15.relative(path15.resolve(options.repositoryRoot), path15.resolve(trimmed));
8983
9168
  return normalizeRelativeProjectContextPath(relativePath);
8984
9169
  }
8985
9170
  function normalizeRelativeProjectContextPath(value) {
@@ -9063,15 +9248,15 @@ function stripJsonFence(value) {
9063
9248
  }
9064
9249
 
9065
9250
  // src/runner-resources.ts
9066
- import os8 from "node:os";
9251
+ import os9 from "node:os";
9067
9252
  var defaultRuntime = {
9068
9253
  nowMs: () => Date.now(),
9069
9254
  memoryUsage: () => process.memoryUsage(),
9070
9255
  uptime: () => process.uptime(),
9071
9256
  cpuUsage: () => process.cpuUsage(),
9072
- totalmem: () => os8.totalmem(),
9073
- freemem: () => os8.freemem(),
9074
- loadavg: () => os8.loadavg()
9257
+ totalmem: () => os9.totalmem(),
9258
+ freemem: () => os9.freemem(),
9259
+ loadavg: () => os9.loadavg()
9075
9260
  };
9076
9261
  var previousRunnerResourceSample;
9077
9262
  function sampleCurrentRunnerResourceUsage() {
@@ -9184,8 +9369,8 @@ function roundNumber(value, digits) {
9184
9369
  // src/importer.ts
9185
9370
  import { execFile as execFile4 } from "node:child_process";
9186
9371
  import { createHash as createHash7 } from "node:crypto";
9187
- import { readdir as readdir6, readFile as readFile12, stat as stat5 } from "node:fs/promises";
9188
- import path15 from "node:path";
9372
+ import { readdir as readdir6, readFile as readFile13, stat as stat6 } from "node:fs/promises";
9373
+ import path16 from "node:path";
9189
9374
  import { promisify as promisify4 } from "node:util";
9190
9375
  var execFileAsync4 = promisify4(execFile4);
9191
9376
  var defaultMaxFileKb = 256;
@@ -9206,12 +9391,12 @@ var documentFolderByType = {
9206
9391
  workflow: "docs/workflows"
9207
9392
  };
9208
9393
  async function inspectLocalRepository(rootDir, defaultBranch) {
9209
- const requestedRoot = path15.resolve(rootDir);
9394
+ const requestedRoot = path16.resolve(rootDir);
9210
9395
  const root = await runGit2(["-C", requestedRoot, "rev-parse", "--show-toplevel"]).catch(() => requestedRoot);
9211
9396
  const detectedBranch = await runGit2(["-C", root, "symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => defaultBranch);
9212
9397
  const originUrl = await runGit2(["-C", root, "remote", "get-url", "origin"]).catch(() => void 0);
9213
9398
  const parsedCloneUrl = originUrl ? parseOptionalOriginCloneUrl(originUrl) : void 0;
9214
- const repoName = (parsedCloneUrl?.repoName ?? path15.basename(root)) || "repository";
9399
+ const repoName = (parsedCloneUrl?.repoName ?? path16.basename(root)) || "repository";
9215
9400
  const fingerprintSeed = parsedCloneUrl ? `origin:${parsedCloneUrl.normalizedKey}` : `repo:${repoName}:${detectedBranch || defaultBranch}`;
9216
9401
  return {
9217
9402
  rootDir: root,
@@ -9223,7 +9408,7 @@ async function inspectLocalRepository(rootDir, defaultBranch) {
9223
9408
  };
9224
9409
  }
9225
9410
  async function scanLegacyDocuments(options) {
9226
- const rootDir = path15.resolve(options.rootDir);
9411
+ const rootDir = path16.resolve(options.rootDir);
9227
9412
  const maxBytes = (options.maxFileKb ?? defaultMaxFileKb) * 1024;
9228
9413
  const skipped = [];
9229
9414
  const candidates = [];
@@ -9243,8 +9428,8 @@ async function scanLegacyDocuments(options) {
9243
9428
  skipped.push({ repoPath, reason: "excluded" });
9244
9429
  continue;
9245
9430
  }
9246
- const fullPath = path15.join(rootDir, ...repoPath.split("/"));
9247
- const fileStat = await stat5(fullPath).catch(() => void 0);
9431
+ const fullPath = path16.join(rootDir, ...repoPath.split("/"));
9432
+ const fileStat = await stat6(fullPath).catch(() => void 0);
9248
9433
  if (!fileStat?.isFile()) {
9249
9434
  skipped.push({ repoPath, reason: "unreadable" });
9250
9435
  continue;
@@ -9253,7 +9438,7 @@ async function scanLegacyDocuments(options) {
9253
9438
  skipped.push({ repoPath, reason: "tooLarge" });
9254
9439
  continue;
9255
9440
  }
9256
- const content = await readFile12(fullPath, "utf8").catch(() => void 0);
9441
+ const content = await readFile13(fullPath, "utf8").catch(() => void 0);
9257
9442
  if (content === void 0) {
9258
9443
  skipped.push({ repoPath, reason: "unreadable" });
9259
9444
  continue;
@@ -9345,8 +9530,8 @@ async function listRepositoryPaths(rootDir) {
9345
9530
  async function walkRepository(rootDir, directory, files) {
9346
9531
  const entries = await readdir6(directory, { withFileTypes: true }).catch(() => []);
9347
9532
  for (const entry of entries) {
9348
- const fullPath = path15.join(directory, entry.name);
9349
- const repoPath = normalizeRepoPath4(path15.relative(rootDir, fullPath));
9533
+ const fullPath = path16.join(directory, entry.name);
9534
+ const repoPath = normalizeRepoPath4(path16.relative(rootDir, fullPath));
9350
9535
  if (entry.isDirectory()) {
9351
9536
  if (!excludedDirectoryNames.has(entry.name)) {
9352
9537
  await walkRepository(rootDir, fullPath, files);
@@ -9414,9 +9599,9 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
9414
9599
  usedPaths.add(basePath);
9415
9600
  return basePath;
9416
9601
  }
9417
- const extension = path15.posix.extname(basePath) || ".md";
9418
- const directory = path15.posix.dirname(basePath);
9419
- const basename = path15.posix.basename(basePath, extension);
9602
+ const extension = path16.posix.extname(basePath) || ".md";
9603
+ const directory = path16.posix.dirname(basePath);
9604
+ const basename = path16.posix.basename(basePath, extension);
9420
9605
  const uniquePath = `${directory}/${basename}-${hashText(sourcePath, 8)}${extension}`;
9421
9606
  usedPaths.add(uniquePath);
9422
9607
  return uniquePath;
@@ -9489,7 +9674,7 @@ function inferTitle2(content, repoPath) {
9489
9674
  if (heading) return heading;
9490
9675
  const htmlHeading = body.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
9491
9676
  if (htmlHeading) return htmlHeading;
9492
- const basename = path15.posix.basename(repoPath, path15.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
9677
+ const basename = path16.posix.basename(repoPath, path16.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
9493
9678
  return titleCase(basename || "Imported Document");
9494
9679
  }
9495
9680
  function stripFrontmatter(content) {
@@ -9530,7 +9715,7 @@ async function runGit2(args) {
9530
9715
 
9531
9716
  // src/runner-actions.ts
9532
9717
  import { spawn as spawn4 } from "node:child_process";
9533
- import path16 from "node:path";
9718
+ import path17 from "node:path";
9534
9719
  function buildBackgroundRunnerArgs(options) {
9535
9720
  const args = [
9536
9721
  "run",
@@ -9540,7 +9725,7 @@ function buildBackgroundRunnerArgs(options) {
9540
9725
  "--runner-id",
9541
9726
  options.runnerId,
9542
9727
  "--root",
9543
- path16.resolve(options.root),
9728
+ path17.resolve(options.root),
9544
9729
  "--session",
9545
9730
  options.session,
9546
9731
  "--interval-seconds",
@@ -9664,8 +9849,8 @@ function truncateProcessOutput(value) {
9664
9849
 
9665
9850
  // src/git-worktree.ts
9666
9851
  import { execFile as execFile5 } from "node:child_process";
9667
- import { copyFile, lstat, mkdir as mkdir12, readdir as readdir7, stat as stat6 } from "node:fs/promises";
9668
- import path17 from "node:path";
9852
+ import { copyFile, lstat, mkdir as mkdir13, readdir as readdir7, stat as stat7 } from "node:fs/promises";
9853
+ import path18 from "node:path";
9669
9854
  import { promisify as promisify5 } from "node:util";
9670
9855
  var execFileAsync5 = promisify5(execFile5);
9671
9856
  var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
@@ -9700,7 +9885,7 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
9700
9885
  return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
9701
9886
  }
9702
9887
  await repairMissingRegisteredWorktree(repoRoot, worktreePath, identity.branch, identity.worktreeKey);
9703
- await mkdir12(path17.dirname(worktreePath), { recursive: true });
9888
+ await mkdir13(path18.dirname(worktreePath), { recursive: true });
9704
9889
  const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
9705
9890
  const worktreeArgs = branchExists ? ["worktree", "add", worktreePath, identity.branch] : ["worktree", "add", "-b", identity.branch, worktreePath, baseRevision];
9706
9891
  await gitOutput(repoRoot, worktreeArgs).catch((error) => {
@@ -9718,9 +9903,9 @@ async function resolveExistingGitWorktreeIsolation(rootDir, workItem) {
9718
9903
  return { ...identity, baseRevision, worktreePath };
9719
9904
  }
9720
9905
  function localWorktreePath(repoRoot, worktreeKey) {
9721
- const repoName = path17.basename(repoRoot);
9906
+ const repoName = path18.basename(repoRoot);
9722
9907
  const worktreeSlug = worktreeKey.split("/").filter(Boolean).pop() ?? "work";
9723
- return path17.join(path17.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
9908
+ return path18.join(path18.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
9724
9909
  }
9725
9910
  async function assertExistingWorktree(worktreePath, branch) {
9726
9911
  await gitOutput(worktreePath, ["rev-parse", "--is-inside-work-tree"]);
@@ -9783,7 +9968,7 @@ function parseRegisteredGitWorktrees(output) {
9783
9968
  return worktrees;
9784
9969
  }
9785
9970
  function normalizeGitWorktreePath(value) {
9786
- const resolved = path17.resolve(value);
9971
+ const resolved = path18.resolve(value);
9787
9972
  return resolved.startsWith("/private/var/") ? resolved.replace("/private/var/", "/var/") : resolved;
9788
9973
  }
9789
9974
  async function assertBaseRevision(repoRoot, baseRevision, currentHead) {
@@ -9803,8 +9988,8 @@ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
9803
9988
  const candidates = await localEnvironmentFileCandidates(repoRoot);
9804
9989
  let preparedCount = 0;
9805
9990
  for (const candidate of candidates) {
9806
- const sourcePath = path17.join(repoRoot, candidate);
9807
- const targetPath = path17.join(worktreePath, candidate);
9991
+ const sourcePath = path18.join(repoRoot, candidate);
9992
+ const targetPath = path18.join(worktreePath, candidate);
9808
9993
  if (await pathExists(targetPath)) {
9809
9994
  continue;
9810
9995
  }
@@ -9835,7 +10020,7 @@ async function localEnvironmentFileCandidates(repoRoot) {
9835
10020
  if (!isRootFileName(name)) {
9836
10021
  continue;
9837
10022
  }
9838
- const source = await lstat(path17.join(repoRoot, name)).catch(() => void 0);
10023
+ const source = await lstat(path18.join(repoRoot, name)).catch(() => void 0);
9839
10024
  if (source?.isFile()) {
9840
10025
  candidates.push(name);
9841
10026
  }
@@ -9846,7 +10031,7 @@ function isAllowedLocalEnvironmentFile(name) {
9846
10031
  return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
9847
10032
  }
9848
10033
  function isRootFileName(name) {
9849
- return name === path17.basename(name) && !name.includes("/") && !name.includes("\\");
10034
+ return name === path18.basename(name) && !name.includes("/") && !name.includes("\\");
9850
10035
  }
9851
10036
  async function gitOutput(cwd, args) {
9852
10037
  const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
@@ -9856,7 +10041,7 @@ async function gitCommandSucceeds(cwd, args) {
9856
10041
  return execFileAsync5("git", args, { cwd }).then(() => true, () => false);
9857
10042
  }
9858
10043
  async function pathExists(value) {
9859
- return stat6(value).then(() => true, () => false);
10044
+ return stat7(value).then(() => true, () => false);
9860
10045
  }
9861
10046
  function workIsolationSlug(scopeId, title) {
9862
10047
  const scopeSlug = slugify(scopeId);
@@ -9879,7 +10064,7 @@ function safeFileError(error) {
9879
10064
 
9880
10065
  // src/implementation-handoff.ts
9881
10066
  import { execFile as execFile6 } from "node:child_process";
9882
- import path18 from "node:path";
10067
+ import path19 from "node:path";
9883
10068
  import { promisify as promisify6 } from "node:util";
9884
10069
  var execFileAsync6 = promisify6(execFile6);
9885
10070
  var DEFAULT_IMPLEMENTATION_EXPECTED_OUTCOME = "sourceImplementation";
@@ -10333,7 +10518,7 @@ async function cleanupWorktree(run, input) {
10333
10518
  return { status: "failed", message: "Cleanup skipped because the worktree is not clean after PR handoff." };
10334
10519
  }
10335
10520
  try {
10336
- await gitOutput2(run, input.primaryRepoRoot || path18.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
10521
+ await gitOutput2(run, input.primaryRepoRoot || path19.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
10337
10522
  return { status: "completed" };
10338
10523
  } catch (error) {
10339
10524
  return { status: "failed", message: `Cleanup failed: ${safeErrorMessage(error)}` };
@@ -11151,7 +11336,7 @@ program.command("import").description("Pair an existing checkout and import lega
11151
11336
  });
11152
11337
  program.command("pair").description("Pair this repository with an Amistio web project").requiredOption("--account <accountId>", "Amistio account ID").requiredOption("--project <projectId>", "Amistio project ID").option("--repository-link <repositoryLinkId>", "Existing repository link ID").option("--default-branch <branch>", "Default branch", "main").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--pairing-code <code>", "Short-lived pairing code from the Amistio app").option("--token <token>", "Runner/device credential to store outside the repository").option("--root <path>", "Repository root", defaultRoot).action(async (options, command) => {
11153
11338
  const pairingRoot = await resolvePairingRoot(options.root, { explicitRoot: command.getOptionValueSource("root") === "cli" });
11154
- let repositoryLinkId = options.repositoryLink ?? `repo_${randomUUID3()}`;
11339
+ let repositoryLinkId = options.repositoryLink ?? `repo_${randomUUID4()}`;
11155
11340
  let credential = options.token;
11156
11341
  if (options.pairingCode) {
11157
11342
  const pairing = await new ApiClient({
@@ -11292,7 +11477,7 @@ sync.command("watch").description("Watch repository brain folders and auto-sync
11292
11477
  console.log(`Auto-sync watcher stopped after ${iterations} polling attempt${iterations === 1 ? "" : "s"}.`);
11293
11478
  return;
11294
11479
  }
11295
- await delay(options.intervalSeconds * 1e3);
11480
+ await delay2(options.intervalSeconds * 1e3);
11296
11481
  }
11297
11482
  });
11298
11483
  var work = program.command("work").description("Inspect approved work items");
@@ -11337,7 +11522,7 @@ work.command("prompt").description("Print or write an approved work prompt witho
11337
11522
  }
11338
11523
  const prompt = await createRunnerWorkPrompt(context.client, context.metadata.amistioProjectId, workItem);
11339
11524
  if (options.out) {
11340
- await writeFile12(options.out, prompt, "utf8");
11525
+ await writeFile13(options.out, prompt, "utf8");
11341
11526
  console.log(`Wrote work prompt to ${options.out}.`);
11342
11527
  } else {
11343
11528
  console.log(prompt);
@@ -11447,7 +11632,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
11447
11632
  ...options.toolCommand ? { toolCommand: options.toolCommand } : {},
11448
11633
  ...localModelConfig,
11449
11634
  streamOutput: options.stream,
11450
- ...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID3()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
11635
+ ...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID4()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
11451
11636
  });
11452
11637
  if (!options.stream && result.stdout.trim()) {
11453
11638
  console.log(result.stdout.trim());
@@ -11488,7 +11673,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
11488
11673
  projectId: context.metadata.amistioProjectId,
11489
11674
  repositoryLinkId: context.metadata.repositoryLinkId,
11490
11675
  runnerId,
11491
- rootDir: path19.resolve(options.root),
11676
+ rootDir: path20.resolve(options.root),
11492
11677
  apiUrl: options.apiUrl,
11493
11678
  args: buildBackgroundRunnerArgs(resolvedOptions)
11494
11679
  });
@@ -11558,7 +11743,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
11558
11743
  console.log(`Runner stopped after ${iterations} polling attempt${iterations === 1 ? "" : "s"}.`);
11559
11744
  return;
11560
11745
  }
11561
- await delay(options.intervalSeconds * 1e3);
11746
+ await delay2(options.intervalSeconds * 1e3);
11562
11747
  }
11563
11748
  } finally {
11564
11749
  process.off("SIGINT", handleShutdownSignal);
@@ -11717,7 +11902,7 @@ runnerService.command("install").description("Install a user-level startup servi
11717
11902
  projectId: context.metadata.amistioProjectId,
11718
11903
  repositoryLinkId: context.metadata.repositoryLinkId,
11719
11904
  runnerId,
11720
- rootDir: path19.resolve(options.root),
11905
+ rootDir: path20.resolve(options.root),
11721
11906
  apiUrl: options.apiUrl,
11722
11907
  args,
11723
11908
  platform
@@ -12006,612 +12191,696 @@ async function runNextWorkItem({
12006
12191
  }
12007
12192
  return { status: "idle", exitCode: 0, nextAction, message };
12008
12193
  }
12009
- const prompt = await createRunnerWorkPrompt(apiClient, projectId, result.workItem);
12010
- await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12011
- status: "running",
12012
- summary: "Prepared local runner execution prompt.",
12013
- idempotencyKey: `runner_milestone_prompt_${result.workItem.workItemId}_${result.workItem.attempt}`,
12014
- metadata: { workKind: result.workItem.workKind ?? "implementation", attempt: result.workItem.attempt }
12015
- });
12016
- if (dryRun || toolConfig.tool === "none") {
12017
- console.log(prompt);
12018
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12019
- return { status: "preview", exitCode: 0 };
12020
- }
12021
- const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
12022
- if (worktreeIsolation.status !== "ready") {
12023
- return { status: worktreeIsolation.status === "failed" ? "failed" : "blocked", exitCode: 1, message: worktreeIsolation.message };
12024
- }
12025
- const executionRoot = worktreeIsolation.isolation?.worktreePath ?? root;
12026
- const isolationTelemetry = workItemIsolationTelemetry(result.workItem, worktreeIsolation.isolation);
12027
- const environmentReadiness = await checkRunnerExecutionEnvironment({
12028
- executionRoot,
12029
- primaryCheckoutRoot: root,
12030
- profile: executionProfile,
12031
- setupCommands: runnerEnvironmentSetupCommands(setupPackageManagerInstall),
12032
- ...result.workItem.workKind ? { workKind: result.workItem.workKind } : {},
12033
- env: process.env
12034
- });
12035
- if (environmentReadiness.profile === "dockerWorkspace" && environmentReadiness.status === "ready") {
12036
- environmentReadiness.status = "blocked";
12037
- environmentReadiness.summary = "Runner execution environment dockerWorkspace is blocked: containerized harness execution is not enabled in this runner build.";
12038
- environmentReadiness.blockers.push({ reason: "unsupportedProfile", summary: "Containerized harness execution is not enabled in this runner build.", remediation: "Select hostWorktree or hostWorktreeWithSetup until Docker workspace execution is enabled.", safePaths: [] });
12039
- environmentReadiness.checks.push({ checkId: "docker-harness", title: "Docker harness", status: "blocked", reason: "unsupportedProfile", summary: "Docker workspace envelope is valid, but this runner cannot execute the harness inside the container yet.", safePaths: [] });
12040
- }
12041
- if (environmentReadiness.status === "blocked") {
12042
- const statusResult2 = await apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "blocked", `environment_blocked_${result.workItem.workItemId}_${result.workItem.attempt}_${randomUUID3()}`, runnerId, {
12043
- ...isolationTelemetry,
12044
- executionEnvironmentProfile: environmentReadiness.profile,
12045
- executionEnvironmentReadiness: environmentReadiness,
12046
- blockerReason: environmentReadiness.summary,
12047
- message: environmentReadiness.summary
12194
+ const localActiveClaim = await defaultRunnerActiveClaimStore.reserve(runnerActiveClaimInput({
12195
+ accountId: commandContext.accountId,
12196
+ claimLaneId,
12197
+ repositoryLinkId,
12198
+ runnerId,
12199
+ toolTimeoutMs,
12200
+ workItem: result.workItem
12201
+ }));
12202
+ if (localActiveClaim.status === "blocked") {
12203
+ return await releaseSkippedLocalClaim({
12204
+ apiClient,
12205
+ claimLaneId,
12206
+ conflict: localActiveClaim,
12207
+ projectId,
12208
+ repositoryLinkId,
12209
+ runnerId,
12210
+ workItem: result.workItem
12048
12211
  });
12049
- await recordRunnerMilestone(apiClient, projectId, statusResult2.workItem, runnerId, repositoryLinkId, {
12050
- status: "warning",
12051
- summary: environmentReadiness.summary,
12052
- idempotencyKey: `runner_milestone_environment_blocked_${result.workItem.workItemId}_${statusResult2.workItem.idempotencyKey}`,
12053
- metadata: { executionEnvironmentProfile: environmentReadiness.profile, blockerReasons: environmentReadiness.blockers.map((blocker) => blocker.reason) }
12212
+ }
12213
+ const releaseLocalActiveClaim = async () => {
12214
+ await defaultRunnerActiveClaimStore.release(localActiveClaim.record.reservationId).catch((error) => {
12215
+ console.error(`Could not release local Amistio active-claim reservation for ${result.workItem?.workItemId}: ${truncateLogExcerpt(errorDetail(error))}`);
12054
12216
  });
12055
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "blocked", {
12056
- ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
12057
- currentWorkItemId: result.workItem.workItemId,
12058
- currentExecutionEnvironmentProfile: environmentReadiness.profile,
12059
- environmentReadiness,
12060
- preferenceMessage: environmentReadiness.summary,
12061
- ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
12062
- ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
12063
- ...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
12217
+ };
12218
+ try {
12219
+ const prompt = await createRunnerWorkPrompt(apiClient, projectId, result.workItem);
12220
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12221
+ status: "running",
12222
+ summary: "Prepared local runner execution prompt.",
12223
+ idempotencyKey: `runner_milestone_prompt_${result.workItem.workItemId}_${result.workItem.attempt}`,
12224
+ metadata: { workKind: result.workItem.workKind ?? "implementation", attempt: result.workItem.attempt }
12064
12225
  });
12065
- console.error(environmentReadiness.summary);
12066
- return { status: "blocked", exitCode: 1, message: environmentReadiness.summary };
12067
- }
12068
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
12069
- ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
12070
- currentWorkItemId: result.workItem.workItemId,
12071
- currentExecutionEnvironmentProfile: environmentReadiness.profile,
12072
- environmentReadiness,
12073
- ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
12074
- ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
12075
- ...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
12076
- });
12077
- if (isImplementationHandoffWork(result.workItem)) {
12078
- const repositoryLink = await loadWorkItemRepositoryLink(apiClient, projectId, result.workItem.repositoryLinkId ?? repositoryLinkId).catch(() => void 0);
12079
- const readinessHandoff = await checkImplementationHandoffReadiness({
12080
- primaryRepoRoot: root,
12081
- ...repositoryLink ? { repositoryLink } : {},
12082
- workItem: result.workItem,
12083
- ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
12084
- worktreePath: executionRoot
12226
+ if (dryRun || toolConfig.tool === "none") {
12227
+ console.log(prompt);
12228
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12229
+ return { status: "preview", exitCode: 0 };
12230
+ }
12231
+ const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
12232
+ if (worktreeIsolation.status !== "ready") {
12233
+ return { status: worktreeIsolation.status === "failed" ? "failed" : "blocked", exitCode: 1, message: worktreeIsolation.message };
12234
+ }
12235
+ const executionRoot = worktreeIsolation.isolation?.worktreePath ?? root;
12236
+ const isolationTelemetry = workItemIsolationTelemetry(result.workItem, worktreeIsolation.isolation);
12237
+ const environmentReadiness = await checkRunnerExecutionEnvironment({
12238
+ executionRoot,
12239
+ primaryCheckoutRoot: root,
12240
+ profile: executionProfile,
12241
+ setupCommands: runnerEnvironmentSetupCommands(setupPackageManagerInstall),
12242
+ ...result.workItem.workKind ? { workKind: result.workItem.workKind } : {},
12243
+ env: process.env
12085
12244
  });
12086
- if (readinessHandoff) {
12087
- const message = readinessHandoff.message ?? "Implementation handoff readiness is blocked before local tool execution.";
12088
- const statusResult2 = await apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "blocked", `handoff_readiness_blocked_${result.workItem.workItemId}_${result.workItem.attempt}_${randomUUID3()}`, runnerId, {
12245
+ if (environmentReadiness.profile === "dockerWorkspace" && environmentReadiness.status === "ready") {
12246
+ environmentReadiness.status = "blocked";
12247
+ environmentReadiness.summary = "Runner execution environment dockerWorkspace is blocked: containerized harness execution is not enabled in this runner build.";
12248
+ environmentReadiness.blockers.push({ reason: "unsupportedProfile", summary: "Containerized harness execution is not enabled in this runner build.", remediation: "Select hostWorktree or hostWorktreeWithSetup until Docker workspace execution is enabled.", safePaths: [] });
12249
+ environmentReadiness.checks.push({ checkId: "docker-harness", title: "Docker harness", status: "blocked", reason: "unsupportedProfile", summary: "Docker workspace envelope is valid, but this runner cannot execute the harness inside the container yet.", safePaths: [] });
12250
+ }
12251
+ if (environmentReadiness.status === "blocked") {
12252
+ const statusResult2 = await apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "blocked", `environment_blocked_${result.workItem.workItemId}_${result.workItem.attempt}_${randomUUID4()}`, runnerId, {
12089
12253
  ...isolationTelemetry,
12090
- implementationHandoff: readinessHandoff,
12091
- blockerReason: message,
12092
- message,
12093
- ...readinessHandoff.error ? { error: readinessHandoff.error } : {}
12254
+ executionEnvironmentProfile: environmentReadiness.profile,
12255
+ executionEnvironmentReadiness: environmentReadiness,
12256
+ blockerReason: environmentReadiness.summary,
12257
+ message: environmentReadiness.summary
12094
12258
  });
12095
12259
  await recordRunnerMilestone(apiClient, projectId, statusResult2.workItem, runnerId, repositoryLinkId, {
12096
- status: "blocked",
12097
- summary: message,
12098
- idempotencyKey: `runner_milestone_handoff_readiness_blocked_${result.workItem.workItemId}_${statusResult2.workItem.idempotencyKey}`,
12099
- metadata: {
12100
- recoveryCategory: readinessHandoff.recovery?.category ?? "providerBlocked",
12101
- executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "",
12102
- executionBranch: isolationTelemetry.executionBranch ?? ""
12103
- }
12260
+ status: "warning",
12261
+ summary: environmentReadiness.summary,
12262
+ idempotencyKey: `runner_milestone_environment_blocked_${result.workItem.workItemId}_${statusResult2.workItem.idempotencyKey}`,
12263
+ metadata: { executionEnvironmentProfile: environmentReadiness.profile, blockerReasons: environmentReadiness.blockers.map((blocker) => blocker.reason) }
12104
12264
  });
12105
12265
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "blocked", {
12106
12266
  ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
12107
12267
  currentWorkItemId: result.workItem.workItemId,
12108
- preferenceMessage: message,
12268
+ currentExecutionEnvironmentProfile: environmentReadiness.profile,
12269
+ environmentReadiness,
12270
+ preferenceMessage: environmentReadiness.summary,
12109
12271
  ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
12110
12272
  ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
12111
12273
  ...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
12112
12274
  });
12113
- console.error(message);
12114
- return { status: "blocked", exitCode: 1, message };
12275
+ console.error(environmentReadiness.summary);
12276
+ return { status: "blocked", exitCode: 1, message: environmentReadiness.summary };
12115
12277
  }
12116
- }
12117
- const resolvedModelConfig = toolConfigModelOptions(toolConfig);
12118
- const preparedHarnessRun = await builtinAmistioHarnessAdapter.createRunPreview({
12119
- rootDir: executionRoot,
12120
- prompt,
12121
- tool: toolConfig.tool,
12122
- invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
12123
- ...toolCommand ? { toolCommand } : {},
12124
- ...resolvedModelConfig,
12125
- executionPolicy: {
12126
- executionRoot,
12127
- mutationPolicy: harnessMutationPolicyForWorkItem(result.workItem),
12128
- claimLaneId
12129
- }
12130
- });
12131
- const preview = preparedHarnessRun.preview;
12132
- const sessionContext = await prepareToolSession({
12133
- apiClient,
12134
- projectId,
12135
- repositoryLinkId,
12136
- runnerId,
12137
- machineId: runnerMachineId(),
12138
- sessionPolicy: result.workItem.sessionPolicy ?? sessionPolicy,
12139
- toolName: preview.toolName,
12140
- ...toolConfig.model ? { model: toolConfig.model } : {},
12141
- supportsSessionReuse: preview.supportsSessionReuse,
12142
- resumabilityScope: preview.resumabilityScope,
12143
- workItem: result.workItem,
12144
- isolationTelemetry
12145
- });
12146
- console.log(`Claimed ${result.workItem.workItemId}. Running ${preview.toolName}: ${preview.displayCommand}`);
12147
- const autopilotClaimLine = formatAutopilotClaimLine(result.workItem);
12148
- if (autopilotClaimLine) {
12149
- console.log(autopilotClaimLine);
12150
- }
12151
- const promptBatchClaimLine = formatPromptBatchClaimLine(result.workItem);
12152
- if (promptBatchClaimLine) {
12153
- console.log(promptBatchClaimLine);
12154
- }
12155
- await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12156
- status: "running",
12157
- summary: `Local runner started ${preview.toolName} execution.`,
12158
- idempotencyKey: `runner_milestone_started_${result.workItem.workItemId}_${result.workItem.attempt}`,
12159
- metadata: {
12160
- tool: preview.toolName,
12161
- invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
12162
- claimLaneId: preparedHarnessRun.concurrency.claimLaneId,
12163
- harnessSupportsConcurrentRuns: preparedHarnessRun.concurrency.supportsConcurrentRuns,
12164
- harnessRequiresExclusiveRepository: preparedHarnessRun.concurrency.requiresExclusiveRepository,
12165
- harnessRequiresExclusiveProviderAuth: preparedHarnessRun.concurrency.requiresExclusiveProviderAuth,
12166
- harnessSerializedResourceKeys: preparedHarnessRun.concurrency.serializedResourceKeys
12278
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
12279
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
12280
+ currentWorkItemId: result.workItem.workItemId,
12281
+ currentExecutionEnvironmentProfile: environmentReadiness.profile,
12282
+ environmentReadiness,
12283
+ ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
12284
+ ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
12285
+ ...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
12286
+ });
12287
+ if (isImplementationHandoffWork(result.workItem)) {
12288
+ const repositoryLink = await loadWorkItemRepositoryLink(apiClient, projectId, result.workItem.repositoryLinkId ?? repositoryLinkId).catch(() => void 0);
12289
+ const readinessHandoff = await checkImplementationHandoffReadiness({
12290
+ primaryRepoRoot: root,
12291
+ ...repositoryLink ? { repositoryLink } : {},
12292
+ workItem: result.workItem,
12293
+ ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
12294
+ worktreePath: executionRoot
12295
+ });
12296
+ if (readinessHandoff) {
12297
+ const message = readinessHandoff.message ?? "Implementation handoff readiness is blocked before local tool execution.";
12298
+ const statusResult2 = await apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "blocked", `handoff_readiness_blocked_${result.workItem.workItemId}_${result.workItem.attempt}_${randomUUID4()}`, runnerId, {
12299
+ ...isolationTelemetry,
12300
+ implementationHandoff: readinessHandoff,
12301
+ blockerReason: message,
12302
+ message,
12303
+ ...readinessHandoff.error ? { error: readinessHandoff.error } : {}
12304
+ });
12305
+ await recordRunnerMilestone(apiClient, projectId, statusResult2.workItem, runnerId, repositoryLinkId, {
12306
+ status: "blocked",
12307
+ summary: message,
12308
+ idempotencyKey: `runner_milestone_handoff_readiness_blocked_${result.workItem.workItemId}_${statusResult2.workItem.idempotencyKey}`,
12309
+ metadata: {
12310
+ recoveryCategory: readinessHandoff.recovery?.category ?? "providerBlocked",
12311
+ executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "",
12312
+ executionBranch: isolationTelemetry.executionBranch ?? ""
12313
+ }
12314
+ });
12315
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "blocked", {
12316
+ ...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
12317
+ currentWorkItemId: result.workItem.workItemId,
12318
+ preferenceMessage: message,
12319
+ ...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
12320
+ ...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
12321
+ ...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
12322
+ });
12323
+ console.error(message);
12324
+ return { status: "blocked", exitCode: 1, message };
12325
+ }
12167
12326
  }
12168
- });
12169
- const startedAt = Date.now();
12170
- const providerSessionStore = new LocalToolSessionStore();
12171
- const providerSessionId = sessionContext.toolSession ? await providerSessionStore.getProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName) : void 0;
12172
- let toolResult;
12173
- const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry, heartbeatConcurrency });
12174
- try {
12175
- toolResult = await builtinAmistioHarnessAdapter.executeRun({
12176
- preparedRun: preparedHarnessRun,
12327
+ const resolvedModelConfig = toolConfigModelOptions(toolConfig);
12328
+ const preparedHarnessRun = await builtinAmistioHarnessAdapter.createRunPreview({
12177
12329
  rootDir: executionRoot,
12178
12330
  prompt,
12179
12331
  tool: toolConfig.tool,
12180
12332
  invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
12181
12333
  ...toolCommand ? { toolCommand } : {},
12182
12334
  ...resolvedModelConfig,
12183
- streamOutput: stream,
12184
- timeoutMs: toolTimeoutMs,
12185
- ...sessionContext.toolSession ? {
12186
- session: {
12187
- toolSessionId: sessionContext.toolSession.toolSessionId,
12188
- policy: sessionContext.policy,
12189
- decision: sessionContext.decision,
12190
- ...providerSessionId ? { providerSessionId } : {}
12191
- }
12192
- } : {}
12335
+ executionPolicy: {
12336
+ executionRoot,
12337
+ mutationPolicy: harnessMutationPolicyForWorkItem(result.workItem),
12338
+ claimLaneId
12339
+ }
12193
12340
  });
12194
- } catch (error) {
12195
- stopLeaseRenewal();
12196
- const detail = truncateLogExcerpt(errorDetail(error));
12197
- const durationMs2 = Date.now() - startedAt;
12198
- const message = `${preview.toolName} failed before returning a result.`;
12199
- const settlements = await Promise.allSettled([
12200
- apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)),
12201
- markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage7(error)),
12202
- apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "failed", `run_failed_${result.workItem.workItemId}_${result.workItem.attempt}_${runnerId}`, runnerId, {
12203
- ...isolationTelemetry,
12204
- tool: preview.toolName,
12205
- ...toolConfig.model ? { model: toolConfig.model } : {},
12206
- durationMs: durationMs2,
12207
- message,
12208
- error: detail,
12209
- ...result.workItem.workKind === "promptBatch" ? { promptBatch: finalizePromptBatchMetadata(result.workItem, "failed", detail) } : {},
12210
- ...sessionContext.toolSession ? { toolSessionId: sessionContext.toolSession.toolSessionId } : {},
12211
- sessionPolicy: sessionContext.policy,
12212
- sessionDecision: sessionContext.decision,
12213
- sessionDecisionReason: sessionContext.reason
12214
- }),
12215
- recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12216
- status: "failed",
12217
- summary: message,
12218
- idempotencyKey: `runner_milestone_tool_throw_${result.workItem.workItemId}_${result.workItem.attempt}`,
12219
- metadata: { tool: preview.toolName, error: detail }
12220
- })
12221
- ]);
12222
- logRejectedSettlements("record local tool failure", settlements);
12223
- if (verbose || !stream) {
12224
- console.error(detail);
12341
+ const preview = preparedHarnessRun.preview;
12342
+ const sessionContext = await prepareToolSession({
12343
+ apiClient,
12344
+ projectId,
12345
+ repositoryLinkId,
12346
+ runnerId,
12347
+ machineId: runnerMachineId(),
12348
+ sessionPolicy: result.workItem.sessionPolicy ?? sessionPolicy,
12349
+ toolName: preview.toolName,
12350
+ ...toolConfig.model ? { model: toolConfig.model } : {},
12351
+ supportsSessionReuse: preview.supportsSessionReuse,
12352
+ resumabilityScope: preview.resumabilityScope,
12353
+ workItem: result.workItem,
12354
+ isolationTelemetry
12355
+ });
12356
+ console.log(`Claimed ${result.workItem.workItemId}. Running ${preview.toolName}: ${preview.displayCommand}`);
12357
+ const autopilotClaimLine = formatAutopilotClaimLine(result.workItem);
12358
+ if (autopilotClaimLine) {
12359
+ console.log(autopilotClaimLine);
12225
12360
  }
12226
- return { status: "failed", exitCode: 1, message };
12227
- }
12228
- stopLeaseRenewal();
12229
- if (sessionContext.toolSession && toolResult.providerSessionId) {
12230
- await providerSessionStore.setProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName, toolResult.providerSessionId);
12231
- }
12232
- if (!stream && toolResult.stdout.trim()) {
12233
- console.log(toolResult.stdout.trim());
12234
- }
12235
- if (!stream && toolResult.stderr.trim()) {
12236
- console.error(toolResult.stderr.trim());
12237
- }
12238
- if (result.workItem.workKind === "brainGeneration" || result.workItem.workKind === "planRevision") {
12239
- try {
12240
- return await finalizeBrainGenerationWork({
12241
- apiClient,
12242
- durationMs: Date.now() - startedAt,
12243
- projectId,
12244
- repositoryLinkId,
12245
- runnerId,
12246
- sessionContext,
12247
- toolConfig,
12248
- toolName: preview.toolName,
12249
- toolResult,
12250
- workItem: result.workItem
12251
- });
12252
- } catch (error) {
12253
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12361
+ const promptBatchClaimLine = formatPromptBatchClaimLine(result.workItem);
12362
+ if (promptBatchClaimLine) {
12363
+ console.log(promptBatchClaimLine);
12254
12364
  }
12255
- }
12256
- if (result.workItem.workKind === "assistantQuestion") {
12365
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12366
+ status: "running",
12367
+ summary: `Local runner started ${preview.toolName} execution.`,
12368
+ idempotencyKey: `runner_milestone_started_${result.workItem.workItemId}_${result.workItem.attempt}`,
12369
+ metadata: {
12370
+ tool: preview.toolName,
12371
+ invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
12372
+ claimLaneId: preparedHarnessRun.concurrency.claimLaneId,
12373
+ harnessSupportsConcurrentRuns: preparedHarnessRun.concurrency.supportsConcurrentRuns,
12374
+ harnessRequiresExclusiveRepository: preparedHarnessRun.concurrency.requiresExclusiveRepository,
12375
+ harnessRequiresExclusiveProviderAuth: preparedHarnessRun.concurrency.requiresExclusiveProviderAuth,
12376
+ harnessSerializedResourceKeys: preparedHarnessRun.concurrency.serializedResourceKeys
12377
+ }
12378
+ });
12379
+ const startedAt = Date.now();
12380
+ const providerSessionStore = new LocalToolSessionStore();
12381
+ const providerSessionId = sessionContext.toolSession ? await providerSessionStore.getProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName) : void 0;
12382
+ let toolResult;
12383
+ const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry, heartbeatConcurrency });
12257
12384
  try {
12258
- return await finalizeAssistantQuestionWork({
12259
- apiClient,
12260
- durationMs: Date.now() - startedAt,
12261
- projectId,
12262
- repositoryLinkId,
12263
- runnerId,
12264
- sessionContext,
12265
- toolConfig,
12266
- toolName: preview.toolName,
12267
- toolResult,
12268
- workItem: result.workItem
12385
+ toolResult = await builtinAmistioHarnessAdapter.executeRun({
12386
+ preparedRun: preparedHarnessRun,
12387
+ rootDir: executionRoot,
12388
+ prompt,
12389
+ tool: toolConfig.tool,
12390
+ invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
12391
+ ...toolCommand ? { toolCommand } : {},
12392
+ ...resolvedModelConfig,
12393
+ streamOutput: stream,
12394
+ timeoutMs: toolTimeoutMs,
12395
+ ...sessionContext.toolSession ? {
12396
+ session: {
12397
+ toolSessionId: sessionContext.toolSession.toolSessionId,
12398
+ policy: sessionContext.policy,
12399
+ decision: sessionContext.decision,
12400
+ ...providerSessionId ? { providerSessionId } : {}
12401
+ }
12402
+ } : {}
12269
12403
  });
12270
12404
  } catch (error) {
12271
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12405
+ stopLeaseRenewal();
12406
+ const detail = truncateLogExcerpt(errorDetail(error));
12407
+ const durationMs2 = Date.now() - startedAt;
12408
+ const message = `${preview.toolName} failed before returning a result.`;
12409
+ const settlements = await Promise.allSettled([
12410
+ apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)),
12411
+ markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage7(error)),
12412
+ apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "failed", `run_failed_${result.workItem.workItemId}_${result.workItem.attempt}_${runnerId}`, runnerId, {
12413
+ ...isolationTelemetry,
12414
+ tool: preview.toolName,
12415
+ ...toolConfig.model ? { model: toolConfig.model } : {},
12416
+ durationMs: durationMs2,
12417
+ message,
12418
+ error: detail,
12419
+ ...result.workItem.workKind === "promptBatch" ? { promptBatch: finalizePromptBatchMetadata(result.workItem, "failed", detail) } : {},
12420
+ ...sessionContext.toolSession ? { toolSessionId: sessionContext.toolSession.toolSessionId } : {},
12421
+ sessionPolicy: sessionContext.policy,
12422
+ sessionDecision: sessionContext.decision,
12423
+ sessionDecisionReason: sessionContext.reason
12424
+ }),
12425
+ recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12426
+ status: "failed",
12427
+ summary: message,
12428
+ idempotencyKey: `runner_milestone_tool_throw_${result.workItem.workItemId}_${result.workItem.attempt}`,
12429
+ metadata: { tool: preview.toolName, error: detail }
12430
+ })
12431
+ ]);
12432
+ logRejectedSettlements("record local tool failure", settlements);
12433
+ if (verbose || !stream) {
12434
+ console.error(detail);
12435
+ }
12436
+ return { status: "failed", exitCode: 1, message };
12272
12437
  }
12273
- }
12274
- if (result.workItem.workKind === "impactPreview") {
12275
- try {
12276
- return await finalizeImpactPreviewWork({
12277
- apiClient,
12278
- durationMs: Date.now() - startedAt,
12279
- projectId,
12280
- repositoryLinkId,
12281
- root,
12282
- runnerId,
12283
- sessionContext,
12284
- toolConfig,
12285
- toolName: preview.toolName,
12286
- toolResult,
12287
- workItem: result.workItem
12288
- });
12289
- } catch (error) {
12290
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12438
+ stopLeaseRenewal();
12439
+ if (sessionContext.toolSession && toolResult.providerSessionId) {
12440
+ await providerSessionStore.setProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName, toolResult.providerSessionId);
12291
12441
  }
12292
- }
12293
- if (result.workItem.workKind === "issueDiagnosis") {
12294
- try {
12295
- return await finalizeIssueDiagnosisWork({
12296
- apiClient,
12297
- durationMs: Date.now() - startedAt,
12298
- projectId,
12299
- repositoryLinkId,
12300
- runnerId,
12301
- sessionContext,
12302
- toolConfig,
12303
- toolName: preview.toolName,
12304
- toolResult,
12305
- workItem: result.workItem
12306
- });
12307
- } catch (error) {
12308
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12442
+ if (!stream && toolResult.stdout.trim()) {
12443
+ console.log(toolResult.stdout.trim());
12309
12444
  }
12310
- }
12311
- if (result.workItem.workKind === "securityPostureScan") {
12312
- try {
12313
- return await finalizeSecurityPostureScanWork({
12314
- apiClient,
12315
- durationMs: Date.now() - startedAt,
12316
- projectId,
12317
- repositoryLinkId,
12318
- runnerId,
12319
- sessionContext,
12320
- toolConfig,
12321
- toolName: preview.toolName,
12322
- toolResult,
12323
- workItem: result.workItem
12324
- });
12325
- } catch (error) {
12326
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12445
+ if (!stream && toolResult.stderr.trim()) {
12446
+ console.error(toolResult.stderr.trim());
12327
12447
  }
12328
- }
12329
- if (result.workItem.workKind === "appEvaluationScan") {
12330
- try {
12331
- return await finalizeAppEvaluationScanWork({
12332
- apiClient,
12333
- durationMs: Date.now() - startedAt,
12334
- projectId,
12335
- repositoryLinkId,
12336
- runnerId,
12337
- sessionContext,
12338
- toolConfig,
12339
- toolName: preview.toolName,
12340
- toolResult,
12341
- workItem: result.workItem
12342
- });
12343
- } catch (error) {
12344
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12448
+ if (result.workItem.workKind === "brainGeneration" || result.workItem.workKind === "planRevision") {
12449
+ try {
12450
+ return await finalizeBrainGenerationWork({
12451
+ apiClient,
12452
+ durationMs: Date.now() - startedAt,
12453
+ projectId,
12454
+ repositoryLinkId,
12455
+ runnerId,
12456
+ sessionContext,
12457
+ toolConfig,
12458
+ toolName: preview.toolName,
12459
+ toolResult,
12460
+ workItem: result.workItem
12461
+ });
12462
+ } catch (error) {
12463
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12464
+ }
12345
12465
  }
12346
- }
12347
- if (result.workItem.workKind === "brainConsolidationScan") {
12348
- try {
12349
- return await finalizeBrainConsolidationScanWork({
12350
- apiClient,
12351
- durationMs: Date.now() - startedAt,
12352
- projectId,
12353
- repositoryLinkId,
12354
- runnerId,
12355
- sessionContext,
12356
- toolConfig,
12357
- toolName: preview.toolName,
12358
- toolResult,
12359
- workItem: result.workItem
12360
- });
12361
- } catch (error) {
12362
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12466
+ if (result.workItem.workKind === "assistantQuestion") {
12467
+ try {
12468
+ return await finalizeAssistantQuestionWork({
12469
+ apiClient,
12470
+ durationMs: Date.now() - startedAt,
12471
+ projectId,
12472
+ repositoryLinkId,
12473
+ runnerId,
12474
+ sessionContext,
12475
+ toolConfig,
12476
+ toolName: preview.toolName,
12477
+ toolResult,
12478
+ workItem: result.workItem
12479
+ });
12480
+ } catch (error) {
12481
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12482
+ }
12363
12483
  }
12364
- }
12365
- if (result.workItem.workKind === "projectContextRefresh") {
12366
- try {
12367
- return await finalizeProjectContextRefreshWork({
12368
- apiClient,
12369
- durationMs: Date.now() - startedAt,
12370
- executionRoot,
12371
- projectId,
12372
- repositoryLinkId,
12373
- runnerId,
12374
- sessionContext,
12375
- toolConfig,
12376
- toolName: preview.toolName,
12377
- toolResult,
12378
- workItem: result.workItem
12379
- });
12380
- } catch (error) {
12381
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12484
+ if (result.workItem.workKind === "impactPreview") {
12485
+ try {
12486
+ return await finalizeImpactPreviewWork({
12487
+ apiClient,
12488
+ durationMs: Date.now() - startedAt,
12489
+ projectId,
12490
+ repositoryLinkId,
12491
+ root,
12492
+ runnerId,
12493
+ sessionContext,
12494
+ toolConfig,
12495
+ toolName: preview.toolName,
12496
+ toolResult,
12497
+ workItem: result.workItem
12498
+ });
12499
+ } catch (error) {
12500
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12501
+ }
12382
12502
  }
12383
- }
12384
- if (result.workItem.workKind === "implementationVerification") {
12385
- try {
12386
- return await finalizeImplementationVerificationWork({
12387
- apiClient,
12388
- durationMs: Date.now() - startedAt,
12389
- projectId,
12390
- repositoryLinkId,
12391
- runnerId,
12392
- sessionContext,
12393
- toolConfig,
12394
- toolName: preview.toolName,
12395
- toolResult,
12396
- workItem: result.workItem
12503
+ if (result.workItem.workKind === "issueDiagnosis") {
12504
+ try {
12505
+ return await finalizeIssueDiagnosisWork({
12506
+ apiClient,
12507
+ durationMs: Date.now() - startedAt,
12508
+ projectId,
12509
+ repositoryLinkId,
12510
+ runnerId,
12511
+ sessionContext,
12512
+ toolConfig,
12513
+ toolName: preview.toolName,
12514
+ toolResult,
12515
+ workItem: result.workItem
12516
+ });
12517
+ } catch (error) {
12518
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12519
+ }
12520
+ }
12521
+ if (result.workItem.workKind === "securityPostureScan") {
12522
+ try {
12523
+ return await finalizeSecurityPostureScanWork({
12524
+ apiClient,
12525
+ durationMs: Date.now() - startedAt,
12526
+ projectId,
12527
+ repositoryLinkId,
12528
+ runnerId,
12529
+ sessionContext,
12530
+ toolConfig,
12531
+ toolName: preview.toolName,
12532
+ toolResult,
12533
+ workItem: result.workItem
12534
+ });
12535
+ } catch (error) {
12536
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12537
+ }
12538
+ }
12539
+ if (result.workItem.workKind === "appEvaluationScan") {
12540
+ try {
12541
+ return await finalizeAppEvaluationScanWork({
12542
+ apiClient,
12543
+ durationMs: Date.now() - startedAt,
12544
+ projectId,
12545
+ repositoryLinkId,
12546
+ runnerId,
12547
+ sessionContext,
12548
+ toolConfig,
12549
+ toolName: preview.toolName,
12550
+ toolResult,
12551
+ workItem: result.workItem
12552
+ });
12553
+ } catch (error) {
12554
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12555
+ }
12556
+ }
12557
+ if (result.workItem.workKind === "brainConsolidationScan") {
12558
+ try {
12559
+ return await finalizeBrainConsolidationScanWork({
12560
+ apiClient,
12561
+ durationMs: Date.now() - startedAt,
12562
+ projectId,
12563
+ repositoryLinkId,
12564
+ runnerId,
12565
+ sessionContext,
12566
+ toolConfig,
12567
+ toolName: preview.toolName,
12568
+ toolResult,
12569
+ workItem: result.workItem
12570
+ });
12571
+ } catch (error) {
12572
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12573
+ }
12574
+ }
12575
+ if (result.workItem.workKind === "projectContextRefresh") {
12576
+ try {
12577
+ return await finalizeProjectContextRefreshWork({
12578
+ apiClient,
12579
+ durationMs: Date.now() - startedAt,
12580
+ executionRoot,
12581
+ projectId,
12582
+ repositoryLinkId,
12583
+ runnerId,
12584
+ sessionContext,
12585
+ toolConfig,
12586
+ toolName: preview.toolName,
12587
+ toolResult,
12588
+ workItem: result.workItem
12589
+ });
12590
+ } catch (error) {
12591
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12592
+ }
12593
+ }
12594
+ if (result.workItem.workKind === "implementationVerification") {
12595
+ try {
12596
+ return await finalizeImplementationVerificationWork({
12597
+ apiClient,
12598
+ durationMs: Date.now() - startedAt,
12599
+ projectId,
12600
+ repositoryLinkId,
12601
+ runnerId,
12602
+ sessionContext,
12603
+ toolConfig,
12604
+ toolName: preview.toolName,
12605
+ toolResult,
12606
+ workItem: result.workItem
12607
+ });
12608
+ } catch (error) {
12609
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12610
+ }
12611
+ }
12612
+ if (result.workItem.workKind === "testQualityScan") {
12613
+ try {
12614
+ return await finalizeTestQualityScanWork({
12615
+ apiClient,
12616
+ durationMs: Date.now() - startedAt,
12617
+ projectId,
12618
+ repositoryLinkId,
12619
+ runnerId,
12620
+ sessionContext,
12621
+ toolConfig,
12622
+ toolName: preview.toolName,
12623
+ toolResult,
12624
+ workItem: result.workItem
12625
+ });
12626
+ } catch (error) {
12627
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12628
+ }
12629
+ }
12630
+ if (result.workItem.workKind === "implementationTestGate") {
12631
+ try {
12632
+ return await finalizeImplementationTestGateWork({
12633
+ apiClient,
12634
+ durationMs: Date.now() - startedAt,
12635
+ projectId,
12636
+ repositoryLinkId,
12637
+ runnerId,
12638
+ sessionContext,
12639
+ toolConfig,
12640
+ toolName: preview.toolName,
12641
+ toolResult,
12642
+ workItem: result.workItem
12643
+ });
12644
+ } catch (error) {
12645
+ return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12646
+ }
12647
+ }
12648
+ let finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
12649
+ const durationMs = Date.now() - startedAt;
12650
+ const promptBatchResult = result.workItem.workKind === "promptBatch" ? parsePromptBatchResultBestEffort(toolResult) : void 0;
12651
+ const promptBatchFinalStatus = promptBatchResult ? workStatusFromPromptBatchResult(promptBatchResult) : void 0;
12652
+ if (promptBatchFinalStatus && (toolResult.exitCode === 0 || promptBatchFinalStatus !== "completed")) {
12653
+ finalStatus = promptBatchFinalStatus;
12654
+ }
12655
+ const failureExcerpt = finalStatus === "completed" ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
12656
+ let implementationHandoff;
12657
+ let finalMessage = `${preview.toolName} exited with code ${toolResult.exitCode}.`;
12658
+ let finalError = failureExcerpt ?? promptBatchFailureReason(promptBatchResult);
12659
+ const patchDrift = finalStatus !== "completed" ? classifyPatchDriftToolResult(toolResult) : void 0;
12660
+ if (patchDrift) {
12661
+ finalStatus = "blocked";
12662
+ finalMessage = patchDrift.message;
12663
+ finalError = patchDrift.summary;
12664
+ implementationHandoff = {
12665
+ status: "blocked",
12666
+ cleanupStatus: "pending",
12667
+ message: patchDrift.message,
12668
+ error: patchDrift.summary,
12669
+ recovery: {
12670
+ category: "patchDrift",
12671
+ availableActions: ["requeueFreshAttempt", "exportHandoffDetails"],
12672
+ conflictFiles: [],
12673
+ summary: "The local patch did not match the current worktree context. Refresh context or queue a fresh linked attempt before retrying.",
12674
+ ...isolationTelemetry.executionWorktreeKey ? { worktreeKey: isolationTelemetry.executionWorktreeKey } : {}
12675
+ }
12676
+ };
12677
+ }
12678
+ if (promptBatchResult && finalStatus !== "completed" && toolResult.exitCode === 0) {
12679
+ finalMessage = "Prompt batch reported a failed or blocked child prompt.";
12680
+ }
12681
+ if (finalStatus === "completed" && isImplementationHandoffWork(result.workItem)) {
12682
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12683
+ status: "running",
12684
+ summary: "Preparing GitHub PR handoff for implementation work.",
12685
+ idempotencyKey: `runner_milestone_handoff_started_${result.workItem.workItemId}_${result.workItem.attempt}`,
12686
+ metadata: { executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
12397
12687
  });
12398
- } catch (error) {
12399
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12688
+ const repositoryLink = await loadWorkItemRepositoryLink(apiClient, projectId, result.workItem.repositoryLinkId ?? repositoryLinkId);
12689
+ const approvedArtifacts = await apiClient.listBrainDocuments(projectId).then((response) => response.documents).catch((error) => {
12690
+ implementationHandoff = {
12691
+ status: "blocked",
12692
+ cleanupStatus: "pending",
12693
+ artifacts: { included: [], skipped: [], blocked: [] },
12694
+ message: "Implementation handoff is blocked because approved artifact metadata could not be loaded.",
12695
+ error: truncateLogExcerpt(errorDetail(error))
12696
+ };
12697
+ return void 0;
12698
+ });
12699
+ if (!implementationHandoff) {
12700
+ implementationHandoff = await completeImplementationHandoff({
12701
+ ...approvedArtifacts ? { approvedArtifacts } : {},
12702
+ primaryRepoRoot: root,
12703
+ ...repositoryLink ? { repositoryLink } : {},
12704
+ verificationSummary: "Local execution reported completion.",
12705
+ workItem: result.workItem,
12706
+ ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
12707
+ worktreePath: executionRoot
12708
+ });
12709
+ }
12710
+ finalStatus = workStatusFromImplementationHandoff(implementationHandoff);
12711
+ finalMessage = implementationHandoff.message ?? "Implementation handoff finished.";
12712
+ finalError = implementationHandoff.error;
12400
12713
  }
12401
- }
12402
- if (result.workItem.workKind === "testQualityScan") {
12714
+ const updatedToolSession = await finalizeToolSession({
12715
+ apiClient,
12716
+ projectId,
12717
+ status: toolResult.exitCode === 0 ? "completed" : "failed",
12718
+ runnerId,
12719
+ workItemId: result.workItem.workItemId,
12720
+ stdout: toolResult.stdout,
12721
+ ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
12722
+ ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
12723
+ ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
12724
+ ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
12725
+ ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
12726
+ });
12727
+ let statusResult;
12728
+ const finalizationIdempotencyKey = `run_${result.workItem.workItemId}_${randomUUID4()}`;
12729
+ const finalizationTelemetry = {
12730
+ tool: preview.toolName,
12731
+ ...toolResult.model ? { model: toolResult.model } : {},
12732
+ durationMs,
12733
+ message: finalMessage,
12734
+ ...isolationTelemetry,
12735
+ ...finalStatus === "blocked" ? { blockerReason: finalMessage } : {},
12736
+ sessionPolicy: sessionContext.policy,
12737
+ sessionDecision: sessionContext.decision,
12738
+ sessionDecisionReason: sessionContext.reason,
12739
+ ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
12740
+ ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {},
12741
+ ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
12742
+ ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
12743
+ ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {},
12744
+ ...implementationHandoff ? { implementationHandoff } : {},
12745
+ ...result.workItem.workKind === "promptBatch" ? { promptBatch: finalizePromptBatchMetadata(result.workItem, finalStatus, finalError, promptBatchResult) } : {},
12746
+ ...finalError ? { error: finalError } : {}
12747
+ };
12403
12748
  try {
12404
- return await finalizeTestQualityScanWork({
12405
- apiClient,
12406
- durationMs: Date.now() - startedAt,
12749
+ const implementationFinalization = isNonMutatingImplementationOutcome(result.workItem) ? await submitNonMutatingImplementationCompletion(apiClient, {
12750
+ attempt: result.workItem.attempt,
12751
+ finalStatus,
12752
+ idempotencyKey: finalizationIdempotencyKey,
12407
12753
  projectId,
12408
- repositoryLinkId,
12409
12754
  runnerId,
12410
- sessionContext,
12411
- toolConfig,
12412
- toolName: preview.toolName,
12413
- toolResult,
12414
- workItem: result.workItem
12415
- });
12416
- } catch (error) {
12417
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12418
- }
12419
- }
12420
- if (result.workItem.workKind === "implementationTestGate") {
12421
- try {
12422
- return await finalizeImplementationTestGateWork({
12423
- apiClient,
12424
- durationMs: Date.now() - startedAt,
12755
+ telemetry: finalizationTelemetry,
12756
+ workItemId: result.workItem.workItemId
12757
+ }) : await submitStagedImplementationFinalization(apiClient, {
12758
+ accountId: commandContext.accountId,
12759
+ attempt: result.workItem.attempt,
12760
+ finalStatus,
12761
+ idempotencyKey: finalizationIdempotencyKey,
12425
12762
  projectId,
12426
12763
  repositoryLinkId,
12427
12764
  runnerId,
12428
- sessionContext,
12429
- toolConfig,
12430
- toolName: preview.toolName,
12431
- toolResult,
12432
- workItem: result.workItem
12765
+ telemetry: implementationFinalizationTelemetry(finalizationTelemetry),
12766
+ workItemId: result.workItem.workItemId,
12767
+ workKind: result.workItem.workKind === "promptBatch" ? "promptBatch" : "implementation"
12433
12768
  });
12769
+ if (implementationFinalization.status === "failed") {
12770
+ if (implementationFinalization.retryable) {
12771
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)).catch(() => void 0);
12772
+ return { status: "blocked", exitCode: 0, message: implementationFinalization.message };
12773
+ }
12774
+ throw new Error(implementationFinalization.message);
12775
+ }
12776
+ if (implementationFinalization.status === "deferred") {
12777
+ const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
12778
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12779
+ status: "queued",
12780
+ summary: gateMessage,
12781
+ idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
12782
+ metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
12783
+ });
12784
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12785
+ console.log(gateMessage);
12786
+ return { status: "blocked", exitCode: 0, message: gateMessage };
12787
+ }
12788
+ statusResult = { workItem: implementationFinalization.workItem };
12434
12789
  } catch (error) {
12435
- return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
12436
- }
12437
- }
12438
- let finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
12439
- const durationMs = Date.now() - startedAt;
12440
- const promptBatchResult = result.workItem.workKind === "promptBatch" ? parsePromptBatchResultBestEffort(toolResult) : void 0;
12441
- const promptBatchFinalStatus = promptBatchResult ? workStatusFromPromptBatchResult(promptBatchResult) : void 0;
12442
- if (promptBatchFinalStatus && (toolResult.exitCode === 0 || promptBatchFinalStatus !== "completed")) {
12443
- finalStatus = promptBatchFinalStatus;
12444
- }
12445
- const failureExcerpt = finalStatus === "completed" ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
12446
- let implementationHandoff;
12447
- let finalMessage = `${preview.toolName} exited with code ${toolResult.exitCode}.`;
12448
- let finalError = failureExcerpt ?? promptBatchFailureReason(promptBatchResult);
12449
- const patchDrift = finalStatus !== "completed" ? classifyPatchDriftToolResult(toolResult) : void 0;
12450
- if (patchDrift) {
12451
- finalStatus = "blocked";
12452
- finalMessage = patchDrift.message;
12453
- finalError = patchDrift.summary;
12454
- implementationHandoff = {
12455
- status: "blocked",
12456
- cleanupStatus: "pending",
12457
- message: patchDrift.message,
12458
- error: patchDrift.summary,
12459
- recovery: {
12460
- category: "patchDrift",
12461
- availableActions: ["requeueFreshAttempt", "exportHandoffDetails"],
12462
- conflictFiles: [],
12463
- summary: "The local patch did not match the current worktree context. Refresh context or queue a fresh linked attempt before retrying.",
12464
- ...isolationTelemetry.executionWorktreeKey ? { worktreeKey: isolationTelemetry.executionWorktreeKey } : {}
12790
+ if (error instanceof AmistioApiError && error.status === 409 && error.detail.includes("implementation_test_gate_required")) {
12791
+ const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
12792
+ await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12793
+ status: "queued",
12794
+ summary: gateMessage,
12795
+ idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
12796
+ metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
12797
+ });
12798
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12799
+ console.log(gateMessage);
12800
+ return { status: "blocked", exitCode: 0 };
12465
12801
  }
12466
- };
12467
- }
12468
- if (promptBatchResult && finalStatus !== "completed" && toolResult.exitCode === 0) {
12469
- finalMessage = "Prompt batch reported a failed or blocked child prompt.";
12470
- }
12471
- if (finalStatus === "completed" && isImplementationHandoffWork(result.workItem)) {
12802
+ throw error;
12803
+ }
12472
12804
  await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12473
- status: "running",
12474
- summary: "Preparing GitHub PR handoff for implementation work.",
12475
- idempotencyKey: `runner_milestone_handoff_started_${result.workItem.workItemId}_${result.workItem.attempt}`,
12476
- metadata: { executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
12477
- });
12478
- const repositoryLink = await loadWorkItemRepositoryLink(apiClient, projectId, result.workItem.repositoryLinkId ?? repositoryLinkId);
12479
- const approvedArtifacts = await apiClient.listBrainDocuments(projectId).then((response) => response.documents).catch((error) => {
12480
- implementationHandoff = {
12481
- status: "blocked",
12482
- cleanupStatus: "pending",
12483
- artifacts: { included: [], skipped: [], blocked: [] },
12484
- message: "Implementation handoff is blocked because approved artifact metadata could not be loaded.",
12485
- error: truncateLogExcerpt(errorDetail(error))
12486
- };
12487
- return void 0;
12805
+ status: finalStatus,
12806
+ summary: finalMessage,
12807
+ idempotencyKey: `runner_milestone_finished_${result.workItem.workItemId}_${statusResult.workItem.idempotencyKey}`,
12808
+ metadata: {
12809
+ tool: preview.toolName,
12810
+ durationMs,
12811
+ exitCode: toolResult.exitCode,
12812
+ verificationSummary: finalStatus === "completed" ? "Local execution reported completion." : "Local execution reported failure.",
12813
+ executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "",
12814
+ executionBranch: isolationTelemetry.executionBranch ?? "",
12815
+ ...implementationHandoff?.status ? { handoffStatus: implementationHandoff.status } : {},
12816
+ ...implementationHandoff?.prUrl ? { prUrl: implementationHandoff.prUrl } : {},
12817
+ ...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {},
12818
+ ...implementationHandoff?.artifacts ? artifactHandoffMetadata(implementationHandoff.artifacts) : {}
12819
+ }
12488
12820
  });
12489
- if (!implementationHandoff) {
12490
- implementationHandoff = await completeImplementationHandoff({
12491
- ...approvedArtifacts ? { approvedArtifacts } : {},
12492
- primaryRepoRoot: root,
12493
- ...repositoryLink ? { repositoryLink } : {},
12494
- verificationSummary: "Local execution reported completion.",
12495
- workItem: result.workItem,
12496
- ...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
12497
- worktreePath: executionRoot
12498
- });
12499
- }
12500
- finalStatus = workStatusFromImplementationHandoff(implementationHandoff);
12501
- finalMessage = implementationHandoff.message ?? "Implementation handoff finished.";
12502
- finalError = implementationHandoff.error;
12821
+ await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12822
+ const durationSeconds = Math.round(durationMs / 1e3);
12823
+ console.log(`Marked ${statusResult.workItem.workItemId} ${statusResult.workItem.status} after ${durationSeconds}s.`);
12824
+ return { status: finalStatus, exitCode: finalStatus === "completed" ? toolResult.exitCode : 1 };
12825
+ } finally {
12826
+ await releaseLocalActiveClaim();
12503
12827
  }
12504
- const updatedToolSession = await finalizeToolSession({
12505
- apiClient,
12506
- projectId,
12507
- status: toolResult.exitCode === 0 ? "completed" : "failed",
12828
+ }
12829
+ function runnerActiveClaimInput({ accountId, claimLaneId, repositoryLinkId, runnerId, toolTimeoutMs, workItem }) {
12830
+ const identity = needsGitWorktreeIsolation(workItem) ? resolveWorktreeIdentity(workItem) : void 0;
12831
+ const implementationScopeId = identity?.implementationScopeId ?? workItem.implementationScopeId;
12832
+ const worktreeKey = identity?.worktreeKey ?? workItem.executionWorktreeKey;
12833
+ const executionBranch = identity?.branch ?? workItem.executionBranch;
12834
+ const expiresAtMs = Date.now() + Math.max(toolTimeoutMs, RUNNER_WORK_LEASE_SECONDS * 1e3) + RUNNER_WORK_LEASE_SECONDS * 1e3;
12835
+ return {
12836
+ accountId,
12837
+ projectId: workItem.projectId,
12838
+ repositoryLinkId: workItem.repositoryLinkId ?? repositoryLinkId,
12508
12839
  runnerId,
12509
- workItemId: result.workItem.workItemId,
12510
- stdout: toolResult.stdout,
12511
- ...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
12512
- ...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
12513
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
12514
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
12515
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
12516
- });
12517
- let statusResult;
12518
- const finalizationIdempotencyKey = `run_${result.workItem.workItemId}_${randomUUID3()}`;
12519
- const finalizationTelemetry = {
12520
- tool: preview.toolName,
12521
- ...toolResult.model ? { model: toolResult.model } : {},
12522
- durationMs,
12523
- message: finalMessage,
12524
- ...isolationTelemetry,
12525
- ...finalStatus === "blocked" ? { blockerReason: finalMessage } : {},
12526
- sessionPolicy: sessionContext.policy,
12527
- sessionDecision: sessionContext.decision,
12528
- sessionDecisionReason: sessionContext.reason,
12529
- ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
12530
- ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {},
12531
- ...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
12532
- ...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
12533
- ...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {},
12534
- ...implementationHandoff ? { implementationHandoff } : {},
12535
- ...result.workItem.workKind === "promptBatch" ? { promptBatch: finalizePromptBatchMetadata(result.workItem, finalStatus, finalError, promptBatchResult) } : {},
12536
- ...finalError ? { error: finalError } : {}
12840
+ claimLaneId,
12841
+ workItemId: workItem.workItemId,
12842
+ ...workItem.claimLeaseId ? { claimLeaseId: workItem.claimLeaseId } : {},
12843
+ attempt: workItem.attempt,
12844
+ ...implementationScopeId ? { implementationScopeId } : {},
12845
+ ...worktreeKey ? { worktreeKey } : {},
12846
+ ...executionBranch ? { executionBranch } : {},
12847
+ expiresAt: new Date(expiresAtMs).toISOString()
12537
12848
  };
12538
- try {
12539
- const implementationFinalization = isNonMutatingImplementationOutcome(result.workItem) ? await submitNonMutatingImplementationCompletion(apiClient, {
12540
- attempt: result.workItem.attempt,
12541
- finalStatus,
12542
- idempotencyKey: finalizationIdempotencyKey,
12543
- projectId,
12849
+ }
12850
+ async function releaseSkippedLocalClaim({ apiClient, claimLaneId, conflict, projectId, repositoryLinkId, runnerId, workItem }) {
12851
+ const message = activeClaimConflictMessage(conflict);
12852
+ await apiClient.updateWorkStatus(projectId, workItem.workItemId, "approved", `local_claim_conflict_${workItem.workItemId}_${workItem.attempt}_${randomUUID4()}`, runnerId, {
12853
+ ...workItemIsolationTelemetry(workItem, void 0),
12854
+ claimLaneId,
12855
+ message,
12856
+ releaseClaim: true
12857
+ }).catch(async (error) => {
12858
+ await apiClient.recordRunnerLog(projectId, {
12544
12859
  runnerId,
12545
- telemetry: finalizationTelemetry,
12546
- workItemId: result.workItem.workItemId
12547
- }) : await submitStagedImplementationFinalization(apiClient, {
12548
- accountId: commandContext.accountId,
12549
- attempt: result.workItem.attempt,
12550
- finalStatus,
12551
- idempotencyKey: finalizationIdempotencyKey,
12552
- projectId,
12553
12860
  repositoryLinkId,
12554
- runnerId,
12555
- telemetry: implementationFinalizationTelemetry(finalizationTelemetry),
12556
- workItemId: result.workItem.workItemId,
12557
- workKind: result.workItem.workKind === "promptBatch" ? "promptBatch" : "implementation"
12558
- });
12559
- if (implementationFinalization.status === "failed") {
12560
- if (implementationFinalization.retryable) {
12561
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)).catch(() => void 0);
12562
- return { status: "blocked", exitCode: 0, message: implementationFinalization.message };
12563
- }
12564
- throw new Error(implementationFinalization.message);
12565
- }
12566
- if (implementationFinalization.status === "deferred") {
12567
- const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
12568
- await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12569
- status: "queued",
12570
- summary: gateMessage,
12571
- idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
12572
- metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
12573
- });
12574
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12575
- console.log(gateMessage);
12576
- return { status: "blocked", exitCode: 0, message: gateMessage };
12577
- }
12578
- statusResult = { workItem: implementationFinalization.workItem };
12579
- } catch (error) {
12580
- if (error instanceof AmistioApiError && error.status === 409 && error.detail.includes("implementation_test_gate_required")) {
12581
- const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
12582
- await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12583
- status: "queued",
12584
- summary: gateMessage,
12585
- idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
12586
- metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
12587
- });
12588
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12589
- console.log(gateMessage);
12590
- return { status: "blocked", exitCode: 0 };
12591
- }
12592
- throw error;
12593
- }
12594
- await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
12595
- status: finalStatus,
12596
- summary: finalMessage,
12597
- idempotencyKey: `runner_milestone_finished_${result.workItem.workItemId}_${statusResult.workItem.idempotencyKey}`,
12598
- metadata: {
12599
- tool: preview.toolName,
12600
- durationMs,
12601
- exitCode: toolResult.exitCode,
12602
- verificationSummary: finalStatus === "completed" ? "Local execution reported completion." : "Local execution reported failure.",
12603
- executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "",
12604
- executionBranch: isolationTelemetry.executionBranch ?? "",
12605
- ...implementationHandoff?.status ? { handoffStatus: implementationHandoff.status } : {},
12606
- ...implementationHandoff?.prUrl ? { prUrl: implementationHandoff.prUrl } : {},
12607
- ...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {},
12608
- ...implementationHandoff?.artifacts ? artifactHandoffMetadata(implementationHandoff.artifacts) : {}
12609
- }
12861
+ status: "blocked",
12862
+ workItemId: workItem.workItemId,
12863
+ workTitle: workItem.title,
12864
+ ...workItem.workKind ? { workKind: workItem.workKind } : {},
12865
+ ...claimLaneId ? { claimLaneId } : {},
12866
+ message: "Runner skipped a locally conflicting active claim but could not release the server claim.",
12867
+ error: truncateLogExcerpt(errorDetail(error)),
12868
+ machineId: runnerMachineId()
12869
+ }).catch(() => void 0);
12610
12870
  });
12611
- await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
12612
- const durationSeconds = Math.round(durationMs / 1e3);
12613
- console.log(`Marked ${statusResult.workItem.workItemId} ${statusResult.workItem.status} after ${durationSeconds}s.`);
12614
- return { status: finalStatus, exitCode: finalStatus === "completed" ? toolResult.exitCode : 1 };
12871
+ await apiClient.recordRunnerLog(projectId, {
12872
+ runnerId,
12873
+ repositoryLinkId,
12874
+ status: "blocked",
12875
+ workItemId: workItem.workItemId,
12876
+ workTitle: workItem.title,
12877
+ ...workItem.workKind ? { workKind: workItem.workKind } : {},
12878
+ ...claimLaneId ? { claimLaneId } : {},
12879
+ message,
12880
+ machineId: runnerMachineId()
12881
+ }).catch(() => void 0);
12882
+ console.error(message);
12883
+ return { status: "idle", exitCode: 0, message };
12615
12884
  }
12616
12885
  function artifactHandoffMetadata(artifacts) {
12617
12886
  return {
@@ -12657,7 +12926,7 @@ async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency,
12657
12926
  const telemetry = workItemIsolationTelemetry(workItem, { ...identity, baseRevision: workItem.baseRevision ?? "unknown", worktreePath: "" });
12658
12927
  const finalAttempt = workItem.attempt >= maxPreflightAttempts;
12659
12928
  const statusMessage = finalAttempt ? `Git worktree preflight failed after ${workItem.attempt}/${maxPreflightAttempts} attempts. ${message}` : `Git worktree preflight attempt ${workItem.attempt}/${maxPreflightAttempts} failed. Requeueing for retry. ${message}`;
12660
- const statusResult = await apiClient.updateWorkStatus(projectId, workItem.workItemId, finalAttempt ? "failed" : "approved", `worktree_${finalAttempt ? "failed" : "retry"}_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`, runnerId, {
12929
+ const statusResult = await apiClient.updateWorkStatus(projectId, workItem.workItemId, finalAttempt ? "failed" : "approved", `worktree_${finalAttempt ? "failed" : "retry"}_${workItem.workItemId}_${workItem.attempt}_${randomUUID4()}`, runnerId, {
12661
12930
  ...telemetry,
12662
12931
  message: statusMessage,
12663
12932
  ...finalAttempt ? { blockerReason: message } : { releaseClaim: true },
@@ -12726,7 +12995,7 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
12726
12995
  const settlements = await Promise.allSettled([
12727
12996
  apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
12728
12997
  markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage7(error)),
12729
- apiClient.updateWorkStatus(projectId, workItem.workItemId, "failed", `finalize_failed_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`, runnerId, {
12998
+ apiClient.updateWorkStatus(projectId, workItem.workItemId, "failed", `finalize_failed_${workItem.workItemId}_${workItem.attempt}_${randomUUID4()}`, runnerId, {
12730
12999
  ...isolationTelemetry,
12731
13000
  tool: toolName,
12732
13001
  durationMs,
@@ -12963,7 +13232,7 @@ async function updateRunnerCommandStatus(apiClient, context, command, status, me
12963
13232
  runnerId: context.runnerId,
12964
13233
  repositoryLinkId: context.repositoryLinkId,
12965
13234
  status,
12966
- idempotencyKey: `runner_command_${command.commandId}_${status}_${randomUUID3()}`,
13235
+ idempotencyKey: `runner_command_${command.commandId}_${status}_${randomUUID4()}`,
12967
13236
  message,
12968
13237
  ...error ? { error } : {},
12969
13238
  ...providerAuthStatus ? { providerAuthStatus } : {}
@@ -13055,7 +13324,7 @@ async function findRecoveryWorkItem(apiClient, projectId, workItemId) {
13055
13324
  return workItems.find((item) => item.workItemId === workItemId);
13056
13325
  }
13057
13326
  async function submitRecoveredHandoff(apiClient, context, workItem, handoff, action) {
13058
- await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${randomUUID3()}`, context.runnerId, {
13327
+ await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${randomUUID4()}`, context.runnerId, {
13059
13328
  implementationHandoff: handoff,
13060
13329
  ...handoff.message ? { message: handoff.message } : {},
13061
13330
  ...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
@@ -13453,7 +13722,7 @@ ${toolResult.stderr}`);
13453
13722
  const resultMutation = {
13454
13723
  status: "completed",
13455
13724
  runnerId,
13456
- idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${randomUUID3()}`,
13725
+ idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${randomUUID4()}`,
13457
13726
  artifacts,
13458
13727
  tool: toolName,
13459
13728
  durationMs,
@@ -13528,7 +13797,7 @@ ${toolResult.stderr}`);
13528
13797
  const failedResult2 = await apiClient.submitBrainGenerationResult(projectId, workItem.workItemId, {
13529
13798
  status: "failed",
13530
13799
  runnerId,
13531
- idempotencyKey: `generation_${workItem.workItemId}_${randomUUID3()}`,
13800
+ idempotencyKey: `generation_${workItem.workItemId}_${randomUUID4()}`,
13532
13801
  tool: toolName,
13533
13802
  durationMs,
13534
13803
  ...failedSessionTelemetry,
@@ -13578,7 +13847,7 @@ ${toolResult.stderr}`);
13578
13847
  const resultMutation = {
13579
13848
  status: "completed",
13580
13849
  runnerId,
13581
- idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID3()}`,
13850
+ idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID4()}`,
13582
13851
  answer: answerResult.answer,
13583
13852
  sourceBoundary: answerResult.sourceBoundary,
13584
13853
  citations: answerResult.citations,
@@ -13614,7 +13883,7 @@ ${toolResult.stderr}`);
13614
13883
  const failedMutation = {
13615
13884
  status: "failed",
13616
13885
  runnerId,
13617
- idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID3()}`,
13886
+ idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID4()}`,
13618
13887
  tool: toolName,
13619
13888
  durationMs,
13620
13889
  ...sessionTelemetry,
@@ -13677,7 +13946,7 @@ ${toolResult.stderr}`);
13677
13946
  const resultMutation = {
13678
13947
  status: "completed",
13679
13948
  runnerId,
13680
- idempotencyKey: `impact_${workItem.workItemId}_${randomUUID3()}`,
13949
+ idempotencyKey: `impact_${workItem.workItemId}_${randomUUID4()}`,
13681
13950
  report: {
13682
13951
  ...report,
13683
13952
  analyzedRepoRevision: report.analyzedRepoRevision ?? metadata?.lastSyncedRevision
@@ -13714,7 +13983,7 @@ ${toolResult.stderr}`);
13714
13983
  const failedMutation = {
13715
13984
  status: "failed",
13716
13985
  runnerId,
13717
- idempotencyKey: `impact_${workItem.workItemId}_${randomUUID3()}`,
13986
+ idempotencyKey: `impact_${workItem.workItemId}_${randomUUID4()}`,
13718
13987
  tool: toolName,
13719
13988
  durationMs,
13720
13989
  ...sessionTelemetry,
@@ -13775,7 +14044,7 @@ ${toolResult.stderr}`);
13775
14044
  const resultMutation = {
13776
14045
  status: "completed",
13777
14046
  runnerId,
13778
- idempotencyKey: `issue_${workItem.workItemId}_${randomUUID3()}`,
14047
+ idempotencyKey: `issue_${workItem.workItemId}_${randomUUID4()}`,
13779
14048
  diagnosis,
13780
14049
  tool: toolName,
13781
14050
  durationMs,
@@ -13809,7 +14078,7 @@ ${toolResult.stderr}`);
13809
14078
  const failedMutation = {
13810
14079
  status: "failed",
13811
14080
  runnerId,
13812
- idempotencyKey: `issue_${workItem.workItemId}_${randomUUID3()}`,
14081
+ idempotencyKey: `issue_${workItem.workItemId}_${randomUUID4()}`,
13813
14082
  tool: toolName,
13814
14083
  durationMs,
13815
14084
  ...sessionTelemetry,
@@ -13870,7 +14139,7 @@ ${toolResult.stderr}`);
13870
14139
  const resultMutation = {
13871
14140
  status: "completed",
13872
14141
  runnerId,
13873
- idempotencyKey: `security_${workItem.workItemId}_${randomUUID3()}`,
14142
+ idempotencyKey: `security_${workItem.workItemId}_${randomUUID4()}`,
13874
14143
  result: scanResult,
13875
14144
  tool: toolName,
13876
14145
  durationMs,
@@ -13904,7 +14173,7 @@ ${toolResult.stderr}`);
13904
14173
  const failedMutation = {
13905
14174
  status: "failed",
13906
14175
  runnerId,
13907
- idempotencyKey: `security_${workItem.workItemId}_${randomUUID3()}`,
14176
+ idempotencyKey: `security_${workItem.workItemId}_${randomUUID4()}`,
13908
14177
  tool: toolName,
13909
14178
  durationMs,
13910
14179
  ...sessionTelemetry,
@@ -13965,7 +14234,7 @@ ${toolResult.stderr}`);
13965
14234
  const resultMutation = {
13966
14235
  status: "completed",
13967
14236
  runnerId,
13968
- idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID3()}`,
14237
+ idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID4()}`,
13969
14238
  result: scanResult,
13970
14239
  tool: toolName,
13971
14240
  durationMs,
@@ -13999,7 +14268,7 @@ ${toolResult.stderr}`);
13999
14268
  const failedMutation = {
14000
14269
  status: "failed",
14001
14270
  runnerId,
14002
- idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID3()}`,
14271
+ idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID4()}`,
14003
14272
  tool: toolName,
14004
14273
  durationMs,
14005
14274
  ...sessionTelemetry,
@@ -14060,7 +14329,7 @@ ${toolResult.stderr}`);
14060
14329
  const resultMutation = {
14061
14330
  status: "completed",
14062
14331
  runnerId,
14063
- idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID3()}`,
14332
+ idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID4()}`,
14064
14333
  result: scanResult,
14065
14334
  tool: toolName,
14066
14335
  durationMs,
@@ -14094,7 +14363,7 @@ ${toolResult.stderr}`);
14094
14363
  const failedMutation = {
14095
14364
  status: "failed",
14096
14365
  runnerId,
14097
- idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID3()}`,
14366
+ idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID4()}`,
14098
14367
  tool: toolName,
14099
14368
  durationMs,
14100
14369
  ...sessionTelemetry,
@@ -14156,7 +14425,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
14156
14425
  const resultMutation = {
14157
14426
  status: "completed",
14158
14427
  runnerId,
14159
- idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID3()}`,
14428
+ idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID4()}`,
14160
14429
  result: refreshResult,
14161
14430
  tool: toolName,
14162
14431
  durationMs,
@@ -14202,7 +14471,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
14202
14471
  const failedMutation = {
14203
14472
  status: "failed",
14204
14473
  runnerId,
14205
- idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID3()}`,
14474
+ idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID4()}`,
14206
14475
  tool: toolName,
14207
14476
  durationMs,
14208
14477
  ...sessionTelemetry,
@@ -14263,7 +14532,7 @@ ${toolResult.stderr}`);
14263
14532
  const resultMutation = {
14264
14533
  status: "completed",
14265
14534
  runnerId,
14266
- idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID3()}`,
14535
+ idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID4()}`,
14267
14536
  result: verificationResult,
14268
14537
  tool: toolName,
14269
14538
  durationMs,
@@ -14297,7 +14566,7 @@ ${toolResult.stderr}`);
14297
14566
  const failedMutation = {
14298
14567
  status: "failed",
14299
14568
  runnerId,
14300
- idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID3()}`,
14569
+ idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID4()}`,
14301
14570
  tool: toolName,
14302
14571
  durationMs,
14303
14572
  ...sessionTelemetry,
@@ -14358,7 +14627,7 @@ ${toolResult.stderr}`);
14358
14627
  const resultMutation = {
14359
14628
  status: "completed",
14360
14629
  runnerId,
14361
- idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID3()}`,
14630
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID4()}`,
14362
14631
  result: scanResult,
14363
14632
  tool: toolName,
14364
14633
  durationMs,
@@ -14392,7 +14661,7 @@ ${toolResult.stderr}`);
14392
14661
  const failedMutation = {
14393
14662
  status: "failed",
14394
14663
  runnerId,
14395
- idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID3()}`,
14664
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID4()}`,
14396
14665
  tool: toolName,
14397
14666
  durationMs,
14398
14667
  ...sessionTelemetry,
@@ -14453,7 +14722,7 @@ ${toolResult.stderr}`);
14453
14722
  const resultMutation = {
14454
14723
  status: "completed",
14455
14724
  runnerId,
14456
- idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID3()}`,
14725
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID4()}`,
14457
14726
  result: gateResult,
14458
14727
  tool: toolName,
14459
14728
  durationMs,
@@ -14487,7 +14756,7 @@ ${toolResult.stderr}`);
14487
14756
  const failedMutation = {
14488
14757
  status: "failed",
14489
14758
  runnerId,
14490
- idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID3()}`,
14759
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID4()}`,
14491
14760
  tool: toolName,
14492
14761
  durationMs,
14493
14762
  ...sessionTelemetry,
@@ -14724,7 +14993,7 @@ async function prepareToolSession({
14724
14993
  });
14725
14994
  return { ...selection, toolSession: toolSession2 };
14726
14995
  }
14727
- const toolSessionId = `tool_session_${randomUUID3()}`;
14996
+ const toolSessionId = `tool_session_${randomUUID4()}`;
14728
14997
  const { toolSession } = await apiClient.createToolSession(projectId, {
14729
14998
  toolSessionId,
14730
14999
  repositoryLinkId,
@@ -14883,7 +15152,7 @@ function runnerEnvironmentSetupCommands(setupPackageManagerInstall) {
14883
15152
  return setupPackageManagerInstall ? [{ id: "packageManager.install" }] : [];
14884
15153
  }
14885
15154
  function inferRepoName(root) {
14886
- return path19.basename(path19.resolve(root)) || "repository";
15155
+ return path20.basename(path20.resolve(root)) || "repository";
14887
15156
  }
14888
15157
  function createRepoFingerprint(accountId, projectId, repositoryLinkId) {
14889
15158
  return createHash9("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");
@@ -15161,7 +15430,7 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
15161
15430
  return {
15162
15431
  version: CLI_VERSION,
15163
15432
  mode,
15164
- hostname: os9.hostname(),
15433
+ hostname: os10.hostname(),
15165
15434
  ...runnerIsolationCapabilityMetadata(),
15166
15435
  maxConcurrentWork: concurrencyMetadata.maxConcurrentWork,
15167
15436
  activeClaimLaneIds: concurrencyMetadata.activeClaimLaneIds,
@@ -15186,9 +15455,9 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
15186
15455
  };
15187
15456
  }
15188
15457
  function runnerMachineId() {
15189
- return createHash9("sha256").update(`${os9.hostname()}:${os9.platform()}:${os9.arch()}`).digest("hex").slice(0, 20);
15458
+ return createHash9("sha256").update(`${os10.hostname()}:${os10.platform()}:${os10.arch()}`).digest("hex").slice(0, 20);
15190
15459
  }
15191
- async function delay(milliseconds) {
15460
+ async function delay2(milliseconds) {
15192
15461
  await new Promise((resolve) => setTimeout(resolve, milliseconds));
15193
15462
  }
15194
15463
  program.parseAsync().catch((error) => {