@agentskit/harness 0.11.0 → 0.12.0
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/CHANGELOG.md +20 -0
- package/capabilities/public-surface.json +86 -84
- package/dist/cli.js +54 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +49 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/release/manifest.json +3 -3
- package/release/notes.md +13 -0
package/dist/cli.js
CHANGED
|
@@ -2503,6 +2503,21 @@ var orcaStatus = async (runner, options2 = {}) => parseOrcaStatus(await orcaJson
|
|
|
2503
2503
|
var orcaWorktrees = async (runner, options2 = {}) => parseOrcaWorktrees(await orcaJson(runner, ["worktree", "ps"], options2));
|
|
2504
2504
|
var orcaAgentHooks = async (runner, options2 = {}) => parseOrcaAgentHooks(await orcaJson(runner, ["agent", "hooks", "status"], options2));
|
|
2505
2505
|
var orcaAccountList = async (runner, options2 = {}) => orcaJson(runner, ["account", "list"], options2);
|
|
2506
|
+
var orcaDiagnosticsMemory = async (runner, options2 = {}) => {
|
|
2507
|
+
try {
|
|
2508
|
+
const result = await orcaJson(runner, ["diagnostics", "memory"], options2);
|
|
2509
|
+
if (!isRecord7(result)) return null;
|
|
2510
|
+
const host = isRecord7(result["host"]) ? result["host"] : {};
|
|
2511
|
+
const availableBytes = host["availableMemory"];
|
|
2512
|
+
if (typeof availableBytes !== "number" || !Number.isFinite(availableBytes) || availableBytes <= 0) return null;
|
|
2513
|
+
const totalBytes = typeof host["totalMemory"] === "number" ? host["totalMemory"] : null;
|
|
2514
|
+
const worktrees = Array.isArray(result["worktrees"]) ? result["worktrees"] : [];
|
|
2515
|
+
const agentRssSamples = worktrees.filter(isRecord7).flatMap((worktree) => Array.isArray(worktree["sessions"]) ? worktree["sessions"] : []).filter(isRecord7).map((session) => session["memory"]).filter((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
|
|
2516
|
+
return { availableBytes, totalBytes, agentRssSamples };
|
|
2517
|
+
} catch {
|
|
2518
|
+
return null;
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2506
2521
|
var parseOrcaWorktreeCreate = (result) => {
|
|
2507
2522
|
const record3 = isRecord7(result) ? result : {};
|
|
2508
2523
|
const nested = isRecord7(record3["worktree"]) ? record3["worktree"] : record3;
|
|
@@ -2578,6 +2593,12 @@ var orcaTerminalWait = async (runner, input, options2 = {}) => {
|
|
|
2578
2593
|
const wait = isRecord7(record3["wait"]) ? record3["wait"] : record3;
|
|
2579
2594
|
return { satisfied: wait["satisfied"] === true, raw: result };
|
|
2580
2595
|
};
|
|
2596
|
+
var orcaTerminalScreen = async (runner, input, options2 = {}) => {
|
|
2597
|
+
const result = await orcaJson(runner, ["terminal", "read", "--terminal", input.terminal, "--screen"], options2);
|
|
2598
|
+
const record3 = isRecord7(result) ? isRecord7(result["terminal"]) ? result["terminal"] : result : {};
|
|
2599
|
+
const screen = record3["tail"] ?? record3["screen"] ?? record3["lines"] ?? record3["text"] ?? record3["output"];
|
|
2600
|
+
return Array.isArray(screen) ? screen.map((line2) => isRecord7(line2) ? str(line2["text"], str(line2["line"])) : String(line2)).join("\n") : typeof screen === "string" ? screen : "";
|
|
2601
|
+
};
|
|
2581
2602
|
var parseOrcaAutomations = (result) => {
|
|
2582
2603
|
const list2 = isRecord7(result) ? Array.isArray(result["automations"]) ? result["automations"] : Array.isArray(result["items"]) ? result["items"] : [] : Array.isArray(result) ? result : [];
|
|
2583
2604
|
return list2.filter(isRecord7).map((item) => ({ id: str(item["id"]), name: str(item["name"]), enabled: item["enabled"] !== false && item["disabled"] !== true, trigger: str(item["rrule"], str(item["trigger"], str(item["schedule"], typeof item["schedule"] === "object" && item["schedule"] !== null ? JSON.stringify(item["schedule"]) : ""))), provider: str(item["agentId"], str(item["provider"], str(item["agent"]))) || null, raw: item })).filter((item) => item.id);
|
|
@@ -3194,8 +3215,8 @@ var isWsl = (platform = process.platform, osRelease = release(), env = process.e
|
|
|
3194
3215
|
var assessSlots = (input) => {
|
|
3195
3216
|
const platform = input.platform ?? process.platform;
|
|
3196
3217
|
const wsl = isWsl(platform, input.osRelease);
|
|
3197
|
-
const freeBytes = input.freeBytes ?? availableMemoryBytes(platform);
|
|
3198
|
-
const totalBytes = input.totalBytes ?? totalmem();
|
|
3218
|
+
const freeBytes = input.freeBytes ?? input.orcaMemory?.availableBytes ?? availableMemoryBytes(platform);
|
|
3219
|
+
const totalBytes = input.totalBytes ?? input.orcaMemory?.totalBytes ?? totalmem();
|
|
3199
3220
|
const sample = input.sample ?? { ...sampleMachine(), memoryUsedPercent: Number(Math.max(0, Math.min(100, (1 - freeBytes / Math.max(1, totalBytes)) * 100)).toFixed(2)) };
|
|
3200
3221
|
const freeRamGb = Number((freeBytes / 1024 ** 3).toFixed(2));
|
|
3201
3222
|
const reasons = [];
|
|
@@ -3203,9 +3224,11 @@ var assessSlots = (input) => {
|
|
|
3203
3224
|
const adaptive = adaptiveConcurrency(ceiling, sample, { warningPercent: input.machine.warningPercent, criticalPercent: input.machine.criticalPercent });
|
|
3204
3225
|
if (adaptive < ceiling) reasons.push(`machine pressure capped concurrency at ${adaptive} (load ${sample.load1PerCpuPercent}%, memory ${sample.memoryUsedPercent}%)`);
|
|
3205
3226
|
const reservedBytes = input.machine.minFreeRamGb * 1024 ** 3;
|
|
3206
|
-
const
|
|
3227
|
+
const measuredAgentBytes = input.orcaMemory?.agentRssSamples.length ? input.orcaMemory.agentRssSamples.reduce((total, value) => total + value, 0) / input.orcaMemory.agentRssSamples.length : null;
|
|
3228
|
+
const perAgentBytes = measuredAgentBytes ?? input.machine.agentRssMb * 1024 ** 2;
|
|
3229
|
+
const perAgentMb = Math.round(perAgentBytes / 1024 ** 2);
|
|
3207
3230
|
const ramBound = Math.max(0, Math.floor((freeBytes - reservedBytes) / perAgentBytes)) + input.running;
|
|
3208
|
-
if (ramBound < adaptive) reasons.push(`free RAM ${freeRamGb} GB minus ${input.machine.minFreeRamGb} GB reserve fits ${Math.max(0, ramBound - input.running)} more agent(s) at ${
|
|
3231
|
+
if (ramBound < adaptive) reasons.push(`free RAM ${freeRamGb} GB minus ${input.machine.minFreeRamGb} GB reserve fits ${Math.max(0, ramBound - input.running)} more agent(s) at ${perAgentMb} MB each${measuredAgentBytes ? " (measured)" : ""}`);
|
|
3209
3232
|
let maxAgents = Math.min(adaptive, ramBound);
|
|
3210
3233
|
if (wsl && maxAgents > input.machine.wslCap) {
|
|
3211
3234
|
maxAgents = input.machine.wslCap;
|
|
@@ -3855,7 +3878,8 @@ var runLoopDoctor = async (input) => {
|
|
|
3855
3878
|
push("orca.worktrees", "warning", `worktree ps unavailable: ${workersError}`);
|
|
3856
3879
|
}
|
|
3857
3880
|
const running = countRunningWorkers(worktrees);
|
|
3858
|
-
const
|
|
3881
|
+
const orcaMemory = await orcaDiagnosticsMemory(input.runner, orcaOptions2);
|
|
3882
|
+
const machine = assessSlots({ machine: config.machine, running, platform: input.platform, orcaMemory });
|
|
3859
3883
|
push("machine.slots", machine.free > 0 ? "passed" : "warning", `${machine.free} free of ${machine.maxAgents} (running ${running}, cpus ${machine.sample.cpus}, load ${machine.sample.load1PerCpuPercent}%, free RAM ${machine.freeRamGb} GB)${machine.reasons.length ? `; ${machine.reasons.join("; ")}` : ""}`);
|
|
3860
3884
|
let queue = [];
|
|
3861
3885
|
let queueError = null;
|
|
@@ -4747,11 +4771,12 @@ var gatherLoopState = async (input) => {
|
|
|
4747
4771
|
const { config } = input.loaded;
|
|
4748
4772
|
const person = queueOwner(input.loaded);
|
|
4749
4773
|
const orca = { bin: config.orca.bin, timeoutMs: config.orca.timeoutMs };
|
|
4750
|
-
const [accountList, agentHooks, worktrees, queue] = await Promise.all([
|
|
4774
|
+
const [accountList, agentHooks, worktrees, queue, orcaMemory] = await Promise.all([
|
|
4751
4775
|
orcaAccountList(input.runner, orca).catch(() => ({})),
|
|
4752
4776
|
orcaAgentHooks(input.runner, orca).catch(() => ({})),
|
|
4753
4777
|
orcaWorktrees(input.runner, orca),
|
|
4754
|
-
fetchLinearQueue(input.runner, { bin: config.orca.bin, workspaceId: config.linear.workspaceId, teamKey: config.linear.teamKey, assignee: person, filter: config.linear, orca })
|
|
4778
|
+
fetchLinearQueue(input.runner, { bin: config.orca.bin, workspaceId: config.linear.workspaceId, teamKey: config.linear.teamKey, assignee: person, filter: config.linear, orca }),
|
|
4779
|
+
orcaDiagnosticsMemory(input.runner, orca)
|
|
4755
4780
|
]);
|
|
4756
4781
|
const providers = await detectProviders({ providers: providerSpecs(config), accountList, agentHooks, env: input.env, platform: input.platform, exhaustedPercent: config.models.cooldown.exhaustedPercent, cooldowns: activeCooldowns(readCooldowns(input.loaded.stateDir), input.now()), now: input.now });
|
|
4757
4782
|
const availableIds = providers.filter((provider) => provider.available).map((provider) => provider.id);
|
|
@@ -4766,7 +4791,7 @@ var gatherLoopState = async (input) => {
|
|
|
4766
4791
|
})]))) : {};
|
|
4767
4792
|
const routing = routeAllRoles(config, providers, extrasByRole);
|
|
4768
4793
|
const running = countRunningWorkers(worktrees);
|
|
4769
|
-
const slots = assessSlots({ machine: config.machine, running, platform: input.platform, ...input.machine });
|
|
4794
|
+
const slots = assessSlots({ machine: config.machine, running, platform: input.platform, orcaMemory, ...input.machine });
|
|
4770
4795
|
const leases = input.ledger.active();
|
|
4771
4796
|
const busy = busyIssues(queue, leases, worktrees, person);
|
|
4772
4797
|
const candidates = queue.filter((issue) => !busy.has(issue.identifier) && (!input.onlyIssue || issue.identifier === input.onlyIssue));
|
|
@@ -5278,14 +5303,34 @@ ${JSON.stringify(stored.contract, null, 2)}
|
|
|
5278
5303
|
return false;
|
|
5279
5304
|
}
|
|
5280
5305
|
};
|
|
5306
|
+
var captureWorkerOutput = async (ctx, terminal2) => {
|
|
5307
|
+
if (!terminal2) return null;
|
|
5308
|
+
try {
|
|
5309
|
+
const screen = (await orcaTerminalScreen(ctx.runner, { terminal: terminal2 }, orcaOptions(ctx.config))).trim();
|
|
5310
|
+
return screen ? screen.slice(-2e3) : null;
|
|
5311
|
+
} catch {
|
|
5312
|
+
return null;
|
|
5313
|
+
}
|
|
5314
|
+
};
|
|
5281
5315
|
var escalateLinear = async (ctx, record3, kind, body2, actions) => {
|
|
5282
5316
|
if (ctx.dryRun) {
|
|
5283
5317
|
actions.push(`would mark ${kind} in Linear and Orca`);
|
|
5284
5318
|
return;
|
|
5285
5319
|
}
|
|
5320
|
+
const workerOutput = await captureWorkerOutput(ctx, record3.terminal);
|
|
5321
|
+
const fullBody = workerOutput ? `${body2}
|
|
5322
|
+
|
|
5323
|
+
<details><summary>Worker's last terminal output</summary>
|
|
5324
|
+
|
|
5325
|
+
\`\`\`
|
|
5326
|
+
${workerOutput}
|
|
5327
|
+
\`\`\`
|
|
5328
|
+
|
|
5329
|
+
</details>` : body2;
|
|
5330
|
+
if (workerOutput) actions.push("captured worker terminal output for the escalation");
|
|
5286
5331
|
const linear = linearOptions(ctx.config);
|
|
5287
5332
|
try {
|
|
5288
|
-
await linearCommentAdd(ctx.runner, { issue: record3.issue, body: `${
|
|
5333
|
+
await linearCommentAdd(ctx.runner, { issue: record3.issue, body: `${fullBody}
|
|
5289
5334
|
|
|
5290
5335
|
<!-- loop:${kind}:${record3.leaseId} -->`, dedupeKey: `${kind}:${record3.issue}:${record3.leaseId}` }, linear);
|
|
5291
5336
|
await linearLabelAdd(ctx.runner, { issue: record3.issue, labels: [ctx.config.linear.blockedLabel] }, linear);
|