@ai-development-environment/control-agent 0.0.130 → 0.0.132
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/dist/control-agent.js +275 -72
- package/package.json +1 -1
package/dist/control-agent.js
CHANGED
|
@@ -2288,7 +2288,7 @@ var require_websocket = __commonJS({
|
|
|
2288
2288
|
var http = __require("http");
|
|
2289
2289
|
var net = __require("net");
|
|
2290
2290
|
var tls = __require("tls");
|
|
2291
|
-
var { randomBytes, createHash:
|
|
2291
|
+
var { randomBytes, createHash: createHash9 } = __require("crypto");
|
|
2292
2292
|
var { Duplex, Readable: Readable2 } = __require("stream");
|
|
2293
2293
|
var { URL: URL2 } = __require("url");
|
|
2294
2294
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2969,7 +2969,7 @@ var require_websocket = __commonJS({
|
|
|
2969
2969
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
2970
2970
|
return;
|
|
2971
2971
|
}
|
|
2972
|
-
const digest =
|
|
2972
|
+
const digest = createHash9("sha1").update(key + GUID).digest("base64");
|
|
2973
2973
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
2974
2974
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
2975
2975
|
return;
|
|
@@ -3338,7 +3338,7 @@ var require_websocket_server = __commonJS({
|
|
|
3338
3338
|
var EventEmitter = __require("events");
|
|
3339
3339
|
var http = __require("http");
|
|
3340
3340
|
var { Duplex } = __require("stream");
|
|
3341
|
-
var { createHash:
|
|
3341
|
+
var { createHash: createHash9 } = __require("crypto");
|
|
3342
3342
|
var extension2 = require_extension();
|
|
3343
3343
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
3344
3344
|
var subprotocol2 = require_subprotocol();
|
|
@@ -3645,7 +3645,7 @@ var require_websocket_server = __commonJS({
|
|
|
3645
3645
|
);
|
|
3646
3646
|
}
|
|
3647
3647
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
3648
|
-
const digest =
|
|
3648
|
+
const digest = createHash9("sha1").update(key + GUID).digest("base64");
|
|
3649
3649
|
const headers = [
|
|
3650
3650
|
"HTTP/1.1 101 Switching Protocols",
|
|
3651
3651
|
"Upgrade: websocket",
|
|
@@ -6438,6 +6438,42 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
6438
6438
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
6439
6439
|
var wrapper_default = import_websocket.default;
|
|
6440
6440
|
|
|
6441
|
+
// src/request-errors.ts
|
|
6442
|
+
var MAX_AGENT_REQUEST_ATTEMPTS = 5;
|
|
6443
|
+
var AgentRequestError = class extends Error {
|
|
6444
|
+
retryable;
|
|
6445
|
+
constructor(message2, status2, codes = []) {
|
|
6446
|
+
super(message2);
|
|
6447
|
+
this.name = "AgentRequestError";
|
|
6448
|
+
this.retryable = (status2 < 400 || status2 >= 500 || [408, 425, 429].includes(status2)) && !codes.some(
|
|
6449
|
+
(code) => [
|
|
6450
|
+
"UNAUTHENTICATED",
|
|
6451
|
+
"FORBIDDEN",
|
|
6452
|
+
"BAD_USER_INPUT",
|
|
6453
|
+
"GRAPHQL_VALIDATION_FAILED",
|
|
6454
|
+
"NOT_FOUND"
|
|
6455
|
+
].includes(code)
|
|
6456
|
+
) && !/^(?:Job|Run|Run attempt|Run command) not found(?: for this agent)?$/.test(
|
|
6457
|
+
message2
|
|
6458
|
+
);
|
|
6459
|
+
}
|
|
6460
|
+
};
|
|
6461
|
+
function isRetryableAgentError(error) {
|
|
6462
|
+
return !(error instanceof AgentRequestError) || error.retryable;
|
|
6463
|
+
}
|
|
6464
|
+
function waitForAgentRetry(delay3, signal2) {
|
|
6465
|
+
return new Promise((resolve11) => {
|
|
6466
|
+
const finish = () => {
|
|
6467
|
+
clearTimeout(timer);
|
|
6468
|
+
signal2.removeEventListener("abort", finish);
|
|
6469
|
+
resolve11();
|
|
6470
|
+
};
|
|
6471
|
+
const timer = setTimeout(finish, delay3);
|
|
6472
|
+
if (signal2.aborted) finish();
|
|
6473
|
+
else signal2.addEventListener("abort", finish, { once: true });
|
|
6474
|
+
});
|
|
6475
|
+
}
|
|
6476
|
+
|
|
6441
6477
|
// src/graphql-client.ts
|
|
6442
6478
|
async function writeArtifactTransferBytes(writer, bytes, position) {
|
|
6443
6479
|
let written = 0;
|
|
@@ -6532,8 +6568,9 @@ var AgentGraphQLClient = class {
|
|
|
6532
6568
|
}
|
|
6533
6569
|
if (!response.ok || typeof body?.mode !== "string") {
|
|
6534
6570
|
const detail = `HTTP ${response.status} ${response.statusText}`.trim();
|
|
6535
|
-
throw new
|
|
6536
|
-
!body && raw ? `${detail}: ${raw.slice(0, 500)}` : detail
|
|
6571
|
+
throw new AgentRequestError(
|
|
6572
|
+
!body && raw ? `${detail}: ${raw.slice(0, 500)}` : detail,
|
|
6573
|
+
response.status
|
|
6537
6574
|
);
|
|
6538
6575
|
}
|
|
6539
6576
|
return { mode: body.mode };
|
|
@@ -6557,8 +6594,12 @@ var AgentGraphQLClient = class {
|
|
|
6557
6594
|
}
|
|
6558
6595
|
if (!response.ok || body?.errors?.length || !body?.data) {
|
|
6559
6596
|
const detail = body?.errors?.map((error) => error.message).join("; ") || `HTTP ${response.status} ${response.statusText}`.trim();
|
|
6560
|
-
throw new
|
|
6561
|
-
!body && raw ? `${detail}: ${raw.slice(0, 500)}` : detail
|
|
6597
|
+
throw new AgentRequestError(
|
|
6598
|
+
!body && raw ? `${detail}: ${raw.slice(0, 500)}` : detail,
|
|
6599
|
+
response.status,
|
|
6600
|
+
body?.errors?.flatMap(
|
|
6601
|
+
({ extensions }) => extensions?.code ? [extensions.code] : []
|
|
6602
|
+
)
|
|
6562
6603
|
);
|
|
6563
6604
|
}
|
|
6564
6605
|
return body.data;
|
|
@@ -11105,7 +11146,7 @@ function tailscaleServeRemovePayload(value) {
|
|
|
11105
11146
|
// package.json
|
|
11106
11147
|
var package_default = {
|
|
11107
11148
|
name: "@ai-development-environment/control-agent",
|
|
11108
|
-
version: "0.0.
|
|
11149
|
+
version: "0.0.132",
|
|
11109
11150
|
description: "Control agent for the AI Development Environment",
|
|
11110
11151
|
license: "MIT",
|
|
11111
11152
|
repository: {
|
|
@@ -45688,19 +45729,50 @@ function physicalDestinations(value, includeUnavailable = false) {
|
|
|
45688
45729
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return [];
|
|
45689
45730
|
const id2 = nestedString(raw, [
|
|
45690
45731
|
["identifier"],
|
|
45732
|
+
["properties", "hardware", "udid"],
|
|
45691
45733
|
["hardwareProperties", "udid"],
|
|
45692
45734
|
["deviceProperties", "identifier"]
|
|
45693
45735
|
]);
|
|
45694
45736
|
const platform2 = nestedString(raw, [
|
|
45737
|
+
["properties", "hardware", "platform"],
|
|
45695
45738
|
["hardwareProperties", "platform"],
|
|
45696
45739
|
["deviceProperties", "osType"]
|
|
45697
45740
|
]);
|
|
45698
|
-
if (!id2 || platform2 && !platform2.
|
|
45699
|
-
|
|
45741
|
+
if (!id2 || platform2 && !["ios", "ipados"].includes(platform2.trim().toLowerCase()))
|
|
45742
|
+
return [];
|
|
45743
|
+
const reality = nestedString(raw, [
|
|
45744
|
+
["properties", "hardware", "reality"],
|
|
45745
|
+
["hardwareProperties", "reality"]
|
|
45746
|
+
])?.trim().toLowerCase();
|
|
45747
|
+
const transport = nestedString(raw, [
|
|
45748
|
+
["properties", "connection", "transportType"],
|
|
45749
|
+
["connectionProperties", "transportType"]
|
|
45750
|
+
])?.trim().toLowerCase();
|
|
45751
|
+
const visibility = nestedString(raw, [
|
|
45752
|
+
["visibilityClass"],
|
|
45753
|
+
["properties", "state", "visibilityClass"]
|
|
45754
|
+
])?.trim().toLowerCase();
|
|
45755
|
+
if (reality === "simulated" || !reality && (visibility === "simulators" || transport === "samemachine"))
|
|
45756
|
+
return [];
|
|
45757
|
+
const connectionState = nestedString(raw, [
|
|
45758
|
+
["properties", "connection", "state"],
|
|
45700
45759
|
["connectionProperties", "tunnelState"],
|
|
45701
45760
|
["deviceProperties", "connectionState"]
|
|
45702
|
-
]);
|
|
45703
|
-
const
|
|
45761
|
+
])?.trim().toLowerCase();
|
|
45762
|
+
const paired = nestedString(raw, [
|
|
45763
|
+
["properties", "connection", "pairingState"],
|
|
45764
|
+
["connectionProperties", "pairingState"]
|
|
45765
|
+
])?.trim().toLowerCase() === "paired";
|
|
45766
|
+
const connected = connectionState === "connected";
|
|
45767
|
+
const available = connected || paired && (transport === "wired" || transport === "localnetwork");
|
|
45768
|
+
const state = connected ? "Connected" : available ? "Available (paired)" : connectionState && [
|
|
45769
|
+
"disconnected",
|
|
45770
|
+
"unavailable",
|
|
45771
|
+
"offline",
|
|
45772
|
+
"notconnected",
|
|
45773
|
+
"not_connected",
|
|
45774
|
+
"not connected"
|
|
45775
|
+
].includes(connectionState) ? "Unavailable" : "Unknown";
|
|
45704
45776
|
if (!available && !includeUnavailable) {
|
|
45705
45777
|
return [];
|
|
45706
45778
|
}
|
|
@@ -45708,9 +45780,15 @@ function physicalDestinations(value, includeUnavailable = false) {
|
|
|
45708
45780
|
{
|
|
45709
45781
|
type: "PHYSICAL_DEVICE",
|
|
45710
45782
|
id: id2,
|
|
45711
|
-
name: nestedString(raw, [
|
|
45783
|
+
name: nestedString(raw, [
|
|
45784
|
+
["properties", "state", "name"],
|
|
45785
|
+
["deviceProperties", "name"],
|
|
45786
|
+
["name"]
|
|
45787
|
+
]) ?? id2,
|
|
45712
45788
|
platform: "iOS",
|
|
45713
45789
|
osVersion: nestedString(raw, [
|
|
45790
|
+
["properties", "software", "osVersionNumber", "stringValue"],
|
|
45791
|
+
["properties", "software", "osVersionNumber"],
|
|
45714
45792
|
["deviceProperties", "osVersionNumber"],
|
|
45715
45793
|
["deviceProperties", "osVersion"]
|
|
45716
45794
|
]),
|
|
@@ -45760,7 +45838,7 @@ var inspectBuildDestinations = async (payload, timeoutMs, signal2) => {
|
|
|
45760
45838
|
signal2,
|
|
45761
45839
|
folder
|
|
45762
45840
|
),
|
|
45763
|
-
listPhysicalDevices(timeoutMs, signal2)
|
|
45841
|
+
listPhysicalDevices(timeoutMs, signal2, true)
|
|
45764
45842
|
]);
|
|
45765
45843
|
return {
|
|
45766
45844
|
...successfulProcess3,
|
|
@@ -53986,6 +54064,7 @@ var JobExecutor = class {
|
|
|
53986
54064
|
repositoryCoordinator;
|
|
53987
54065
|
running = /* @__PURE__ */ new Map();
|
|
53988
54066
|
stopping = false;
|
|
54067
|
+
shutdown = new AbortController();
|
|
53989
54068
|
execute(job) {
|
|
53990
54069
|
if (this.stopping || this.running.has(job.id)) return;
|
|
53991
54070
|
const controller = new AbortController();
|
|
@@ -54001,6 +54080,7 @@ var JobExecutor = class {
|
|
|
54001
54080
|
}
|
|
54002
54081
|
async cancelAll() {
|
|
54003
54082
|
this.stopping = true;
|
|
54083
|
+
this.shutdown.abort();
|
|
54004
54084
|
closeAllWorktreeWatches();
|
|
54005
54085
|
const jobs = [...this.running.values()];
|
|
54006
54086
|
for (const { controller } of jobs) controller.abort();
|
|
@@ -54059,8 +54139,7 @@ var JobExecutor = class {
|
|
|
54059
54139
|
status2 = controller.signal.aborted ? "CANCELLED" : "FAILED";
|
|
54060
54140
|
console.error(`Job ${claimed.id} failed:`, completionError);
|
|
54061
54141
|
}
|
|
54062
|
-
let
|
|
54063
|
-
while (true) {
|
|
54142
|
+
for (let attempt = 1; ; attempt++) {
|
|
54064
54143
|
try {
|
|
54065
54144
|
await this.client.completeJob(
|
|
54066
54145
|
claimed.id,
|
|
@@ -54070,13 +54149,21 @@ var JobExecutor = class {
|
|
|
54070
54149
|
);
|
|
54071
54150
|
return;
|
|
54072
54151
|
} catch (error) {
|
|
54152
|
+
if (this.stopping) return;
|
|
54153
|
+
if (!isRetryableAgentError(error)) {
|
|
54154
|
+
console.error(
|
|
54155
|
+
`Could not report completion for ${claimed.id}; giving up after ${attempt} attempts:`,
|
|
54156
|
+
error instanceof Error ? error.message : error
|
|
54157
|
+
);
|
|
54158
|
+
return;
|
|
54159
|
+
}
|
|
54073
54160
|
console.error(
|
|
54074
54161
|
`Could not report completion for ${claimed.id}; retrying:`,
|
|
54075
54162
|
error instanceof Error ? error.message : error
|
|
54076
54163
|
);
|
|
54164
|
+
const delay3 = Math.min(3e4, 1e3 * 2 ** Math.min(attempt - 1, 5));
|
|
54165
|
+
await waitForAgentRetry(delay3, this.shutdown.signal);
|
|
54077
54166
|
if (this.stopping) return;
|
|
54078
|
-
const delay3 = Math.min(3e4, 1e3 * 2 ** Math.min(retry++, 5));
|
|
54079
|
-
await new Promise((resolve11) => setTimeout(resolve11, delay3));
|
|
54080
54167
|
}
|
|
54081
54168
|
}
|
|
54082
54169
|
}
|
|
@@ -54232,18 +54319,22 @@ var CodebaseMonitor = class {
|
|
|
54232
54319
|
|
|
54233
54320
|
// src/runs/run-manager.ts
|
|
54234
54321
|
import { execFile as execFile5 } from "node:child_process";
|
|
54322
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
54235
54323
|
import { mkdir as mkdir10, rm as rm20 } from "node:fs/promises";
|
|
54236
54324
|
import { dirname as dirname13, join as join23 } from "node:path";
|
|
54237
54325
|
import { promisify as promisify5 } from "node:util";
|
|
54238
54326
|
|
|
54239
54327
|
// src/runs/journal.ts
|
|
54240
54328
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
54329
|
+
import { createReadStream as createReadStream6 } from "node:fs";
|
|
54241
54330
|
import {
|
|
54242
54331
|
appendFile,
|
|
54243
54332
|
mkdir as mkdir9,
|
|
54244
54333
|
readFile as readFile12,
|
|
54245
54334
|
rename as rename7,
|
|
54246
54335
|
rm as rm19,
|
|
54336
|
+
stat as stat12,
|
|
54337
|
+
truncate,
|
|
54247
54338
|
writeFile as writeFile11
|
|
54248
54339
|
} from "node:fs/promises";
|
|
54249
54340
|
import { dirname as dirname12, join as join22 } from "node:path";
|
|
@@ -54256,6 +54347,7 @@ var RunJournal = class {
|
|
|
54256
54347
|
attemptId;
|
|
54257
54348
|
nextSequence = 0;
|
|
54258
54349
|
acknowledged = -1;
|
|
54350
|
+
acknowledgedOffset = 0;
|
|
54259
54351
|
loaded = false;
|
|
54260
54352
|
loading;
|
|
54261
54353
|
appending = Promise.resolve();
|
|
@@ -54288,14 +54380,33 @@ var RunJournal = class {
|
|
|
54288
54380
|
const state = JSON.parse(await readFile12(this.statePath, "utf8"));
|
|
54289
54381
|
this.acknowledged = state.acknowledged ?? -1;
|
|
54290
54382
|
this.nextSequence = state.nextSequence ?? 0;
|
|
54291
|
-
|
|
54292
|
-
|
|
54293
|
-
|
|
54294
|
-
|
|
54295
|
-
|
|
54296
|
-
|
|
54383
|
+
this.acknowledgedOffset = state.acknowledgedOffset ?? 0;
|
|
54384
|
+
} catch (error) {
|
|
54385
|
+
if (!(error instanceof SyntaxError) && error.code !== "ENOENT")
|
|
54386
|
+
throw error;
|
|
54387
|
+
}
|
|
54388
|
+
const size = await stat12(this.eventsPath).then(
|
|
54389
|
+
({ size: size2 }) => size2,
|
|
54390
|
+
(error) => {
|
|
54391
|
+
if (error.code === "ENOENT") return 0;
|
|
54392
|
+
throw error;
|
|
54297
54393
|
}
|
|
54394
|
+
);
|
|
54395
|
+
if (!Number.isSafeInteger(this.acknowledgedOffset) || this.acknowledgedOffset < 0 || this.acknowledgedOffset > size)
|
|
54396
|
+
this.acknowledgedOffset = 0;
|
|
54397
|
+
this.nextSequence = Math.max(this.nextSequence, this.acknowledged + 1);
|
|
54398
|
+
let completeOffset = this.acknowledgedOffset;
|
|
54399
|
+
for await (const { event, endOffset } of this.readEvents(completeOffset)) {
|
|
54400
|
+
this.nextSequence = Math.max(this.nextSequence, event.sequence + 1);
|
|
54401
|
+
if (event.sequence <= this.acknowledged) {
|
|
54402
|
+
this.acknowledgedOffset = endOffset;
|
|
54403
|
+
}
|
|
54404
|
+
completeOffset = endOffset;
|
|
54405
|
+
}
|
|
54406
|
+
if (size > completeOffset) {
|
|
54407
|
+
await truncate(this.eventsPath, completeOffset);
|
|
54298
54408
|
}
|
|
54409
|
+
await this.saveState();
|
|
54299
54410
|
this.loaded = true;
|
|
54300
54411
|
}
|
|
54301
54412
|
async append(event) {
|
|
@@ -54326,27 +54437,28 @@ var RunJournal = class {
|
|
|
54326
54437
|
return this.nextSequence - 1;
|
|
54327
54438
|
}
|
|
54328
54439
|
async flush(client) {
|
|
54329
|
-
|
|
54330
|
-
|
|
54331
|
-
|
|
54332
|
-
this.
|
|
54333
|
-
|
|
54334
|
-
|
|
54440
|
+
const previous = this.flushing;
|
|
54441
|
+
const operation = (async () => {
|
|
54442
|
+
await previous?.catch(() => void 0);
|
|
54443
|
+
await this.load();
|
|
54444
|
+
await this.appending;
|
|
54445
|
+
await this.flushInternal(client);
|
|
54446
|
+
})();
|
|
54447
|
+
this.flushing = operation;
|
|
54448
|
+
try {
|
|
54449
|
+
await operation;
|
|
54450
|
+
} finally {
|
|
54451
|
+
if (this.flushing === operation) this.flushing = void 0;
|
|
54452
|
+
}
|
|
54335
54453
|
}
|
|
54336
54454
|
async flushInternal(client) {
|
|
54337
|
-
|
|
54338
|
-
|
|
54339
|
-
)
|
|
54340
|
-
|
|
54341
|
-
const attemptId = pending[index].attemptId ?? this.attemptId;
|
|
54342
|
-
let end = index + 1;
|
|
54343
|
-
while (end < pending.length && end - index < 100 && (pending[end].attemptId ?? this.attemptId) === attemptId) {
|
|
54344
|
-
end += 1;
|
|
54345
|
-
}
|
|
54346
|
-
const chunk = pending.slice(index, end);
|
|
54455
|
+
let chunk = [];
|
|
54456
|
+
let endOffset = this.acknowledgedOffset;
|
|
54457
|
+
const upload = async () => {
|
|
54458
|
+
if (!chunk.length) return;
|
|
54347
54459
|
await client.appendRunEvents(
|
|
54348
54460
|
this.runId,
|
|
54349
|
-
attemptId,
|
|
54461
|
+
chunk[0].attemptId ?? this.attemptId,
|
|
54350
54462
|
chunk.map((value) => {
|
|
54351
54463
|
const event = { ...value };
|
|
54352
54464
|
Reflect.deleteProperty(event, "attemptId");
|
|
@@ -54354,20 +54466,49 @@ var RunJournal = class {
|
|
|
54354
54466
|
})
|
|
54355
54467
|
);
|
|
54356
54468
|
this.acknowledged = chunk.at(-1).sequence;
|
|
54469
|
+
this.acknowledgedOffset = endOffset;
|
|
54357
54470
|
await this.saveState();
|
|
54358
|
-
|
|
54359
|
-
}
|
|
54360
|
-
|
|
54361
|
-
|
|
54471
|
+
chunk = [];
|
|
54472
|
+
};
|
|
54473
|
+
for await (const entry of this.readEvents(this.acknowledgedOffset)) {
|
|
54474
|
+
if (chunk.length && (chunk.length === 100 || (chunk[0].attemptId ?? this.attemptId) !== (entry.event.attemptId ?? this.attemptId)))
|
|
54475
|
+
await upload();
|
|
54476
|
+
chunk.push(entry.event);
|
|
54477
|
+
endOffset = entry.endOffset;
|
|
54478
|
+
}
|
|
54479
|
+
await upload();
|
|
54480
|
+
}
|
|
54481
|
+
async *readEvents(start) {
|
|
54482
|
+
const stream = createReadStream6(this.eventsPath, { start });
|
|
54483
|
+
let pending = Buffer.alloc(0);
|
|
54484
|
+
let offset = start;
|
|
54362
54485
|
try {
|
|
54363
|
-
|
|
54486
|
+
for await (const bytes of stream) {
|
|
54487
|
+
const buffer = Buffer.concat([pending, bytes]);
|
|
54488
|
+
let index = 0;
|
|
54489
|
+
let newline;
|
|
54490
|
+
while ((newline = buffer.indexOf(10, index)) !== -1) {
|
|
54491
|
+
const line = buffer.subarray(index, newline);
|
|
54492
|
+
offset += newline - index + 1;
|
|
54493
|
+
index = newline + 1;
|
|
54494
|
+
if (line.length) {
|
|
54495
|
+
yield {
|
|
54496
|
+
event: JSON.parse(line.toString("utf8")),
|
|
54497
|
+
endOffset: offset
|
|
54498
|
+
};
|
|
54499
|
+
}
|
|
54500
|
+
}
|
|
54501
|
+
pending = buffer.subarray(index);
|
|
54502
|
+
}
|
|
54364
54503
|
} catch (error) {
|
|
54365
|
-
if (error.code === "ENOENT") return
|
|
54504
|
+
if (error.code === "ENOENT") return;
|
|
54366
54505
|
throw error;
|
|
54506
|
+
} finally {
|
|
54507
|
+
stream.destroy();
|
|
54367
54508
|
}
|
|
54368
54509
|
}
|
|
54369
54510
|
saveState() {
|
|
54370
|
-
const contents = `${JSON.stringify({ acknowledged: this.acknowledged, nextSequence: this.nextSequence })}
|
|
54511
|
+
const contents = `${JSON.stringify({ acknowledged: this.acknowledged, nextSequence: this.nextSequence, acknowledgedOffset: this.acknowledgedOffset })}
|
|
54371
54512
|
`;
|
|
54372
54513
|
const operation = this.saving.then(async () => {
|
|
54373
54514
|
const temporary = `${this.statePath}.${process.pid}.${randomUUID7()}.tmp`;
|
|
@@ -55548,11 +55689,14 @@ var RunManager = class {
|
|
|
55548
55689
|
runQueues = /* @__PURE__ */ new Map();
|
|
55549
55690
|
interruptionRequests = /* @__PURE__ */ new Map();
|
|
55550
55691
|
importTimer;
|
|
55692
|
+
importTask;
|
|
55693
|
+
importedSnapshots = /* @__PURE__ */ new Map();
|
|
55551
55694
|
commandTimer;
|
|
55552
55695
|
stopped = false;
|
|
55696
|
+
shutdown = new AbortController();
|
|
55553
55697
|
async start() {
|
|
55554
55698
|
if (typeof this.client.pendingRunCommands !== "function") return;
|
|
55555
|
-
|
|
55699
|
+
void this.syncImports();
|
|
55556
55700
|
try {
|
|
55557
55701
|
const commands = await this.client.pendingRunCommands();
|
|
55558
55702
|
await Promise.all(commands.map((command3) => this.execute(command3, true)));
|
|
@@ -55868,7 +56012,14 @@ var RunManager = class {
|
|
|
55868
56012
|
journal,
|
|
55869
56013
|
adapter,
|
|
55870
56014
|
baseline
|
|
55871
|
-
)
|
|
56015
|
+
).catch((error) => {
|
|
56016
|
+
if (!this.stopped) {
|
|
56017
|
+
console.error(
|
|
56018
|
+
`Run ${run.id} finalization failed:`,
|
|
56019
|
+
error instanceof Error ? error.message : error
|
|
56020
|
+
);
|
|
56021
|
+
}
|
|
56022
|
+
});
|
|
55872
56023
|
const requestedAfterStart = this.interruptionRequests.get(run.id);
|
|
55873
56024
|
if (requestedAfterStart)
|
|
55874
56025
|
await this.interruptActive(active, requestedAfterStart);
|
|
@@ -55890,7 +56041,7 @@ var RunManager = class {
|
|
|
55890
56041
|
baseline,
|
|
55891
56042
|
finalCheckpoint
|
|
55892
56043
|
);
|
|
55893
|
-
await this.
|
|
56044
|
+
await this.retryFinalization(
|
|
55894
56045
|
() => this.client.reportRunCheckpoint(run.id, attemptId, {
|
|
55895
56046
|
...finalCheckpoint,
|
|
55896
56047
|
diffPatch: comparison.rollbackPatch
|
|
@@ -55908,14 +56059,14 @@ var RunManager = class {
|
|
|
55908
56059
|
).catch(() => void 0);
|
|
55909
56060
|
}
|
|
55910
56061
|
}
|
|
55911
|
-
await this.
|
|
56062
|
+
await this.retryFinalization(
|
|
55912
56063
|
() => this.client.finishRunAttempt(attemptId, {
|
|
55913
56064
|
status: completion.status,
|
|
55914
56065
|
finalOutput: completion.finalOutput,
|
|
55915
56066
|
error: completion.error
|
|
55916
56067
|
})
|
|
55917
56068
|
);
|
|
55918
|
-
await this.
|
|
56069
|
+
await this.retryFinalization(
|
|
55919
56070
|
() => this.client.completeRunCommand(
|
|
55920
56071
|
command3.id,
|
|
55921
56072
|
completion.status === "FAILED" ? "FAILED" : "SUCCEEDED",
|
|
@@ -55923,13 +56074,14 @@ var RunManager = class {
|
|
|
55923
56074
|
)
|
|
55924
56075
|
);
|
|
55925
56076
|
} catch (error) {
|
|
55926
|
-
|
|
56077
|
+
if (this.stopped) return;
|
|
56078
|
+
await this.retryFinalization(
|
|
55927
56079
|
() => this.client.finishRunAttempt(attemptId, {
|
|
55928
56080
|
status: "FAILED",
|
|
55929
56081
|
error: error instanceof Error ? error.message : String(error)
|
|
55930
56082
|
})
|
|
55931
56083
|
);
|
|
55932
|
-
await this.
|
|
56084
|
+
await this.retryFinalization(
|
|
55933
56085
|
() => this.client.completeRunCommand(
|
|
55934
56086
|
command3.id,
|
|
55935
56087
|
"FAILED",
|
|
@@ -56212,10 +56364,22 @@ var RunManager = class {
|
|
|
56212
56364
|
}
|
|
56213
56365
|
return null;
|
|
56214
56366
|
}
|
|
56215
|
-
|
|
56367
|
+
syncImports() {
|
|
56368
|
+
if (this.stopped) return Promise.resolve();
|
|
56369
|
+
if (!this.importTask) {
|
|
56370
|
+
this.importTask = this.syncImportsInternal().finally(() => {
|
|
56371
|
+
this.importTask = void 0;
|
|
56372
|
+
});
|
|
56373
|
+
}
|
|
56374
|
+
return this.importTask;
|
|
56375
|
+
}
|
|
56376
|
+
async syncImportsInternal() {
|
|
56216
56377
|
if (this.stopped) return;
|
|
56378
|
+
if (this.importTimer) clearTimeout(this.importTimer);
|
|
56379
|
+
this.importTimer = void 0;
|
|
56217
56380
|
try {
|
|
56218
56381
|
const codebases = await this.client.agentCodebases();
|
|
56382
|
+
if (this.stopped) return;
|
|
56219
56383
|
const worktrees = codebases.flatMap(
|
|
56220
56384
|
(codebase) => codebase.worktrees.map((worktree) => ({
|
|
56221
56385
|
id: worktree.id,
|
|
@@ -56224,29 +56388,59 @@ var RunManager = class {
|
|
|
56224
56388
|
}))
|
|
56225
56389
|
);
|
|
56226
56390
|
for (const adapter of this.adapters.values()) {
|
|
56391
|
+
if (this.stopped) return;
|
|
56227
56392
|
try {
|
|
56228
56393
|
const catalog = await adapter.catalog?.(worktrees);
|
|
56394
|
+
if (this.stopped) return;
|
|
56229
56395
|
await this.client.reportRunProviderImportStatus(
|
|
56230
56396
|
adapter.key,
|
|
56231
56397
|
"SYNCING",
|
|
56232
56398
|
void 0,
|
|
56233
56399
|
{ ...catalog, capabilities: adapter.capabilities }
|
|
56234
56400
|
);
|
|
56401
|
+
if (this.stopped) return;
|
|
56235
56402
|
const runs = await adapter.discover(worktrees);
|
|
56403
|
+
if (this.stopped) return;
|
|
56236
56404
|
let batch = [];
|
|
56237
56405
|
let batchBytes = 0;
|
|
56406
|
+
let snapshots = [];
|
|
56407
|
+
const upload = async () => {
|
|
56408
|
+
await this.client.importProviderRuns(adapter.key, batch);
|
|
56409
|
+
for (const [key, digest] of snapshots) {
|
|
56410
|
+
this.importedSnapshots.set(key, digest);
|
|
56411
|
+
}
|
|
56412
|
+
batch = [];
|
|
56413
|
+
batchBytes = 0;
|
|
56414
|
+
snapshots = [];
|
|
56415
|
+
};
|
|
56238
56416
|
for (const run of runs) {
|
|
56239
|
-
|
|
56417
|
+
if (this.stopped) return;
|
|
56418
|
+
const serialized2 = JSON.stringify(run);
|
|
56419
|
+
const key = JSON.stringify([
|
|
56420
|
+
adapter.key,
|
|
56421
|
+
run.nativeId,
|
|
56422
|
+
run.worktreeId
|
|
56423
|
+
]);
|
|
56424
|
+
const digest = createHash8("sha256").update(serialized2).digest("hex");
|
|
56425
|
+
const active = [
|
|
56426
|
+
"ACTIVE",
|
|
56427
|
+
"RUNNING",
|
|
56428
|
+
"STARTING",
|
|
56429
|
+
"IN_PROGRESS",
|
|
56430
|
+
"WAITING_FOR_ANSWER"
|
|
56431
|
+
].includes(run.status?.toUpperCase() ?? "");
|
|
56432
|
+
if (!active && this.importedSnapshots.get(key) === digest) continue;
|
|
56433
|
+
const bytes = Buffer.byteLength(serialized2, "utf8");
|
|
56240
56434
|
if (batch.length && batchBytes + bytes > 4 * 1024 * 1024) {
|
|
56241
|
-
await
|
|
56242
|
-
batch = [];
|
|
56243
|
-
batchBytes = 0;
|
|
56435
|
+
await upload();
|
|
56244
56436
|
}
|
|
56245
56437
|
batch.push(run);
|
|
56246
56438
|
batchBytes += bytes;
|
|
56439
|
+
snapshots.push([key, digest]);
|
|
56247
56440
|
}
|
|
56248
|
-
await
|
|
56441
|
+
await upload();
|
|
56249
56442
|
} catch (error) {
|
|
56443
|
+
if (this.stopped) return;
|
|
56250
56444
|
await this.client.reportRunProviderImportStatus(
|
|
56251
56445
|
adapter.key,
|
|
56252
56446
|
"FAILED",
|
|
@@ -56259,10 +56453,12 @@ var RunManager = class {
|
|
|
56259
56453
|
}
|
|
56260
56454
|
}
|
|
56261
56455
|
} catch (error) {
|
|
56262
|
-
|
|
56263
|
-
|
|
56264
|
-
|
|
56265
|
-
|
|
56456
|
+
if (!this.stopped) {
|
|
56457
|
+
console.error(
|
|
56458
|
+
"Provider history sync failed:",
|
|
56459
|
+
error instanceof Error ? error.message : error
|
|
56460
|
+
);
|
|
56461
|
+
}
|
|
56266
56462
|
} finally {
|
|
56267
56463
|
if (!this.stopped) {
|
|
56268
56464
|
this.importTimer = setTimeout(
|
|
@@ -56273,14 +56469,19 @@ var RunManager = class {
|
|
|
56273
56469
|
}
|
|
56274
56470
|
}
|
|
56275
56471
|
}
|
|
56276
|
-
|
|
56472
|
+
retryFinalization(operation) {
|
|
56473
|
+
return this.retry(operation, Infinity);
|
|
56474
|
+
}
|
|
56475
|
+
async retry(operation, attempts = MAX_AGENT_REQUEST_ATTEMPTS) {
|
|
56277
56476
|
let delay3 = 1e3;
|
|
56278
56477
|
for (let attempt = 1; ; attempt += 1) {
|
|
56279
56478
|
try {
|
|
56280
56479
|
return await operation();
|
|
56281
56480
|
} catch (error) {
|
|
56282
|
-
if (this.stopped || attempt >= attempts
|
|
56283
|
-
|
|
56481
|
+
if (this.stopped || attempt >= attempts || !isRetryableAgentError(error))
|
|
56482
|
+
throw error;
|
|
56483
|
+
await waitForAgentRetry(delay3, this.shutdown.signal);
|
|
56484
|
+
if (this.stopped) throw error;
|
|
56284
56485
|
delay3 = Math.min(delay3 * 2, 3e4);
|
|
56285
56486
|
}
|
|
56286
56487
|
}
|
|
@@ -56293,14 +56494,16 @@ var RunManager = class {
|
|
|
56293
56494
|
}
|
|
56294
56495
|
async close() {
|
|
56295
56496
|
this.stopped = true;
|
|
56497
|
+
this.shutdown.abort();
|
|
56296
56498
|
if (this.importTimer) clearTimeout(this.importTimer);
|
|
56297
56499
|
if (this.commandTimer) clearTimeout(this.commandTimer);
|
|
56298
56500
|
for (const active of this.active.values()) {
|
|
56299
56501
|
await active.handle.interrupt("PAUSED").catch(() => void 0);
|
|
56300
56502
|
}
|
|
56301
|
-
await Promise.allSettled(
|
|
56302
|
-
this.
|
|
56303
|
-
|
|
56503
|
+
await Promise.allSettled([
|
|
56504
|
+
this.importTask,
|
|
56505
|
+
...this.adapters.values().map((adapter) => adapter.close?.())
|
|
56506
|
+
]);
|
|
56304
56507
|
}
|
|
56305
56508
|
};
|
|
56306
56509
|
|