@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.
- package/README.md +1 -1
- package/dist/index.js +952 -683
- 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
|
|
5
|
-
import { writeFile as
|
|
6
|
-
import
|
|
7
|
-
import
|
|
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
|
|
3306
|
-
return new URL(`${base}${
|
|
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
|
|
6324
|
-
import
|
|
6325
|
-
import
|
|
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 ??
|
|
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 =
|
|
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:
|
|
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
|
|
6366
|
-
await
|
|
6367
|
-
await
|
|
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
|
|
6384
|
-
await
|
|
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
|
|
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
|
|
6400
|
-
await
|
|
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
|
|
6668
|
+
return path12.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
|
|
6484
6669
|
}
|
|
6485
|
-
return
|
|
6670
|
+
return path12.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
|
|
6486
6671
|
}
|
|
6487
6672
|
function runnerServiceMetadataPath(input, metadataDir) {
|
|
6488
|
-
return
|
|
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
|
|
6824
|
-
import
|
|
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
|
|
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
|
|
6971
|
-
await
|
|
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
|
|
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 =
|
|
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 =
|
|
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 (
|
|
7329
|
+
if (path13.isAbsolute(repoPath)) {
|
|
7145
7330
|
throw new Error(`Refusing to use absolute repo path: ${repoPath}`);
|
|
7146
7331
|
}
|
|
7147
|
-
const normalized =
|
|
7148
|
-
if (normalized === ".." || normalized.startsWith(`..${
|
|
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 =
|
|
7152
|
-
const fullPath =
|
|
7153
|
-
if (!fullPath.startsWith(`${root}${
|
|
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 =
|
|
7160
|
-
return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${
|
|
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
|
|
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 ||
|
|
7362
|
+
return htmlHeading || path13.basename(repoPath, path13.extname(repoPath));
|
|
7178
7363
|
}
|
|
7179
7364
|
async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options) {
|
|
7180
|
-
const root =
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
7280
|
-
const repoPath = normalizeRepoPath3(
|
|
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
|
|
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
|
|
7343
|
-
import
|
|
7344
|
-
import
|
|
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
|
|
7359
|
-
await
|
|
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
|
|
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
|
|
7556
|
+
return path14.join(os8.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
|
|
7372
7557
|
}
|
|
7373
7558
|
if (process.platform === "win32") {
|
|
7374
|
-
return
|
|
7559
|
+
return path14.join(process.env.APPDATA ?? os8.homedir(), "Amistio", "tool-sessions.json");
|
|
7375
7560
|
}
|
|
7376
|
-
return
|
|
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
|
|
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("\\\\") ||
|
|
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 =
|
|
8982
|
-
const relativePath = useWindowsPathRules ?
|
|
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
|
|
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: () =>
|
|
9073
|
-
freemem: () =>
|
|
9074
|
-
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
|
|
9188
|
-
import
|
|
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 =
|
|
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 ??
|
|
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 =
|
|
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 =
|
|
9247
|
-
const fileStat = await
|
|
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
|
|
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 =
|
|
9349
|
-
const repoPath = normalizeRepoPath4(
|
|
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 =
|
|
9418
|
-
const directory =
|
|
9419
|
-
const basename =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
9668
|
-
import
|
|
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
|
|
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 =
|
|
9906
|
+
const repoName = path18.basename(repoRoot);
|
|
9722
9907
|
const worktreeSlug = worktreeKey.split("/").filter(Boolean).pop() ?? "work";
|
|
9723
|
-
return
|
|
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 =
|
|
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 =
|
|
9807
|
-
const targetPath =
|
|
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(
|
|
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 ===
|
|
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
|
|
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
|
|
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 ||
|
|
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_${
|
|
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
|
|
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
|
|
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_${
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
12010
|
-
|
|
12011
|
-
|
|
12012
|
-
|
|
12013
|
-
|
|
12014
|
-
|
|
12015
|
-
|
|
12016
|
-
|
|
12017
|
-
|
|
12018
|
-
await
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12025
|
-
|
|
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
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
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
|
-
|
|
12056
|
-
|
|
12057
|
-
|
|
12058
|
-
|
|
12059
|
-
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
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
|
-
|
|
12066
|
-
|
|
12067
|
-
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12081
|
-
...
|
|
12082
|
-
|
|
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 (
|
|
12087
|
-
|
|
12088
|
-
|
|
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
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
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: "
|
|
12097
|
-
summary:
|
|
12098
|
-
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
|
-
|
|
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(
|
|
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
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12133
|
-
|
|
12134
|
-
|
|
12135
|
-
|
|
12136
|
-
|
|
12137
|
-
|
|
12138
|
-
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
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
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
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
|
-
|
|
12227
|
-
|
|
12228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12275
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
})
|
|
12343
|
-
|
|
12344
|
-
|
|
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
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
})
|
|
12361
|
-
|
|
12362
|
-
|
|
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
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
})
|
|
12380
|
-
|
|
12381
|
-
|
|
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
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
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
|
-
|
|
12399
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
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
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
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
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
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
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
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:
|
|
12474
|
-
summary:
|
|
12475
|
-
idempotencyKey: `
|
|
12476
|
-
metadata: {
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
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
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
|
|
12494
|
-
|
|
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
|
-
|
|
12505
|
-
|
|
12506
|
-
|
|
12507
|
-
|
|
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
|
-
|
|
12510
|
-
|
|
12511
|
-
...
|
|
12512
|
-
|
|
12513
|
-
...
|
|
12514
|
-
...
|
|
12515
|
-
...
|
|
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
|
-
|
|
12539
|
-
|
|
12540
|
-
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
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
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
workKind:
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
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.
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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}_${
|
|
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_${
|
|
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
|
|
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:
|
|
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(`${
|
|
15458
|
+
return createHash9("sha256").update(`${os10.hostname()}:${os10.platform()}:${os10.arch()}`).digest("hex").slice(0, 20);
|
|
15190
15459
|
}
|
|
15191
|
-
async function
|
|
15460
|
+
async function delay2(milliseconds) {
|
|
15192
15461
|
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
15193
15462
|
}
|
|
15194
15463
|
program.parseAsync().catch((error) => {
|