@aiden-ade/sandbox-agent 0.1.51 → 0.1.52
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/index.cjs +411 -23
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
36
36
|
"use strict";
|
|
37
37
|
var fs = require("fs");
|
|
38
38
|
var Url = require("url");
|
|
39
|
-
var
|
|
39
|
+
var spawn7 = require("child_process").spawn;
|
|
40
40
|
module2.exports = XMLHttpRequest3;
|
|
41
41
|
XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
|
|
42
42
|
function XMLHttpRequest3(opts) {
|
|
@@ -332,7 +332,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
332
332
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
333
333
|
fs.writeFileSync(syncFile, "", "utf8");
|
|
334
334
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
335
|
-
var syncProc =
|
|
335
|
+
var syncProc = spawn7(process.argv[0], ["-e", execString]);
|
|
336
336
|
var statusText;
|
|
337
337
|
while (fs.existsSync(syncFile)) {
|
|
338
338
|
}
|
|
@@ -4811,7 +4811,7 @@ var require_websocket_server = __commonJS({
|
|
|
4811
4811
|
});
|
|
4812
4812
|
|
|
4813
4813
|
// src/index.ts
|
|
4814
|
-
var
|
|
4814
|
+
var import_node_child_process10 = require("child_process");
|
|
4815
4815
|
|
|
4816
4816
|
// src/cli-executable.ts
|
|
4817
4817
|
var import_node_child_process = require("child_process");
|
|
@@ -5204,7 +5204,7 @@ function resolveCommand(args, env) {
|
|
|
5204
5204
|
}
|
|
5205
5205
|
|
|
5206
5206
|
// src/daemon.ts
|
|
5207
|
-
var
|
|
5207
|
+
var import_node_child_process8 = require("child_process");
|
|
5208
5208
|
var import_node_crypto5 = require("crypto");
|
|
5209
5209
|
var import_node_fs14 = require("fs");
|
|
5210
5210
|
var import_node_http = __toESM(require("http"), 1);
|
|
@@ -5362,7 +5362,7 @@ Every \`start_task_session\` message must include all four of these:
|
|
|
5362
5362
|
3. Write requirements as a team doc (\`create_document\`) before any delegation. Create an artifact only when explicitly requested.
|
|
5363
5363
|
4. Break work into subtasks ordered by dependency. Delegate one at a time unless subtasks are truly independent.
|
|
5364
5364
|
5. Track results and update task status as work completes.`;
|
|
5365
|
-
var GENERAL_PROMPT =
|
|
5365
|
+
var GENERAL_PROMPT = "";
|
|
5366
5366
|
var SETUP_PROMPT = `## You are the Setup Agent
|
|
5367
5367
|
|
|
5368
5368
|
You are the **Setup Agent** for this cloud environment. When identifying yourself, say "I'm the Setup Agent (an Alan agent profile) for this environment."
|
|
@@ -10311,9 +10311,7 @@ Team
|
|
|
10311
10311
|
|
|
10312
10312
|
## Task Logging
|
|
10313
10313
|
|
|
10314
|
-
For non-trivial tasks, log high-signal events as they happen: decisions, changed assumptions, validation results, known-good IDs, blockers, risks, and direction changes. Keep one current task-state summary updated with current state, known-good IDs, open risks, and next action. Do not log routine commands or file reads. Before stopping, ensure the timeline and current state are enough for a human to understand progress and for another agent to resume. If a prior decision is stale, write a newer event that supersedes it
|
|
10315
|
-
|
|
10316
|
-
The specific work you do \u2014 implementation, planning, review, coordination, verification \u2014 is determined by your agent role, described next.`;
|
|
10314
|
+
For non-trivial tasks, log high-signal events as they happen: decisions, changed assumptions, validation results, known-good IDs, blockers, risks, and direction changes. Keep one current task-state summary updated with current state, known-good IDs, open risks, and next action. Do not log routine commands or file reads. Before stopping, ensure the timeline and current state are enough for a human to understand progress and for another agent to resume. If a prior decision is stale, write a newer event that supersedes it.`;
|
|
10317
10315
|
function buildAlanTeamScopePrompt(teamId) {
|
|
10318
10316
|
return [
|
|
10319
10317
|
`You are operating within team \`${teamId}\`.`,
|
|
@@ -10338,6 +10336,7 @@ Examples: "where is X configured", "which file handles Y", "how does Z work", "f
|
|
|
10338
10336
|
- Do NOT start with \`grep\`, \`rg\`, \`find\`, or broad Bash filesystem search for codebase discovery or implementation-answer questions when \`search_code_context\` is available.
|
|
10339
10337
|
- Do NOT repeat or rephrase index searches after the first result identifies useful paths. Continue locally instead.
|
|
10340
10338
|
- Do NOT claim current behavior from an index excerpt without reading the cited local file.
|
|
10339
|
+
- When several repositories are available and repoId is omitted, code context routes automatically using the task workspace, repository-name hints, and indexed evidence. Pass repoId only when the user explicitly names a repository or an existing trusted task selection supplies it. Never invent repo IDs or call list_teams.
|
|
10341
10340
|
|
|
10342
10341
|
### Local follow-ups
|
|
10343
10342
|
- Import/caller structure: use local \`code_graph_query\` or narrow symbol search after reading the cited files.
|
|
@@ -23395,7 +23394,18 @@ function verifiedRemote(repoRoot, params, includePushUrls) {
|
|
|
23395
23394
|
"The configured Git remote does not match the repository selected for migration"
|
|
23396
23395
|
);
|
|
23397
23396
|
}
|
|
23398
|
-
return { remote, expectedRemoteUrl };
|
|
23397
|
+
return { remote, expectedRemoteUrl, remoteUrl: fetchUrls[0] };
|
|
23398
|
+
}
|
|
23399
|
+
function inspectedRemoteUrl(repoRoot, params) {
|
|
23400
|
+
if (optionalString(params, "expectedRemoteUrl")) {
|
|
23401
|
+
return verifiedRemote(repoRoot, params, false).remoteUrl;
|
|
23402
|
+
}
|
|
23403
|
+
const remote = validatedRemote(params);
|
|
23404
|
+
const result = runGitResult(repoRoot, ["remote", "get-url", "--all", remote]);
|
|
23405
|
+
const urls = result.status === 0 ? result.stdout.split("\n").filter(Boolean) : [];
|
|
23406
|
+
if (urls.length === 0) return null;
|
|
23407
|
+
for (const url2 of urls) canonicalRemoteUrl(repoRoot, url2);
|
|
23408
|
+
return urls[0];
|
|
23399
23409
|
}
|
|
23400
23410
|
function isPathContained(childPath, parentPath) {
|
|
23401
23411
|
const rel = (0, import_node_path4.relative)((0, import_node_path4.resolve)(parentPath), (0, import_node_path4.resolve)(childPath));
|
|
@@ -23587,6 +23597,7 @@ function readStatus(repoRoot) {
|
|
|
23587
23597
|
}
|
|
23588
23598
|
return {
|
|
23589
23599
|
repoRoot,
|
|
23600
|
+
remoteUrl: null,
|
|
23590
23601
|
branch,
|
|
23591
23602
|
headSha,
|
|
23592
23603
|
dirty: porcelain.length > 0,
|
|
@@ -24084,7 +24095,11 @@ function verifyHead(params) {
|
|
|
24084
24095
|
return { repoRoot, headSha, verified: true };
|
|
24085
24096
|
}
|
|
24086
24097
|
function statusBindingSwitchGit(params) {
|
|
24087
|
-
|
|
24098
|
+
const repoRoot = resolveAuthorizedRepository(params);
|
|
24099
|
+
return {
|
|
24100
|
+
...readStatus(repoRoot),
|
|
24101
|
+
remoteUrl: inspectedRemoteUrl(repoRoot, params)
|
|
24102
|
+
};
|
|
24088
24103
|
}
|
|
24089
24104
|
async function executeBindingSwitchGitRpc(method, params) {
|
|
24090
24105
|
switch (method) {
|
|
@@ -26687,6 +26702,33 @@ function classifyTransportFailure(error) {
|
|
|
26687
26702
|
return "backend_unreachable";
|
|
26688
26703
|
}
|
|
26689
26704
|
|
|
26705
|
+
// src/observable-map.ts
|
|
26706
|
+
var ObservableMap = class extends Map {
|
|
26707
|
+
/**
|
|
26708
|
+
* Invoked after every membership change.
|
|
26709
|
+
*
|
|
26710
|
+
* Assigned after construction rather than passed to the constructor because
|
|
26711
|
+
* `Map`'s constructor may populate entries before subclass fields exist.
|
|
26712
|
+
*/
|
|
26713
|
+
onChange = null;
|
|
26714
|
+
set(key, value2) {
|
|
26715
|
+
const existed = super.has(key);
|
|
26716
|
+
const result = super.set(key, value2);
|
|
26717
|
+
if (!existed) this.onChange?.();
|
|
26718
|
+
return result;
|
|
26719
|
+
}
|
|
26720
|
+
delete(key) {
|
|
26721
|
+
const removed = super.delete(key);
|
|
26722
|
+
if (removed) this.onChange?.();
|
|
26723
|
+
return removed;
|
|
26724
|
+
}
|
|
26725
|
+
clear() {
|
|
26726
|
+
const had = this.size > 0;
|
|
26727
|
+
super.clear();
|
|
26728
|
+
if (had) this.onChange?.();
|
|
26729
|
+
}
|
|
26730
|
+
};
|
|
26731
|
+
|
|
26690
26732
|
// src/provider-auth-preflight.ts
|
|
26691
26733
|
var AUTH_PROBES = {
|
|
26692
26734
|
claude_cli: {
|
|
@@ -27313,18 +27355,331 @@ var RunStartGate = class {
|
|
|
27313
27355
|
}
|
|
27314
27356
|
};
|
|
27315
27357
|
|
|
27358
|
+
// src/sleep-inhibitor.ts
|
|
27359
|
+
var import_node_child_process5 = require("child_process");
|
|
27360
|
+
var SLEEP_PREVENTION_DISABLED_ENV = "ALAN_DISABLE_SLEEP_PREVENTION";
|
|
27361
|
+
var HEALTH_CHECK_INTERVAL_MS = 3e4;
|
|
27362
|
+
var noopLog = () => {
|
|
27363
|
+
};
|
|
27364
|
+
function isSleepPreventionDisabled(env = process.env) {
|
|
27365
|
+
const raw = env[SLEEP_PREVENTION_DISABLED_ENV];
|
|
27366
|
+
if (raw === void 0) return false;
|
|
27367
|
+
const normalized = raw.trim().toLowerCase();
|
|
27368
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
27369
|
+
}
|
|
27370
|
+
var ChildProcessBackend = class {
|
|
27371
|
+
child = null;
|
|
27372
|
+
/**
|
|
27373
|
+
* Set once a candidate command is proven absent (ENOENT). Prevents a spawn
|
|
27374
|
+
* storm from the health watchdog on machines that simply lack the binary.
|
|
27375
|
+
*/
|
|
27376
|
+
unavailable = false;
|
|
27377
|
+
log;
|
|
27378
|
+
constructor(log) {
|
|
27379
|
+
this.log = log;
|
|
27380
|
+
}
|
|
27381
|
+
/**
|
|
27382
|
+
* Let a multi-candidate backend react when its active helper terminates.
|
|
27383
|
+
* This is not called for an intentional release because `release()` clears
|
|
27384
|
+
* the active child before signalling it.
|
|
27385
|
+
*/
|
|
27386
|
+
onUnexpectedTermination() {
|
|
27387
|
+
}
|
|
27388
|
+
isHealthy() {
|
|
27389
|
+
if (this.unavailable) return true;
|
|
27390
|
+
const child = this.child;
|
|
27391
|
+
return child !== null && child.exitCode === null && child.signalCode === null;
|
|
27392
|
+
}
|
|
27393
|
+
acquire() {
|
|
27394
|
+
if (this.unavailable) return;
|
|
27395
|
+
if (this.isHealthy() && this.child !== null) return;
|
|
27396
|
+
this.child = null;
|
|
27397
|
+
const command = this.command();
|
|
27398
|
+
if (command === null) {
|
|
27399
|
+
this.unavailable = true;
|
|
27400
|
+
this.log(`sleep-inhibitor ${this.name} unavailable: no supported command`);
|
|
27401
|
+
return;
|
|
27402
|
+
}
|
|
27403
|
+
let child;
|
|
27404
|
+
try {
|
|
27405
|
+
child = (0, import_node_child_process5.spawn)(command.file, command.args, { stdio: "ignore", detached: false });
|
|
27406
|
+
} catch (error) {
|
|
27407
|
+
this.unavailable = true;
|
|
27408
|
+
this.log(`sleep-inhibitor ${this.name} spawn threw: ${describeError(error)}`);
|
|
27409
|
+
return;
|
|
27410
|
+
}
|
|
27411
|
+
child.on("error", (error) => {
|
|
27412
|
+
const wasActiveChild = this.child === child;
|
|
27413
|
+
if (wasActiveChild) this.child = null;
|
|
27414
|
+
if (error.code === "ENOENT") this.unavailable = true;
|
|
27415
|
+
this.log(`sleep-inhibitor ${this.name} failed: ${describeError(error)}`);
|
|
27416
|
+
if (wasActiveChild) this.onUnexpectedTermination();
|
|
27417
|
+
});
|
|
27418
|
+
child.on("exit", (code, signal) => {
|
|
27419
|
+
const wasActiveChild = this.child === child;
|
|
27420
|
+
if (wasActiveChild) this.child = null;
|
|
27421
|
+
this.log(`sleep-inhibitor ${this.name} exited code=${code} signal=${signal}`);
|
|
27422
|
+
if (wasActiveChild) this.onUnexpectedTermination();
|
|
27423
|
+
});
|
|
27424
|
+
this.child = child;
|
|
27425
|
+
this.log(`sleep-inhibitor ${this.name} acquired pid=${child.pid}`);
|
|
27426
|
+
}
|
|
27427
|
+
release() {
|
|
27428
|
+
const child = this.child;
|
|
27429
|
+
if (child === null) return;
|
|
27430
|
+
this.child = null;
|
|
27431
|
+
try {
|
|
27432
|
+
child.kill("SIGTERM");
|
|
27433
|
+
} catch (error) {
|
|
27434
|
+
this.log(`sleep-inhibitor ${this.name} release failed: ${describeError(error)}`);
|
|
27435
|
+
}
|
|
27436
|
+
}
|
|
27437
|
+
};
|
|
27438
|
+
var MacCaffeinateBackend = class extends ChildProcessBackend {
|
|
27439
|
+
name = "caffeinate";
|
|
27440
|
+
command() {
|
|
27441
|
+
return { file: "/usr/bin/caffeinate", args: ["-i", "-s", "-w", String(process.pid)] };
|
|
27442
|
+
}
|
|
27443
|
+
};
|
|
27444
|
+
var LinuxIdleInhibitBackend = class extends ChildProcessBackend {
|
|
27445
|
+
name = "systemd-inhibit";
|
|
27446
|
+
candidateIndex = 0;
|
|
27447
|
+
candidateCount = 2;
|
|
27448
|
+
command() {
|
|
27449
|
+
const guard = `while kill -0 ${process.pid} 2>/dev/null; do sleep 5; done`;
|
|
27450
|
+
const candidates = [
|
|
27451
|
+
{
|
|
27452
|
+
file: "systemd-inhibit",
|
|
27453
|
+
args: [
|
|
27454
|
+
"--what=idle:sleep",
|
|
27455
|
+
"--mode=block",
|
|
27456
|
+
"--who=alan-agent",
|
|
27457
|
+
"--why=Alan is running an active agent run",
|
|
27458
|
+
"--",
|
|
27459
|
+
"sh",
|
|
27460
|
+
"-c",
|
|
27461
|
+
guard
|
|
27462
|
+
]
|
|
27463
|
+
},
|
|
27464
|
+
{
|
|
27465
|
+
file: "gnome-session-inhibit",
|
|
27466
|
+
args: [
|
|
27467
|
+
"--inhibit",
|
|
27468
|
+
"idle",
|
|
27469
|
+
"--reason",
|
|
27470
|
+
"Alan is running an active agent run",
|
|
27471
|
+
"sh",
|
|
27472
|
+
"-c",
|
|
27473
|
+
guard
|
|
27474
|
+
]
|
|
27475
|
+
}
|
|
27476
|
+
];
|
|
27477
|
+
return candidates[this.candidateIndex] ?? null;
|
|
27478
|
+
}
|
|
27479
|
+
acquire() {
|
|
27480
|
+
const before = this.candidateIndex;
|
|
27481
|
+
super.acquire();
|
|
27482
|
+
if (this.unavailable && this.candidateIndex === before) {
|
|
27483
|
+
this.tryNextCandidate();
|
|
27484
|
+
}
|
|
27485
|
+
}
|
|
27486
|
+
onUnexpectedTermination() {
|
|
27487
|
+
this.tryNextCandidate();
|
|
27488
|
+
}
|
|
27489
|
+
tryNextCandidate() {
|
|
27490
|
+
this.candidateIndex += 1;
|
|
27491
|
+
if (this.candidateIndex >= this.candidateCount) return;
|
|
27492
|
+
this.unavailable = false;
|
|
27493
|
+
super.acquire();
|
|
27494
|
+
}
|
|
27495
|
+
};
|
|
27496
|
+
var WindowsPowerRequestBackend = class extends ChildProcessBackend {
|
|
27497
|
+
name = "windows-power-request";
|
|
27498
|
+
command() {
|
|
27499
|
+
const guard = `while (Get-Process -Id ${process.pid} -ErrorAction SilentlyContinue) { Start-Sleep -Seconds 5 }`;
|
|
27500
|
+
const script = [
|
|
27501
|
+
`Add-Type -Namespace Alan -Name Power -MemberDefinition '[DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint esFlags);';`,
|
|
27502
|
+
"[Alan.Power]::SetThreadExecutionState(0x80000001) | Out-Null;",
|
|
27503
|
+
guard
|
|
27504
|
+
].join(" ");
|
|
27505
|
+
return {
|
|
27506
|
+
file: "powershell.exe",
|
|
27507
|
+
args: ["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", script]
|
|
27508
|
+
};
|
|
27509
|
+
}
|
|
27510
|
+
};
|
|
27511
|
+
var externalBackends = /* @__PURE__ */ new Set();
|
|
27512
|
+
function createPlatformBackends(log, hasExternalBackend) {
|
|
27513
|
+
switch (process.platform) {
|
|
27514
|
+
case "darwin":
|
|
27515
|
+
return [new MacCaffeinateBackend(log)];
|
|
27516
|
+
case "linux":
|
|
27517
|
+
return [new LinuxIdleInhibitBackend(log)];
|
|
27518
|
+
case "win32":
|
|
27519
|
+
return hasExternalBackend ? [] : [new WindowsPowerRequestBackend(log)];
|
|
27520
|
+
default:
|
|
27521
|
+
return [];
|
|
27522
|
+
}
|
|
27523
|
+
}
|
|
27524
|
+
var SleepInhibitor = class {
|
|
27525
|
+
backends;
|
|
27526
|
+
log;
|
|
27527
|
+
enabled;
|
|
27528
|
+
active = false;
|
|
27529
|
+
healthTimer = null;
|
|
27530
|
+
exitHandler = null;
|
|
27531
|
+
disposed = false;
|
|
27532
|
+
constructor(options = {}) {
|
|
27533
|
+
this.log = options.log ?? noopLog;
|
|
27534
|
+
this.enabled = !isSleepPreventionDisabled(options.env ?? process.env);
|
|
27535
|
+
this.backends = this.enabled ? [...createPlatformBackends(this.log, externalBackends.size > 0), ...externalBackends] : [];
|
|
27536
|
+
if (!this.enabled) {
|
|
27537
|
+
this.log(`sleep-inhibitor disabled via ${SLEEP_PREVENTION_DISABLED_ENV}`);
|
|
27538
|
+
}
|
|
27539
|
+
}
|
|
27540
|
+
isActive() {
|
|
27541
|
+
return this.active;
|
|
27542
|
+
}
|
|
27543
|
+
/** Names of the backends this inhibitor will drive. Exposed for diagnostics. */
|
|
27544
|
+
backendNames() {
|
|
27545
|
+
return this.backends.map((backend) => backend.name);
|
|
27546
|
+
}
|
|
27547
|
+
/**
|
|
27548
|
+
* Turn sleep prevention on or off. Safe to call repeatedly with the same
|
|
27549
|
+
* value; only edges do work.
|
|
27550
|
+
*/
|
|
27551
|
+
setActive(active) {
|
|
27552
|
+
if (this.disposed) return;
|
|
27553
|
+
if (this.active === active) {
|
|
27554
|
+
if (active) this.healBackends();
|
|
27555
|
+
return;
|
|
27556
|
+
}
|
|
27557
|
+
this.active = active;
|
|
27558
|
+
if (active) this.start();
|
|
27559
|
+
else this.stop();
|
|
27560
|
+
}
|
|
27561
|
+
/** Release everything and detach timers/handlers. Idempotent. */
|
|
27562
|
+
dispose() {
|
|
27563
|
+
if (this.disposed) return;
|
|
27564
|
+
this.disposed = true;
|
|
27565
|
+
this.active = false;
|
|
27566
|
+
this.stop();
|
|
27567
|
+
}
|
|
27568
|
+
start() {
|
|
27569
|
+
if (this.backends.length === 0) return;
|
|
27570
|
+
this.healBackends();
|
|
27571
|
+
if (this.healthTimer === null) {
|
|
27572
|
+
this.healthTimer = setInterval(() => this.healBackends(), HEALTH_CHECK_INTERVAL_MS);
|
|
27573
|
+
this.healthTimer.unref?.();
|
|
27574
|
+
}
|
|
27575
|
+
if (this.exitHandler === null) {
|
|
27576
|
+
this.exitHandler = () => {
|
|
27577
|
+
for (const backend of this.backends) backend.release();
|
|
27578
|
+
};
|
|
27579
|
+
process.on("exit", this.exitHandler);
|
|
27580
|
+
}
|
|
27581
|
+
}
|
|
27582
|
+
stop() {
|
|
27583
|
+
if (this.healthTimer !== null) {
|
|
27584
|
+
clearInterval(this.healthTimer);
|
|
27585
|
+
this.healthTimer = null;
|
|
27586
|
+
}
|
|
27587
|
+
if (this.exitHandler !== null) {
|
|
27588
|
+
process.removeListener("exit", this.exitHandler);
|
|
27589
|
+
this.exitHandler = null;
|
|
27590
|
+
}
|
|
27591
|
+
for (const backend of this.backends) {
|
|
27592
|
+
try {
|
|
27593
|
+
backend.release();
|
|
27594
|
+
} catch (error) {
|
|
27595
|
+
this.log(`sleep-inhibitor release error: ${describeError(error)}`);
|
|
27596
|
+
}
|
|
27597
|
+
}
|
|
27598
|
+
}
|
|
27599
|
+
/**
|
|
27600
|
+
* Acquire anything not currently held. This is the self-healing step: a
|
|
27601
|
+
* backend whose helper died is respawned here.
|
|
27602
|
+
*
|
|
27603
|
+
* Healthy backends are skipped rather than re-acquired, so concurrent runs
|
|
27604
|
+
* can never stack assertions even if a backend's own `acquire()` is not
|
|
27605
|
+
* defensively idempotent.
|
|
27606
|
+
*/
|
|
27607
|
+
healBackends() {
|
|
27608
|
+
for (const backend of this.backends) {
|
|
27609
|
+
try {
|
|
27610
|
+
if (backend.isHealthy()) continue;
|
|
27611
|
+
backend.acquire();
|
|
27612
|
+
} catch (error) {
|
|
27613
|
+
this.log(`sleep-inhibitor ${backend.name} acquire error: ${describeError(error)}`);
|
|
27614
|
+
}
|
|
27615
|
+
}
|
|
27616
|
+
}
|
|
27617
|
+
};
|
|
27618
|
+
function describeError(error) {
|
|
27619
|
+
return error instanceof Error ? error.message : String(error);
|
|
27620
|
+
}
|
|
27621
|
+
|
|
27622
|
+
// src/suspension-detector.ts
|
|
27623
|
+
var DEFAULT_CHECK_INTERVAL_MS = 15e3;
|
|
27624
|
+
var DEFAULT_THRESHOLD_MS = 6e4;
|
|
27625
|
+
var SuspensionDetector = class {
|
|
27626
|
+
onSuspensionDetected;
|
|
27627
|
+
checkIntervalMs;
|
|
27628
|
+
thresholdMs;
|
|
27629
|
+
now;
|
|
27630
|
+
setIntervalFn;
|
|
27631
|
+
clearIntervalFn;
|
|
27632
|
+
timer = null;
|
|
27633
|
+
lastTickAtMs = null;
|
|
27634
|
+
constructor(options) {
|
|
27635
|
+
this.onSuspensionDetected = options.onSuspensionDetected;
|
|
27636
|
+
this.checkIntervalMs = options.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
|
|
27637
|
+
this.thresholdMs = options.thresholdMs ?? DEFAULT_THRESHOLD_MS;
|
|
27638
|
+
this.now = options.now ?? Date.now;
|
|
27639
|
+
this.setIntervalFn = options.setIntervalFn ?? setInterval;
|
|
27640
|
+
this.clearIntervalFn = options.clearIntervalFn ?? clearInterval;
|
|
27641
|
+
}
|
|
27642
|
+
start() {
|
|
27643
|
+
if (this.timer !== null) return;
|
|
27644
|
+
this.lastTickAtMs = this.now();
|
|
27645
|
+
this.timer = this.setIntervalFn(() => this.tick(), this.checkIntervalMs);
|
|
27646
|
+
this.timer.unref?.();
|
|
27647
|
+
}
|
|
27648
|
+
stop() {
|
|
27649
|
+
if (this.timer === null) return;
|
|
27650
|
+
this.clearIntervalFn(this.timer);
|
|
27651
|
+
this.timer = null;
|
|
27652
|
+
this.lastTickAtMs = null;
|
|
27653
|
+
}
|
|
27654
|
+
isRunning() {
|
|
27655
|
+
return this.timer !== null;
|
|
27656
|
+
}
|
|
27657
|
+
tick() {
|
|
27658
|
+
const nowMs = this.now();
|
|
27659
|
+
const previousMs = this.lastTickAtMs;
|
|
27660
|
+
this.lastTickAtMs = nowMs;
|
|
27661
|
+
if (previousMs === null) return;
|
|
27662
|
+
const overshootMs = nowMs - previousMs - this.checkIntervalMs;
|
|
27663
|
+
if (overshootMs < this.thresholdMs) return;
|
|
27664
|
+
try {
|
|
27665
|
+
this.onSuspensionDetected(overshootMs);
|
|
27666
|
+
} catch {
|
|
27667
|
+
}
|
|
27668
|
+
}
|
|
27669
|
+
};
|
|
27670
|
+
|
|
27316
27671
|
// src/version.ts
|
|
27317
|
-
var AGENT_VERSION = "0.1.
|
|
27672
|
+
var AGENT_VERSION = "0.1.52";
|
|
27318
27673
|
|
|
27319
27674
|
// src/workspace-relocation.ts
|
|
27320
|
-
var
|
|
27675
|
+
var import_node_child_process6 = require("child_process");
|
|
27321
27676
|
var import_node_fs12 = require("fs");
|
|
27322
27677
|
var import_node_path10 = require("path");
|
|
27323
27678
|
var MAX_RELOCATION_CANDIDATES = 200;
|
|
27324
27679
|
var DEFAULT_PROBE = {
|
|
27325
27680
|
childDirectories: (parentPath, limit) => (0, import_node_fs12.readdirSync)(parentPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).slice(0, limit).map((entry) => (0, import_node_path10.join)(parentPath, entry.name)),
|
|
27326
27681
|
gitRemoteUrl: (candidatePath) => {
|
|
27327
|
-
const result = (0,
|
|
27682
|
+
const result = (0, import_node_child_process6.spawnSync)("git", ["-C", candidatePath, "config", "--get", "remote.origin.url"], {
|
|
27328
27683
|
encoding: "utf8",
|
|
27329
27684
|
env: getDaemonCliEnvironment(),
|
|
27330
27685
|
timeout: 2e3
|
|
@@ -27386,7 +27741,7 @@ function discoverRelocatedWorkspace(input) {
|
|
|
27386
27741
|
}
|
|
27387
27742
|
|
|
27388
27743
|
// src/workspace-run-lease.ts
|
|
27389
|
-
var
|
|
27744
|
+
var import_node_child_process7 = require("child_process");
|
|
27390
27745
|
var import_node_fs13 = require("fs");
|
|
27391
27746
|
var import_node_path11 = require("path");
|
|
27392
27747
|
var WORKSPACE_LEASE_CHECK_INTERVAL_MS = 1e3;
|
|
@@ -27399,7 +27754,7 @@ var WORKSPACE_LEASE_GIT_TIMEOUT_MS = parsePositiveMs(
|
|
|
27399
27754
|
2e3
|
|
27400
27755
|
);
|
|
27401
27756
|
function gitValue(cwd, args) {
|
|
27402
|
-
const result = (0,
|
|
27757
|
+
const result = (0, import_node_child_process7.spawnSync)("git", ["-C", cwd, ...args], {
|
|
27403
27758
|
encoding: "utf8",
|
|
27404
27759
|
env: getDaemonCliEnvironment(),
|
|
27405
27760
|
timeout: WORKSPACE_LEASE_GIT_TIMEOUT_MS
|
|
@@ -27653,7 +28008,7 @@ function assertValidGitRef(ref, label) {
|
|
|
27653
28008
|
if (!ref || ref.startsWith("-") || ref.includes("\0")) {
|
|
27654
28009
|
throw new Error(`${label} is not a safe git ref`);
|
|
27655
28010
|
}
|
|
27656
|
-
const result = (0,
|
|
28011
|
+
const result = (0, import_node_child_process8.spawnSync)("git", ["check-ref-format", "--branch", ref], {
|
|
27657
28012
|
env: getDaemonCliEnvironment(),
|
|
27658
28013
|
encoding: "utf8"
|
|
27659
28014
|
});
|
|
@@ -27669,7 +28024,7 @@ function assertPathContained(childPath, parentPath, label) {
|
|
|
27669
28024
|
}
|
|
27670
28025
|
}
|
|
27671
28026
|
function git(cwd, args) {
|
|
27672
|
-
const result = (0,
|
|
28027
|
+
const result = (0, import_node_child_process8.spawnSync)("git", args, {
|
|
27673
28028
|
cwd,
|
|
27674
28029
|
env: getDaemonCliEnvironment(),
|
|
27675
28030
|
encoding: "utf8",
|
|
@@ -28627,10 +28982,13 @@ async function acquireLocalDaemonOwnerLease(input) {
|
|
|
28627
28982
|
}
|
|
28628
28983
|
return { acquired: false, lease: existing };
|
|
28629
28984
|
}
|
|
28630
|
-
if (endpoint.state === "listening_unauthorized"
|
|
28985
|
+
if (endpoint.state === "listening_unauthorized") {
|
|
28986
|
+
return { acquired: false, lease: existing };
|
|
28987
|
+
}
|
|
28988
|
+
const journal = inspectLocalDaemonRunJournal(input.configPath);
|
|
28989
|
+
if (isProviderProcessAlive(existing.pid) && !journal.safeToRestart) {
|
|
28631
28990
|
return { acquired: false, lease: existing };
|
|
28632
28991
|
}
|
|
28633
|
-
inspectLocalDaemonRunJournal(input.configPath);
|
|
28634
28992
|
releaseOwnedJsonFile(daemonOwnerLeasePath(input.configPath), existing.id);
|
|
28635
28993
|
}
|
|
28636
28994
|
if (!existing && input.storedLocalApiPort && input.storedLocalApiToken) {
|
|
@@ -29998,7 +30356,7 @@ async function startDaemon(args) {
|
|
|
29998
30356
|
ownerLeaseHeld = false;
|
|
29999
30357
|
releaseLocalDaemonOwnerLease(configPath, ownerLease.id);
|
|
30000
30358
|
};
|
|
30001
|
-
const activeAgents =
|
|
30359
|
+
const activeAgents = new ObservableMap();
|
|
30002
30360
|
const pendingRunStarts = new DurablePendingRunStarts(
|
|
30003
30361
|
`${configPath}.pending-runs.json`,
|
|
30004
30362
|
daemonSessionId
|
|
@@ -30019,6 +30377,13 @@ async function startDaemon(args) {
|
|
|
30019
30377
|
recentLogs.push(`${(/* @__PURE__ */ new Date()).toISOString()} ${message}`);
|
|
30020
30378
|
if (recentLogs.length > 200) recentLogs.splice(0, recentLogs.length - 200);
|
|
30021
30379
|
};
|
|
30380
|
+
const sleepInhibitor = new SleepInhibitor({ log: pushLog });
|
|
30381
|
+
activeAgents.onChange = () => {
|
|
30382
|
+
sleepInhibitor.setActive(activeAgents.size > 0);
|
|
30383
|
+
};
|
|
30384
|
+
if (sleepInhibitor.backendNames().length > 0) {
|
|
30385
|
+
pushLog(`sleep-inhibitor backends=${sleepInhibitor.backendNames().join(",")}`);
|
|
30386
|
+
}
|
|
30022
30387
|
const alanMcpRegisteredRuns = /* @__PURE__ */ new Set();
|
|
30023
30388
|
const alanMcpRegisteredBackends = /* @__PURE__ */ new Set();
|
|
30024
30389
|
const finalizeAlanMcpForRun = (runId) => {
|
|
@@ -30247,6 +30612,22 @@ async function startDaemon(args) {
|
|
|
30247
30612
|
emitRunnerPresence();
|
|
30248
30613
|
flushWorkspaceRelocations();
|
|
30249
30614
|
}, 15e3);
|
|
30615
|
+
let pendingWakeGapMs = null;
|
|
30616
|
+
const flushWakeGap = () => {
|
|
30617
|
+
if (pendingWakeGapMs === null || !socket.connected) return;
|
|
30618
|
+
const gapMs = pendingWakeGapMs;
|
|
30619
|
+
pendingWakeGapMs = null;
|
|
30620
|
+
socket.emit("runtime.wake", { gapMs });
|
|
30621
|
+
pushLog(`runtime-wake reported gapMs=${gapMs}`);
|
|
30622
|
+
};
|
|
30623
|
+
const suspensionDetector = new SuspensionDetector({
|
|
30624
|
+
onSuspensionDetected: (gapMs) => {
|
|
30625
|
+
pendingWakeGapMs = Math.max(pendingWakeGapMs ?? 0, gapMs);
|
|
30626
|
+
pushLog(`host-suspension detected gapMs=${gapMs}`);
|
|
30627
|
+
flushWakeGap();
|
|
30628
|
+
}
|
|
30629
|
+
});
|
|
30630
|
+
suspensionDetector.start();
|
|
30250
30631
|
void refreshProviderVersions();
|
|
30251
30632
|
const versionRefresh = setInterval(() => {
|
|
30252
30633
|
void refreshProviderVersions();
|
|
@@ -30338,6 +30719,7 @@ async function startDaemon(args) {
|
|
|
30338
30719
|
runnerStatus: runnerSummary.runnerStatus
|
|
30339
30720
|
}
|
|
30340
30721
|
});
|
|
30722
|
+
flushWakeGap();
|
|
30341
30723
|
scheduleRuntimeModelScan(() => Promise.resolve(metadata));
|
|
30342
30724
|
});
|
|
30343
30725
|
});
|
|
@@ -31213,6 +31595,7 @@ async function startDaemon(args) {
|
|
|
31213
31595
|
clearInterval(heartbeat);
|
|
31214
31596
|
clearInterval(versionRefresh);
|
|
31215
31597
|
clearInterval(workspaceLeaseMonitor);
|
|
31598
|
+
suspensionDetector.stop();
|
|
31216
31599
|
socket.disconnect();
|
|
31217
31600
|
releaseOwnerLease();
|
|
31218
31601
|
releaseStartupLease();
|
|
@@ -31224,6 +31607,7 @@ async function startDaemon(args) {
|
|
|
31224
31607
|
clearInterval(heartbeat);
|
|
31225
31608
|
clearInterval(versionRefresh);
|
|
31226
31609
|
clearInterval(workspaceLeaseMonitor);
|
|
31610
|
+
suspensionDetector.stop();
|
|
31227
31611
|
socket.disconnect();
|
|
31228
31612
|
releaseOwnerLease();
|
|
31229
31613
|
releaseStartupLease();
|
|
@@ -31236,6 +31620,7 @@ async function startDaemon(args) {
|
|
|
31236
31620
|
clearInterval(heartbeat);
|
|
31237
31621
|
clearInterval(versionRefresh);
|
|
31238
31622
|
clearInterval(workspaceLeaseMonitor);
|
|
31623
|
+
suspensionDetector.stop();
|
|
31239
31624
|
socket.disconnect();
|
|
31240
31625
|
if (error.code === "EADDRINUSE") {
|
|
31241
31626
|
void probeLocalDaemonEndpoint(localApiPort).then((probe) => {
|
|
@@ -31278,6 +31663,7 @@ async function startDaemon(args) {
|
|
|
31278
31663
|
clearInterval(heartbeat);
|
|
31279
31664
|
clearInterval(versionRefresh);
|
|
31280
31665
|
clearInterval(workspaceLeaseMonitor);
|
|
31666
|
+
suspensionDetector.stop();
|
|
31281
31667
|
socket.disconnect();
|
|
31282
31668
|
await new Promise((resolve7) => localServer.close(() => resolve7()));
|
|
31283
31669
|
releaseOwnerLease();
|
|
@@ -31293,6 +31679,7 @@ async function startDaemon(args) {
|
|
|
31293
31679
|
clearInterval(heartbeat);
|
|
31294
31680
|
clearInterval(versionRefresh);
|
|
31295
31681
|
clearInterval(workspaceLeaseMonitor);
|
|
31682
|
+
suspensionDetector.stop();
|
|
31296
31683
|
for (const entry of activeAgents.values()) {
|
|
31297
31684
|
abortActiveAgent(entry, { userInitiated: true });
|
|
31298
31685
|
}
|
|
@@ -31302,6 +31689,7 @@ async function startDaemon(args) {
|
|
|
31302
31689
|
localServer.close(() => resolve7());
|
|
31303
31690
|
});
|
|
31304
31691
|
} finally {
|
|
31692
|
+
sleepInhibitor.dispose();
|
|
31305
31693
|
releaseOwnerLease();
|
|
31306
31694
|
}
|
|
31307
31695
|
};
|
|
@@ -32729,7 +33117,7 @@ async function runSandbox(config) {
|
|
|
32729
33117
|
}
|
|
32730
33118
|
|
|
32731
33119
|
// src/service-manager.ts
|
|
32732
|
-
var
|
|
33120
|
+
var import_node_child_process9 = require("child_process");
|
|
32733
33121
|
var import_node_fs16 = require("fs");
|
|
32734
33122
|
var import_node_os10 = require("os");
|
|
32735
33123
|
var import_node_path13 = require("path");
|
|
@@ -32940,7 +33328,7 @@ function writeManifest(path, contents) {
|
|
|
32940
33328
|
}
|
|
32941
33329
|
}
|
|
32942
33330
|
function runServiceCommand(command) {
|
|
32943
|
-
const result = (0,
|
|
33331
|
+
const result = (0, import_node_child_process9.spawnSync)(command.command, command.args, { encoding: "utf8" });
|
|
32944
33332
|
const status = result.status ?? 1;
|
|
32945
33333
|
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
32946
33334
|
if (status !== 0 && !command.tolerateFailure) {
|
|
@@ -32983,7 +33371,7 @@ function printDaemonServiceStatus(args = []) {
|
|
|
32983
33371
|
function logEffectiveGitIdentity(lifecycle, projectPath) {
|
|
32984
33372
|
const readConfig2 = (key) => {
|
|
32985
33373
|
try {
|
|
32986
|
-
const result = (0,
|
|
33374
|
+
const result = (0, import_node_child_process10.spawnSync)("git", ["config", "--get", key], {
|
|
32987
33375
|
cwd: projectPath,
|
|
32988
33376
|
env: getDaemonCliEnvironment(),
|
|
32989
33377
|
encoding: "utf8",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiden-ade/sandbox-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Alan agent runtime — cloud sandbox and local daemon (alan-agent CLI)",
|
|
6
6
|
"bin": {
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"smol-toml": "^1.6.1"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@types/node": "^22.0.0",
|
|
20
|
-
"socket.io": "^4.8.0",
|
|
21
19
|
"socket.io-client": "^4.8.0",
|
|
20
|
+
"socket.io": "^4.8.0",
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
22
|
"tsup": "^8.5.1",
|
|
23
23
|
"tsx": "^4.19.0",
|
|
24
24
|
"typescript": "~5.9.3",
|