@corenel/sidecar 0.3.2 → 0.4.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/dist/cli.js +409 -26
- package/package.json +8 -5
package/dist/cli.js
CHANGED
|
@@ -21692,6 +21692,7 @@ var SidecarServer = class {
|
|
|
21692
21692
|
case "session-destroy":
|
|
21693
21693
|
case "session-commit":
|
|
21694
21694
|
case "session-snapshots":
|
|
21695
|
+
case "session-snapshot-usage":
|
|
21695
21696
|
case "session-watch":
|
|
21696
21697
|
case "session-unwatch":
|
|
21697
21698
|
if (!this.authed) {
|
|
@@ -21877,6 +21878,11 @@ var SidecarServer = class {
|
|
|
21877
21878
|
this.ch.send({ t: "session-snapshots-result", id, snapshots });
|
|
21878
21879
|
return;
|
|
21879
21880
|
}
|
|
21881
|
+
case "session-snapshot-usage": {
|
|
21882
|
+
const usage = sessions.snapshotUsage ? await sessions.snapshotUsage() : { usedBytes: 0, budgetBytes: 0, count: 0 };
|
|
21883
|
+
this.ch.send({ t: "session-snapshot-usage-result", id, usage });
|
|
21884
|
+
return;
|
|
21885
|
+
}
|
|
21880
21886
|
case "session-watch": {
|
|
21881
21887
|
if (this.sessionWatch.has(m2.sessionId)) {
|
|
21882
21888
|
this.ch.send({ t: "session-ok", id });
|
|
@@ -22331,10 +22337,18 @@ var SidecarServer = class {
|
|
|
22331
22337
|
ask = (questions) => this.hostCall("ask", "ask", [questions]).then((v2) => v2, () => null);
|
|
22332
22338
|
};
|
|
22333
22339
|
|
|
22334
|
-
//
|
|
22340
|
+
// ../harness/sidecar/wsChannel.ts
|
|
22335
22341
|
function wsChannel(ws) {
|
|
22336
22342
|
let handler = null;
|
|
22337
22343
|
let closeHandler = null;
|
|
22344
|
+
const pending = [];
|
|
22345
|
+
const flushPending = () => {
|
|
22346
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
22347
|
+
while (pending.length > 0) {
|
|
22348
|
+
ws.send(JSON.stringify(pending.shift()));
|
|
22349
|
+
}
|
|
22350
|
+
};
|
|
22351
|
+
ws.on("open", flushPending);
|
|
22338
22352
|
ws.on("message", (data) => {
|
|
22339
22353
|
if (!handler) return;
|
|
22340
22354
|
try {
|
|
@@ -22348,7 +22362,11 @@ function wsChannel(ws) {
|
|
|
22348
22362
|
});
|
|
22349
22363
|
return {
|
|
22350
22364
|
send(msg) {
|
|
22351
|
-
if (ws.readyState === ws.OPEN)
|
|
22365
|
+
if (ws.readyState === ws.OPEN) {
|
|
22366
|
+
ws.send(JSON.stringify(msg));
|
|
22367
|
+
} else if (ws.readyState === ws.CONNECTING) {
|
|
22368
|
+
pending.push(msg);
|
|
22369
|
+
}
|
|
22352
22370
|
},
|
|
22353
22371
|
onMessage(h2) {
|
|
22354
22372
|
handler = h2;
|
|
@@ -24052,7 +24070,34 @@ import { isAbsolute as isAbsolute3, relative as relative3, resolve as resolve3 }
|
|
|
24052
24070
|
// ../tools-node/fileService.ts
|
|
24053
24071
|
import { promises as fs4 } from "node:fs";
|
|
24054
24072
|
import { join as join3, resolve as resolve2, relative as relative2, dirname as dirname2, sep as sep2, basename, isAbsolute as isAbsolute2 } from "node:path";
|
|
24055
|
-
var SKIP = /* @__PURE__ */ new Set([
|
|
24073
|
+
var SKIP = /* @__PURE__ */ new Set([
|
|
24074
|
+
"node_modules",
|
|
24075
|
+
".git",
|
|
24076
|
+
"dist",
|
|
24077
|
+
"build",
|
|
24078
|
+
".next",
|
|
24079
|
+
".cache",
|
|
24080
|
+
".turbo",
|
|
24081
|
+
"dist-corenel",
|
|
24082
|
+
"test-results",
|
|
24083
|
+
"playwright-report"
|
|
24084
|
+
]);
|
|
24085
|
+
var SKIP_PATHS = /* @__PURE__ */ new Set([".claude/skills"]);
|
|
24086
|
+
var COVERAGE_REPORT_MARKERS = /* @__PURE__ */ new Set([
|
|
24087
|
+
"coverage-final.json",
|
|
24088
|
+
"lcov.info",
|
|
24089
|
+
"clover.xml",
|
|
24090
|
+
"coverage-summary.json",
|
|
24091
|
+
"prettify.js"
|
|
24092
|
+
]);
|
|
24093
|
+
async function looksLikeGeneratedCoverage(absDir) {
|
|
24094
|
+
try {
|
|
24095
|
+
const names = await fs4.readdir(absDir);
|
|
24096
|
+
return names.some((n2) => COVERAGE_REPORT_MARKERS.has(n2));
|
|
24097
|
+
} catch {
|
|
24098
|
+
return false;
|
|
24099
|
+
}
|
|
24100
|
+
}
|
|
24056
24101
|
function escapesRoot(rel, platformSep = sep2) {
|
|
24057
24102
|
if (rel === "") return false;
|
|
24058
24103
|
if (rel === ".." || rel.startsWith(".." + platformSep)) return true;
|
|
@@ -24127,6 +24172,8 @@ var NodeFileService = class {
|
|
|
24127
24172
|
if (SKIP.has(e2.name)) continue;
|
|
24128
24173
|
const absChild = join3(absDir, e2.name);
|
|
24129
24174
|
const path = normalizeFilePath(relative2(this.root, absChild));
|
|
24175
|
+
if (e2.isDirectory() && SKIP_PATHS.has(path)) continue;
|
|
24176
|
+
if (e2.isDirectory() && e2.name === "coverage" && await looksLikeGeneratedCoverage(absChild)) continue;
|
|
24130
24177
|
if (e2.isDirectory()) {
|
|
24131
24178
|
out.push({ name: e2.name, path, kind: "directory", children: await walk(absChild) });
|
|
24132
24179
|
} else {
|
|
@@ -24458,6 +24505,34 @@ ${e2 instanceof Error ? e2.message : String(e2)}` });
|
|
|
24458
24505
|
});
|
|
24459
24506
|
}
|
|
24460
24507
|
|
|
24508
|
+
// src/session/snapshotBudget.ts
|
|
24509
|
+
var DEFAULT_SNAPSHOT_BUDGET_BYTES = 32 * 1024 ** 3;
|
|
24510
|
+
function parseByteSize(spelling) {
|
|
24511
|
+
const s2 = spelling.trim();
|
|
24512
|
+
const m2 = /^(\d+(?:\.\d+)?)\s*([KMGT]?)i?B?$/i.exec(s2);
|
|
24513
|
+
if (!m2) throw new Error(`not a size: "${spelling}" (expected e.g. "8G", "512M", or a bare byte count)`);
|
|
24514
|
+
const n2 = Number(m2[1]);
|
|
24515
|
+
const unit = (m2[2] ?? "").toUpperCase();
|
|
24516
|
+
const mult = unit === "K" ? 1024 : unit === "M" ? 1024 ** 2 : unit === "G" ? 1024 ** 3 : unit === "T" ? 1024 ** 4 : 1;
|
|
24517
|
+
return Math.round(n2 * mult);
|
|
24518
|
+
}
|
|
24519
|
+
function formatByteSize(bytes) {
|
|
24520
|
+
const units = [[1024 ** 4, "T"], [1024 ** 3, "G"], [1024 ** 2, "M"], [1024, "K"]];
|
|
24521
|
+
for (const [mult, suffix] of units) {
|
|
24522
|
+
if (bytes >= mult) return `${(bytes / mult).toFixed(1)}${suffix}`;
|
|
24523
|
+
}
|
|
24524
|
+
return `${bytes}B`;
|
|
24525
|
+
}
|
|
24526
|
+
function checkSnapshotBudget(check) {
|
|
24527
|
+
const { usedBytes, estimateBytes, budgetBytes } = check;
|
|
24528
|
+
const projected = usedBytes + estimateBytes;
|
|
24529
|
+
if (projected <= budgetBytes) return { ok: true };
|
|
24530
|
+
return {
|
|
24531
|
+
ok: false,
|
|
24532
|
+
message: `refusing to capture: session snapshots already use ${formatByteSize(usedBytes)} of a ${formatByteSize(budgetBytes)} budget, and this capture could add up to ${formatByteSize(estimateBytes)} more. Free space by removing snapshots you no longer need (\`msb snapshot list\`, \`msb snapshot remove <name>\`), or raise the ceiling with --snapshot-budget <size>.`
|
|
24533
|
+
};
|
|
24534
|
+
}
|
|
24535
|
+
|
|
24461
24536
|
// src/session/microvmBackend.ts
|
|
24462
24537
|
var GUEST_WORKDIR = "/workspace";
|
|
24463
24538
|
var seq2 = 0;
|
|
@@ -24578,6 +24653,14 @@ function makeMicrovmBackend(deps) {
|
|
|
24578
24653
|
}
|
|
24579
24654
|
return r2.stdout;
|
|
24580
24655
|
};
|
|
24656
|
+
const listSnapshotsRaw = async () => {
|
|
24657
|
+
const r2 = await deps.run(snapshotListArgs());
|
|
24658
|
+
if (r2.code !== 0) {
|
|
24659
|
+
const detail = (r2.stderr || r2.stdout).trim().split("\n").slice(-2).join("; ");
|
|
24660
|
+
throw new Error(`list snapshots failed (msb exit ${r2.code}): ${detail || "no output"}`);
|
|
24661
|
+
}
|
|
24662
|
+
return parseSnapshotList(r2.stdout);
|
|
24663
|
+
};
|
|
24581
24664
|
let lastReconcile = 0;
|
|
24582
24665
|
let reconciling = false;
|
|
24583
24666
|
const RECONCILE_MS = 1e4;
|
|
@@ -24905,7 +24988,13 @@ function makeMicrovmBackend(deps) {
|
|
|
24905
24988
|
workdir,
|
|
24906
24989
|
createdAt: Number.isFinite(stamped) && stamped > 0 ? stamped : row.createdAt ?? now2(),
|
|
24907
24990
|
...owner ? { owner } : {},
|
|
24908
|
-
...repoUrl && repoCommit ? { source: { url: repoUrl, commit: repoCommit, ...repoRef ? { ref: repoRef } : {} } } : {}
|
|
24991
|
+
...repoUrl && repoCommit ? { source: { url: repoUrl, commit: repoCommit, ...repoRef ? { ref: repoRef } : {} } } : {},
|
|
24992
|
+
/* THE ONLY PLACE THIS IS EVER SET TRUE. `create()` never touches it,
|
|
24993
|
+
* so a card can tell "this daemon booted it" from "this daemon
|
|
24994
|
+
* found it already running" — procs.ts's own words: a running
|
|
24995
|
+
* process nobody can see is worse than one that died, and adoption
|
|
24996
|
+
* used to be invisible past this log line. */
|
|
24997
|
+
adopted: true
|
|
24909
24998
|
});
|
|
24910
24999
|
adopted.push(id);
|
|
24911
25000
|
}
|
|
@@ -24944,6 +25033,12 @@ function makeMicrovmBackend(deps) {
|
|
|
24944
25033
|
`session ${id} must be paused before it can be captured (it is "${s2.status}"): pause first and check the drain result, or the snapshot may be torn`
|
|
24945
25034
|
);
|
|
24946
25035
|
}
|
|
25036
|
+
const existing = await listSnapshotsRaw();
|
|
25037
|
+
const usedBytes = existing.reduce((sum, snap) => sum + (snap.sizeBytes ?? 0), 0);
|
|
25038
|
+
const budgetBytes = deps.snapshotBudgetBytes ?? DEFAULT_SNAPSHOT_BUDGET_BYTES;
|
|
25039
|
+
const estimateBytes = parseByteSize(limits.rootDisk);
|
|
25040
|
+
const budget = checkSnapshotBudget({ usedBytes, estimateBytes, budgetBytes });
|
|
25041
|
+
if (!budget.ok) throw new Error(budget.message);
|
|
24947
25042
|
const snapName = name ?? `${sandboxName(id)}-${now2()}-${++snapSeq}`;
|
|
24948
25043
|
await msb([
|
|
24949
25044
|
"snapshot",
|
|
@@ -24975,12 +25070,19 @@ function makeMicrovmBackend(deps) {
|
|
|
24975
25070
|
* memory would show nothing after a restart while the artifacts sat on
|
|
24976
25071
|
* disk. */
|
|
24977
25072
|
async snapshots() {
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
|
|
24982
|
-
|
|
24983
|
-
|
|
25073
|
+
return listSnapshotsRaw();
|
|
25074
|
+
},
|
|
25075
|
+
/** Current usage against the refuse-at-budget ceiling, BEFORE a capture
|
|
25076
|
+
* ever hits it — a budget you only learn about at the moment it stops you
|
|
25077
|
+
* is a bad budget. Same arithmetic `commit()` checks against, so a caller
|
|
25078
|
+
* watching this can see a refusal coming. */
|
|
25079
|
+
async snapshotUsage() {
|
|
25080
|
+
const existing = await listSnapshotsRaw();
|
|
25081
|
+
return {
|
|
25082
|
+
usedBytes: existing.reduce((sum, snap) => sum + (snap.sizeBytes ?? 0), 0),
|
|
25083
|
+
budgetBytes: deps.snapshotBudgetBytes ?? DEFAULT_SNAPSHOT_BUDGET_BYTES,
|
|
25084
|
+
count: existing.length
|
|
25085
|
+
};
|
|
24984
25086
|
}
|
|
24985
25087
|
};
|
|
24986
25088
|
}
|
|
@@ -25091,7 +25193,10 @@ function toInfo(h2, backend) {
|
|
|
25091
25193
|
// Read from the HANDLE, so it survives every later list() — and, on the
|
|
25092
25194
|
// microVM backend, a daemon restart, since the sandbox carries it in its
|
|
25093
25195
|
// own labels.
|
|
25094
|
-
...h2.source ? { source: h2.source } : {}
|
|
25196
|
+
...h2.source ? { source: h2.source } : {},
|
|
25197
|
+
// Same reasoning as `source`: a client cannot derive "this daemon found
|
|
25198
|
+
// it running rather than booted it" from anything else it is given.
|
|
25199
|
+
...h2.adopted ? { adopted: true } : {}
|
|
25095
25200
|
};
|
|
25096
25201
|
}
|
|
25097
25202
|
function makeSessionsPort(deps) {
|
|
@@ -25167,6 +25272,9 @@ function makeSessionsPort(deps) {
|
|
|
25167
25272
|
...s2.sizeBytes !== void 0 ? { sizeBytes: s2.sizeBytes } : {}
|
|
25168
25273
|
}))
|
|
25169
25274
|
} : {},
|
|
25275
|
+
...backend.snapshotUsage ? {
|
|
25276
|
+
snapshotUsage: () => backend.snapshotUsage()
|
|
25277
|
+
} : {},
|
|
25170
25278
|
...backend.metrics ? {
|
|
25171
25279
|
watchMetrics: (id, onSample) => {
|
|
25172
25280
|
const st2 = backend.metrics(id, onSample);
|
|
@@ -28012,6 +28120,7 @@ async function wireSessions(opts) {
|
|
|
28012
28120
|
image: opts.image,
|
|
28013
28121
|
// Safe to apply everywhere: it only rewrites drive-letter paths.
|
|
28014
28122
|
hostPath: wslHostPath,
|
|
28123
|
+
...opts.snapshotBudgetBytes !== void 0 ? { snapshotBudgetBytes: opts.snapshotBudgetBytes } : {},
|
|
28015
28124
|
...opts.log ? { log: opts.log } : {}
|
|
28016
28125
|
});
|
|
28017
28126
|
const manager = new SessionManager({
|
|
@@ -28891,24 +29000,75 @@ function ensureWritable(path) {
|
|
|
28891
29000
|
throw new Error(`${PROMPD_INSTALL_DIR}/ install dirs are read-only (managed by package install). Refusing to modify "${path}". Install or remove packages instead of editing them directly.`);
|
|
28892
29001
|
}
|
|
28893
29002
|
}
|
|
28894
|
-
function
|
|
28895
|
-
|
|
28896
|
-
|
|
28897
|
-
|
|
29003
|
+
function describe(n2) {
|
|
29004
|
+
return `${n2.kind === "directory" ? "[dir] " : ""}${n2.path}${n2.kind === "file" && n2.size != null ? ` (${n2.size}b)` : ""}`;
|
|
29005
|
+
}
|
|
29006
|
+
function bfsNodes(roots, maxDepth = Infinity) {
|
|
29007
|
+
const out = [];
|
|
29008
|
+
let level = roots;
|
|
29009
|
+
let depth = 1;
|
|
29010
|
+
while (level.length) {
|
|
29011
|
+
const next = [];
|
|
29012
|
+
for (const n2 of level) {
|
|
29013
|
+
out.push(n2);
|
|
29014
|
+
if (n2.kind === "directory" && n2.children?.length && depth < maxDepth) next.push(...n2.children);
|
|
29015
|
+
}
|
|
29016
|
+
level = next;
|
|
29017
|
+
depth++;
|
|
29018
|
+
}
|
|
29019
|
+
return out;
|
|
29020
|
+
}
|
|
29021
|
+
var LIST_FILES_MAX_ENTRIES = 300;
|
|
29022
|
+
function selectByBreadthFirstCap(roots, maxDepth) {
|
|
29023
|
+
const bfs = bfsNodes(roots, maxDepth);
|
|
29024
|
+
return { keep: new Set(bfs.slice(0, LIST_FILES_MAX_ENTRIES)), total: bfs.length };
|
|
29025
|
+
}
|
|
29026
|
+
function describeInTreeOrder(roots, keep, maxDepth = Infinity, depth = 1, out = []) {
|
|
29027
|
+
for (const n2 of roots) {
|
|
29028
|
+
if (!keep.has(n2)) continue;
|
|
29029
|
+
out.push(describe(n2));
|
|
29030
|
+
if (n2.kind === "directory" && n2.children?.length && depth < maxDepth) {
|
|
29031
|
+
describeInTreeOrder(n2.children, keep, maxDepth, depth + 1, out);
|
|
29032
|
+
}
|
|
28898
29033
|
}
|
|
28899
29034
|
return out;
|
|
28900
29035
|
}
|
|
29036
|
+
function truncationMarker(total, maxDepth) {
|
|
29037
|
+
const omitted = total - LIST_FILES_MAX_ENTRIES;
|
|
29038
|
+
const omittedEntries = `${omitted} more entr${omitted === 1 ? "y" : "ies"} omitted`;
|
|
29039
|
+
if (maxDepth != null && Number.isFinite(maxDepth)) {
|
|
29040
|
+
return `\u2026 truncated: ${omittedEntries}. ${total} entries were collected within depth ${maxDepth}; deeper levels were not counted, so this is not the size of the whole tree. Pass \`dir\` to narrow by subdirectory, or a larger \`depth\` to go deeper.`;
|
|
29041
|
+
}
|
|
29042
|
+
return `\u2026 truncated: ${omittedEntries} (${total} total). Pass \`dir\` to list a specific subdirectory (narrower breadth), or \`depth\` to limit how many levels deep the listing goes (narrower depth), instead of the whole tree.`;
|
|
29043
|
+
}
|
|
29044
|
+
function normalizeDepth(args) {
|
|
29045
|
+
const v2 = args["depth"];
|
|
29046
|
+
if (v2 == null) return void 0;
|
|
29047
|
+
const n2 = typeof v2 === "number" ? v2 : typeof v2 === "string" ? Number(v2) : NaN;
|
|
29048
|
+
if (!Number.isFinite(n2)) return void 0;
|
|
29049
|
+
const floored = Math.floor(n2);
|
|
29050
|
+
return floored < 1 ? 1 : floored;
|
|
29051
|
+
}
|
|
28901
29052
|
var listFilesTool = {
|
|
28902
29053
|
name: "list_files",
|
|
28903
|
-
description: "List files and directories in the workspace (the open folder or the virtual workspace).
|
|
29054
|
+
description: "List files and directories in the workspace (the open folder or the virtual workspace). Start shallow and drill in: pass `depth` to limit how many levels deep the listing goes (vertical narrowing \u2014 `depth: 1` returns just the immediate children of the target, a quick orientation call) and/or `dir` to list a specific subdirectory instead of the whole tree (horizontal narrowing). Omit `depth` for a full recursive listing. Returns paths relative to the workspace root.",
|
|
28904
29055
|
parameters: {
|
|
28905
29056
|
type: "object",
|
|
28906
|
-
properties: {
|
|
29057
|
+
properties: {
|
|
29058
|
+
dir: { type: "string", description: "Subdirectory to list (optional; omit for the root)." },
|
|
29059
|
+
depth: {
|
|
29060
|
+
type: "number",
|
|
29061
|
+
description: "How many levels deep to list (optional; omit for the full recursive tree). 1 = immediate children of `dir` (or the root) only, 2 = two levels, and so on. Values below 1 (0, negative) are treated as 1, not rejected."
|
|
29062
|
+
}
|
|
29063
|
+
}
|
|
28907
29064
|
},
|
|
28908
29065
|
async run(args, ctx) {
|
|
28909
29066
|
const files = requireFiles(ctx);
|
|
28910
29067
|
const nodes = await files.list(str(args, "dir") || void 0);
|
|
28911
|
-
const
|
|
29068
|
+
const maxDepth = normalizeDepth(args);
|
|
29069
|
+
const { keep, total } = selectByBreadthFirstCap(nodes, maxDepth);
|
|
29070
|
+
const lines = describeInTreeOrder(nodes, keep, maxDepth);
|
|
29071
|
+
if (total > LIST_FILES_MAX_ENTRIES) lines.push(truncationMarker(total, maxDepth));
|
|
28912
29072
|
return lines.length ? lines.join("\n") : "(empty)";
|
|
28913
29073
|
}
|
|
28914
29074
|
};
|
|
@@ -29647,7 +29807,7 @@ function allTools() {
|
|
|
29647
29807
|
return [...defaultTools, ...extensionTools()];
|
|
29648
29808
|
}
|
|
29649
29809
|
|
|
29650
|
-
// ../
|
|
29810
|
+
// ../harness/tools/callAgent.ts
|
|
29651
29811
|
function str5(v2) {
|
|
29652
29812
|
return typeof v2 === "string" ? v2.trim() : "";
|
|
29653
29813
|
}
|
|
@@ -36875,6 +37035,7 @@ var BUILTIN_NAMESPACES = {
|
|
|
36875
37035
|
ask_user: "agent",
|
|
36876
37036
|
spawn_agent: "agent",
|
|
36877
37037
|
spawn_agents: "agent",
|
|
37038
|
+
call_agent: "agent",
|
|
36878
37039
|
todo_write: "agent",
|
|
36879
37040
|
// Named here as a backstop; the sidecar's own decls also carry `namespace`,
|
|
36880
37041
|
// and an explicit field always wins (resolveNamespace). Both say the same
|
|
@@ -36893,7 +37054,17 @@ var BUILTIN_NAMESPACES = {
|
|
|
36893
37054
|
view_strategy: "agent",
|
|
36894
37055
|
run_strategy: "agent",
|
|
36895
37056
|
list_strategies: "agent",
|
|
36896
|
-
list_saved_strategies: "agent"
|
|
37057
|
+
list_saved_strategies: "agent",
|
|
37058
|
+
// Long-lived process control (proc_start/proc_stop/proc_list, see tools/procs.ts).
|
|
37059
|
+
// `shell` is its own subject, deliberately distinct from the one-shot exec's
|
|
37060
|
+
// `sidecar:shell_exec`: a process that stays running is a different risk from
|
|
37061
|
+
// one that returns, and one address lets an operator ban (or allow) both
|
|
37062
|
+
// start and stop together. proc_list is a read - it namespaces as `sidecar`
|
|
37063
|
+
// (like the other machine-state reads) so it is NOT swept up by a `shell:*`
|
|
37064
|
+
// deny; listing what is running carries none of spawn/kill's risk.
|
|
37065
|
+
proc_start: "shell",
|
|
37066
|
+
proc_stop: "shell",
|
|
37067
|
+
proc_list: "sidecar"
|
|
36897
37068
|
};
|
|
36898
37069
|
function resolveNamespace(tool) {
|
|
36899
37070
|
if (tool.namespace) return tool.namespace;
|
|
@@ -36921,6 +37092,7 @@ function toolAddress(tool) {
|
|
|
36921
37092
|
}
|
|
36922
37093
|
}
|
|
36923
37094
|
}
|
|
37095
|
+
if (ns === "shell" && (leaf === "proc_start" || leaf === "proc_stop")) leaf = "spawn";
|
|
36924
37096
|
return `${ns}:${leaf}`;
|
|
36925
37097
|
}
|
|
36926
37098
|
|
|
@@ -36932,8 +37104,12 @@ function effectiveThreshold(mode, fill = 0) {
|
|
|
36932
37104
|
const factor = fill < 0.5 ? 1 : fill < 0.7 ? 0.7 : fill < 0.85 ? 0.5 : 0.35;
|
|
36933
37105
|
return Math.max(FLOOR[mode], Math.round(BASE[mode] * factor));
|
|
36934
37106
|
}
|
|
37107
|
+
var OFFLOAD_MARKER_PREFIX = "[offloaded tool result \xB7 ";
|
|
37108
|
+
function isOffloadPointer(content) {
|
|
37109
|
+
return content.startsWith(OFFLOAD_MARKER_PREFIX);
|
|
37110
|
+
}
|
|
36935
37111
|
function formatOffloadRef(ref) {
|
|
36936
|
-
const head =
|
|
37112
|
+
const head = `${OFFLOAD_MARKER_PREFIX}${ref.toolName} \xB7 ${ref.bytes.toLocaleString()} bytes \xB7 recall the full content with recall_result({ id: "${ref.id}" })]`;
|
|
36937
37113
|
return ref.preview ? `${head}
|
|
36938
37114
|
--- preview ---
|
|
36939
37115
|
${ref.preview}${ref.bytes > ref.preview.length ? "\n\u2026(truncated)" : ""}` : head;
|
|
@@ -36956,7 +37132,9 @@ async function runAgent(args) {
|
|
|
36956
37132
|
const permission = args.permission;
|
|
36957
37133
|
const compression = args.compression;
|
|
36958
37134
|
const ctxWindow = compression?.contextWindow;
|
|
37135
|
+
const compressionStrategy = compression?.strategy ?? "eager";
|
|
36959
37136
|
let fill = 0;
|
|
37137
|
+
const deferredOffloads = [];
|
|
36960
37138
|
const maxTurns = policy.maxTurns ?? args.maxTurns ?? 8;
|
|
36961
37139
|
const signal = args.signal ?? new AbortController().signal;
|
|
36962
37140
|
const emit = (e2) => args.onEvent?.(e2);
|
|
@@ -36973,6 +37151,9 @@ async function runAgent(args) {
|
|
|
36973
37151
|
function: { name: t2.name, description: t2.description, parameters: t2.parameters }
|
|
36974
37152
|
}));
|
|
36975
37153
|
const toolByName = new Map(tools.map((t2) => [t2.name, t2]));
|
|
37154
|
+
if (compressionStrategy === "deferred" && compression && compression.mode !== "off" && toolCtx.recall) {
|
|
37155
|
+
await compactIncomingOffloads(messages, toolByName, compression, toolCtx.recall, emit);
|
|
37156
|
+
}
|
|
36976
37157
|
const usage = { promptTokens: 0, completionTokens: 0 };
|
|
36977
37158
|
let text = "";
|
|
36978
37159
|
let stopReason = "stop";
|
|
@@ -37046,6 +37227,13 @@ async function runAgent(args) {
|
|
|
37046
37227
|
stopReason = "error";
|
|
37047
37228
|
break;
|
|
37048
37229
|
}
|
|
37230
|
+
if (compressionStrategy === "deferred" && deferredOffloads.length) {
|
|
37231
|
+
for (const o2 of deferredOffloads) {
|
|
37232
|
+
const orig = messages[o2.index];
|
|
37233
|
+
if (orig.role === "tool") messages[o2.index] = { ...orig, content: formatOffloadRef(o2.ref) };
|
|
37234
|
+
}
|
|
37235
|
+
deferredOffloads.length = 0;
|
|
37236
|
+
}
|
|
37049
37237
|
usage.promptTokens += streamed.usage.promptTokens;
|
|
37050
37238
|
usage.completionTokens += streamed.usage.completionTokens;
|
|
37051
37239
|
if (ctxWindow && ctxWindow > 0 && streamed.usage.promptTokens) {
|
|
@@ -37121,14 +37309,21 @@ async function runAgent(args) {
|
|
|
37121
37309
|
const decision = await permission.check({ name: tool.name, address, mutates: tool.mutates, permission: tool.permission }, parsed);
|
|
37122
37310
|
emit({ type: "permission", tool: tool.name, decision });
|
|
37123
37311
|
if (decision === "deny") {
|
|
37124
|
-
|
|
37312
|
+
const denialMsg = args.describePermissionDenial?.(tool.name) ?? "Permission denied by the user \u2014 the action was not performed.";
|
|
37313
|
+
messages.push({ role: "tool", tool_call_id: call.id, content: denialMsg });
|
|
37125
37314
|
emit({ type: "tool-result", id: call.id, name: tool.name, result: "permission denied", isError: true });
|
|
37126
37315
|
continue;
|
|
37127
37316
|
}
|
|
37128
37317
|
}
|
|
37129
37318
|
const startedAt = Date.now();
|
|
37319
|
+
let callSettled = false;
|
|
37320
|
+
const stream = (text2) => {
|
|
37321
|
+
if (callSettled) return;
|
|
37322
|
+
emit({ type: "tool-delta", id: call.id, name: call.name, delta: text2 });
|
|
37323
|
+
};
|
|
37130
37324
|
try {
|
|
37131
|
-
const result = await tool.run(parsed, { ...toolCtx, callId: call.id });
|
|
37325
|
+
const result = await tool.run(parsed, { ...toolCtx, callId: call.id, stream });
|
|
37326
|
+
callSettled = true;
|
|
37132
37327
|
const durationMs = Date.now() - startedAt;
|
|
37133
37328
|
gstate.toolCalls++;
|
|
37134
37329
|
gstate.calledTools.add(tool.name);
|
|
@@ -37142,8 +37337,12 @@ async function runAgent(args) {
|
|
|
37142
37337
|
if (result.length > effectiveThreshold(compression.mode, fill)) {
|
|
37143
37338
|
try {
|
|
37144
37339
|
const ref = await toolCtx.recall.record(tool.name, result);
|
|
37145
|
-
out = formatOffloadRef(ref);
|
|
37146
37340
|
emit({ type: "offload", id: ref.id, name: tool.name, bytes: ref.bytes });
|
|
37341
|
+
if (compressionStrategy === "deferred") {
|
|
37342
|
+
deferredOffloads.push({ index: messages.length, ref });
|
|
37343
|
+
} else {
|
|
37344
|
+
out = formatOffloadRef(ref);
|
|
37345
|
+
}
|
|
37147
37346
|
} catch {
|
|
37148
37347
|
out = result;
|
|
37149
37348
|
}
|
|
@@ -37152,6 +37351,7 @@ async function runAgent(args) {
|
|
|
37152
37351
|
messages.push({ role: "tool", tool_call_id: call.id, content: out });
|
|
37153
37352
|
emit({ type: "tool-result", id: call.id, name: call.name, result: out, durationMs });
|
|
37154
37353
|
} catch (e2) {
|
|
37354
|
+
callSettled = true;
|
|
37155
37355
|
const errMsg = String(e2?.message || e2);
|
|
37156
37356
|
const kind2 = classifyToolError(errMsg);
|
|
37157
37357
|
const attempt = toolFails.fail(tool.name, kind2);
|
|
@@ -37175,6 +37375,30 @@ async function runAgent(args) {
|
|
|
37175
37375
|
emit({ type: "done", reason: stopReason });
|
|
37176
37376
|
return { messages, text, usage, stopReason };
|
|
37177
37377
|
}
|
|
37378
|
+
async function compactIncomingOffloads(messages, toolByName, compression, recall, emit) {
|
|
37379
|
+
const nameForCallId = /* @__PURE__ */ new Map();
|
|
37380
|
+
for (const m2 of messages) {
|
|
37381
|
+
if (m2.role === "assistant" && m2.tool_calls) {
|
|
37382
|
+
for (const c2 of m2.tool_calls) nameForCallId.set(c2.id, c2.function.name);
|
|
37383
|
+
}
|
|
37384
|
+
}
|
|
37385
|
+
for (let i2 = 0; i2 < messages.length; i2++) {
|
|
37386
|
+
const m2 = messages[i2];
|
|
37387
|
+
if (m2.role !== "tool" || typeof m2.content !== "string") continue;
|
|
37388
|
+
if (isOffloadPointer(m2.content)) continue;
|
|
37389
|
+
const toolName = nameForCallId.get(m2.tool_call_id);
|
|
37390
|
+
if (!toolName) continue;
|
|
37391
|
+
const tool = toolByName.get(toolName);
|
|
37392
|
+
if (!tool || tool.noOffload) continue;
|
|
37393
|
+
if (m2.content.length <= effectiveThreshold(compression.mode, 0)) continue;
|
|
37394
|
+
try {
|
|
37395
|
+
const ref = await recall.record(toolName, m2.content);
|
|
37396
|
+
messages[i2] = { ...m2, content: formatOffloadRef(ref) };
|
|
37397
|
+
emit({ type: "offload", id: ref.id, name: toolName, bytes: ref.bytes });
|
|
37398
|
+
} catch {
|
|
37399
|
+
}
|
|
37400
|
+
}
|
|
37401
|
+
}
|
|
37178
37402
|
function reconcileDanglingToolCalls(messages) {
|
|
37179
37403
|
let idx = -1;
|
|
37180
37404
|
for (let i2 = messages.length - 1; i2 >= 0; i2--) {
|
|
@@ -39377,6 +39601,114 @@ async function runHostsCli(argv) {
|
|
|
39377
39601
|
|
|
39378
39602
|
// src/sessionsCli.ts
|
|
39379
39603
|
import { resolve as resolve6 } from "node:path";
|
|
39604
|
+
|
|
39605
|
+
// ../harness/sessions/fileRecallStore.ts
|
|
39606
|
+
var UNATTACHED_SESSION = "_unattached";
|
|
39607
|
+
|
|
39608
|
+
// ../harness/sessions/blobGc.ts
|
|
39609
|
+
var DEFAULT_UNATTACHED_GRACE_MS = 30 * 60 * 1e3;
|
|
39610
|
+
var MARKER_ID_RE = /recall_result\(\{\s*id:\s*\\?"([^"\\]+)\\?"\s*\}\)/g;
|
|
39611
|
+
async function globalReferencedIds(files, root, sessionDirs) {
|
|
39612
|
+
const ids = /* @__PURE__ */ new Set();
|
|
39613
|
+
for (const dir of sessionDirs) {
|
|
39614
|
+
if (!dir.hasSessionLog) continue;
|
|
39615
|
+
const text = await files.read(`${root}/${dir.id}/SESSION.jsonl`).catch(() => null);
|
|
39616
|
+
if (text == null) continue;
|
|
39617
|
+
for (const m2 of text.matchAll(MARKER_ID_RE)) {
|
|
39618
|
+
const id = m2[1];
|
|
39619
|
+
if (id) ids.add(id);
|
|
39620
|
+
}
|
|
39621
|
+
}
|
|
39622
|
+
return ids;
|
|
39623
|
+
}
|
|
39624
|
+
async function listSessionDirs(files, root) {
|
|
39625
|
+
const nodes = await files.list(root).catch(() => []);
|
|
39626
|
+
const flat = flattenFiles(nodes).filter((f2) => f2.path.startsWith(`${root}/`));
|
|
39627
|
+
const dirs = /* @__PURE__ */ new Map();
|
|
39628
|
+
for (const f2 of flat) {
|
|
39629
|
+
const rest = f2.path.slice(root.length + 1);
|
|
39630
|
+
const slash = rest.indexOf("/");
|
|
39631
|
+
if (slash < 0) continue;
|
|
39632
|
+
const id = rest.slice(0, slash);
|
|
39633
|
+
const isSessionLog = rest === `${id}/SESSION.jsonl`;
|
|
39634
|
+
if (!dirs.has(id)) dirs.set(id, false);
|
|
39635
|
+
if (isSessionLog) dirs.set(id, true);
|
|
39636
|
+
}
|
|
39637
|
+
return [...dirs.entries()].map(([id, hasSessionLog]) => ({ id, hasSessionLog }));
|
|
39638
|
+
}
|
|
39639
|
+
async function listBlobs(files, root, sessionId) {
|
|
39640
|
+
const dir = `${root}/${sessionId}/blobs`;
|
|
39641
|
+
const nodes = await files.list(dir).catch(() => []);
|
|
39642
|
+
const blobFiles = flattenFiles(nodes).filter((f2) => f2.path.startsWith(`${dir}/`) && f2.path.endsWith(".json"));
|
|
39643
|
+
const out = [];
|
|
39644
|
+
for (const f2 of blobFiles) {
|
|
39645
|
+
const text = await files.read(f2.path).catch(() => null);
|
|
39646
|
+
if (text == null) continue;
|
|
39647
|
+
try {
|
|
39648
|
+
const record = JSON.parse(text);
|
|
39649
|
+
if (record && typeof record.id === "string") out.push({ path: f2.path, record });
|
|
39650
|
+
} catch {
|
|
39651
|
+
}
|
|
39652
|
+
}
|
|
39653
|
+
return out;
|
|
39654
|
+
}
|
|
39655
|
+
async function planBlobGc(files, opts = {}) {
|
|
39656
|
+
const root = opts.root ?? sessionsRoot();
|
|
39657
|
+
const now2 = (opts.now ?? Date.now)();
|
|
39658
|
+
const graceMs = opts.graceMs ?? DEFAULT_UNATTACHED_GRACE_MS;
|
|
39659
|
+
const sessionDirs = await listSessionDirs(files, root);
|
|
39660
|
+
const referenced = await globalReferencedIds(files, root, sessionDirs);
|
|
39661
|
+
const reapable = [];
|
|
39662
|
+
const liveUnreferenced = [];
|
|
39663
|
+
for (const dir of sessionDirs) {
|
|
39664
|
+
const blobs = await listBlobs(files, root, dir.id);
|
|
39665
|
+
for (const { path, record } of blobs) {
|
|
39666
|
+
const isReferenced = referenced.has(record.id);
|
|
39667
|
+
const base = {
|
|
39668
|
+
sessionId: dir.id,
|
|
39669
|
+
id: record.id,
|
|
39670
|
+
path,
|
|
39671
|
+
bytes: record.bytes,
|
|
39672
|
+
toolName: record.toolName,
|
|
39673
|
+
createdAt: record.createdAt
|
|
39674
|
+
};
|
|
39675
|
+
if (isReferenced) continue;
|
|
39676
|
+
if (dir.id === UNATTACHED_SESSION) {
|
|
39677
|
+
const age = now2 - record.createdAt;
|
|
39678
|
+
if (age < graceMs) continue;
|
|
39679
|
+
reapable.push({
|
|
39680
|
+
...base,
|
|
39681
|
+
disposition: "reap",
|
|
39682
|
+
reason: `unattached, unreferenced, and ${Math.round(age / 6e4)}m old (grace is ${Math.round(graceMs / 6e4)}m)`
|
|
39683
|
+
});
|
|
39684
|
+
continue;
|
|
39685
|
+
}
|
|
39686
|
+
if (!dir.hasSessionLog) {
|
|
39687
|
+
reapable.push({ ...base, disposition: "reap", reason: "owning session directory has no SESSION.jsonl" });
|
|
39688
|
+
continue;
|
|
39689
|
+
}
|
|
39690
|
+
liveUnreferenced.push({ ...base, disposition: "report-only", reason: "unreferenced, but the owning session is live \u2014 not deleted by this tool" });
|
|
39691
|
+
}
|
|
39692
|
+
}
|
|
39693
|
+
return {
|
|
39694
|
+
reapable,
|
|
39695
|
+
liveUnreferenced,
|
|
39696
|
+
reapableBytes: reapable.reduce((sum, e2) => sum + e2.bytes, 0),
|
|
39697
|
+
liveUnreferencedBytes: liveUnreferenced.reduce((sum, e2) => sum + e2.bytes, 0)
|
|
39698
|
+
};
|
|
39699
|
+
}
|
|
39700
|
+
async function runBlobGc(files, opts = {}) {
|
|
39701
|
+
const plan = await planBlobGc(files, opts);
|
|
39702
|
+
if (!opts.delete) return { plan, removed: [] };
|
|
39703
|
+
const removed = [];
|
|
39704
|
+
for (const entry of plan.reapable) {
|
|
39705
|
+
await files.remove(entry.path).catch(() => void 0);
|
|
39706
|
+
removed.push(entry);
|
|
39707
|
+
}
|
|
39708
|
+
return { plan, removed };
|
|
39709
|
+
}
|
|
39710
|
+
|
|
39711
|
+
// src/sessionsCli.ts
|
|
39380
39712
|
async function sandboxLifecycle(run, verb, name) {
|
|
39381
39713
|
const call = async (args) => {
|
|
39382
39714
|
const r2 = await run(args).catch((e2) => ({ code: 1, stdout: "", stderr: String(e2) }));
|
|
@@ -39448,6 +39780,31 @@ function formatSession(r2) {
|
|
|
39448
39780
|
...r2.repo ? [`repo ${r2.repo}`] : []
|
|
39449
39781
|
].join("\n");
|
|
39450
39782
|
}
|
|
39783
|
+
function formatBlobGcReport(result, deleted) {
|
|
39784
|
+
const line = (e2) => ` ${e2.path} ${formatByteSize(e2.bytes)} (${e2.reason})`;
|
|
39785
|
+
const lines = [];
|
|
39786
|
+
const { plan } = result;
|
|
39787
|
+
if (!plan.reapable.length && !plan.liveUnreferenced.length) {
|
|
39788
|
+
lines.push("no unattached blobs found.");
|
|
39789
|
+
return lines.join("\n");
|
|
39790
|
+
}
|
|
39791
|
+
if (plan.reapable.length) {
|
|
39792
|
+
lines.push(deleted ? `removed ${result.removed.length} blob(s), ${formatByteSize(plan.reapableBytes)} freed:` : `would remove ${plan.reapable.length} blob(s), ${formatByteSize(plan.reapableBytes)} total:`);
|
|
39793
|
+
lines.push(...plan.reapable.map(line));
|
|
39794
|
+
} else {
|
|
39795
|
+
lines.push("nothing is reap-eligible.");
|
|
39796
|
+
}
|
|
39797
|
+
if (plan.liveUnreferenced.length) {
|
|
39798
|
+
lines.push("");
|
|
39799
|
+
lines.push(`${plan.liveUnreferenced.length} unreferenced blob(s) in live sessions, ${formatByteSize(plan.liveUnreferencedBytes)} total (not removed by this tool):`);
|
|
39800
|
+
lines.push(...plan.liveUnreferenced.map(line));
|
|
39801
|
+
}
|
|
39802
|
+
if (!deleted && plan.reapable.length) {
|
|
39803
|
+
lines.push("");
|
|
39804
|
+
lines.push("pass --delete to remove the first list. Nothing above was touched.");
|
|
39805
|
+
}
|
|
39806
|
+
return lines.join("\n");
|
|
39807
|
+
}
|
|
39451
39808
|
var DEFAULT_GUEST_IMAGE = "corenel-guest:dev";
|
|
39452
39809
|
function backendFor(deps, roots, run) {
|
|
39453
39810
|
return makeMicrovmBackend({
|
|
@@ -39619,13 +39976,28 @@ async function runSessionsCli(argv, deps) {
|
|
|
39619
39976
|
return 0;
|
|
39620
39977
|
} catch (e2) {
|
|
39621
39978
|
err(`corenel sessions ${verb}: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
39979
|
+
`);
|
|
39980
|
+
return 1;
|
|
39981
|
+
}
|
|
39982
|
+
}
|
|
39983
|
+
if (verb === "gc") {
|
|
39984
|
+
const root = rootFrom(argv, process.cwd());
|
|
39985
|
+
const doDelete = argv.includes("--delete");
|
|
39986
|
+
try {
|
|
39987
|
+
const files = new NodeFileService(root);
|
|
39988
|
+
const result = await runBlobGc(files, { delete: doDelete });
|
|
39989
|
+
out(`${formatBlobGcReport(result, doDelete)}
|
|
39990
|
+
`);
|
|
39991
|
+
return 0;
|
|
39992
|
+
} catch (e2) {
|
|
39993
|
+
err(`corenel sessions gc: ${e2 instanceof Error ? e2.message : String(e2)}
|
|
39622
39994
|
`);
|
|
39623
39995
|
return 1;
|
|
39624
39996
|
}
|
|
39625
39997
|
}
|
|
39626
39998
|
err(`corenel sessions: unknown subcommand "${verb}"
|
|
39627
39999
|
usage: corenel sessions [list | show <id> | create [--root <dir>] | run <id> "<prompt>"
|
|
39628
|
-
| pause <id> | resume <id> | destroy <id>]
|
|
40000
|
+
| pause <id> | resume <id> | destroy <id> | gc [--delete]]
|
|
39629
40001
|
`);
|
|
39630
40002
|
return 1;
|
|
39631
40003
|
}
|
|
@@ -39664,6 +40036,7 @@ var FLAGS = [
|
|
|
39664
40036
|
{ flag: "--isolation", arg: "<none|microvm>", help: "How a session confines a run: none (this process) or microvm (a sandbox per session, needs msb). Default none." },
|
|
39665
40037
|
{ flag: "--msb", arg: "<path>", help: "Path to the msb binary, for --isolation microvm." },
|
|
39666
40038
|
{ flag: "--guest-image", arg: "<ref>", help: "Image a session boots (default corenel-guest:dev). Pin by digest for anything snapshotted." },
|
|
40039
|
+
{ flag: "--snapshot-budget", arg: "<size>", help: "Ceiling on TOTAL bytes used by session snapshots (default 32G). A capture that would exceed it is refused, never pruned; size accepts K/M/G/T suffixes." },
|
|
39667
40040
|
{ flag: "--clone-under", arg: "<dir>", help: "Let a session clone a repository into <dir>. Repeatable. Off by default; the clone runs on THIS host with your own git credentials, which never enter the session." },
|
|
39668
40041
|
{ flag: "--keep-transcripts", help: "PERMIT session runs to keep their words, not just metadata. Off by default; each run still has to ask, and none can ask past this." },
|
|
39669
40042
|
{ flag: "--acp", arg: "<target>", help: "Proxy an external ACP agent (claude-code). Needs --root." },
|
|
@@ -39792,6 +40165,7 @@ function parseArgs(argv) {
|
|
|
39792
40165
|
isolation: "none",
|
|
39793
40166
|
msbBin: process.env.MSB_BIN ?? "msb",
|
|
39794
40167
|
guestImage: "corenel-guest:dev",
|
|
40168
|
+
snapshotBudgetBytes: DEFAULT_SNAPSHOT_BUDGET_BYTES,
|
|
39795
40169
|
selfSigned: false,
|
|
39796
40170
|
open: false,
|
|
39797
40171
|
appUrl: "https://corenel.ai",
|
|
@@ -39828,7 +40202,15 @@ function parseArgs(argv) {
|
|
|
39828
40202
|
out.isolation = v2;
|
|
39829
40203
|
} else if (a2 === "--msb") out.msbBin = argv[++i2];
|
|
39830
40204
|
else if (a2 === "--guest-image") out.guestImage = argv[++i2];
|
|
39831
|
-
else if (a2 === "--
|
|
40205
|
+
else if (a2 === "--snapshot-budget") {
|
|
40206
|
+
const v2 = argv[++i2];
|
|
40207
|
+
if (v2 === void 0) throw new Error('--snapshot-budget needs a size, e.g. "32G"');
|
|
40208
|
+
try {
|
|
40209
|
+
out.snapshotBudgetBytes = parseByteSize(v2);
|
|
40210
|
+
} catch (e2) {
|
|
40211
|
+
throw new Error(`--snapshot-budget: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
40212
|
+
}
|
|
40213
|
+
} else if (a2 === "--allow-lan") out.allowLan = true;
|
|
39832
40214
|
else if (a2 === "--tls-cert") out.tlsCert = resolve7(argv[++i2]);
|
|
39833
40215
|
else if (a2 === "--tls-key") out.tlsKey = resolve7(argv[++i2]);
|
|
39834
40216
|
else if (a2 === "--self-signed") out.selfSigned = true;
|
|
@@ -41562,6 +41944,7 @@ async function main() {
|
|
|
41562
41944
|
runner,
|
|
41563
41945
|
image: args.guestImage,
|
|
41564
41946
|
msbBin: args.msbBin,
|
|
41947
|
+
snapshotBudgetBytes: args.snapshotBudgetBytes,
|
|
41565
41948
|
log: slog,
|
|
41566
41949
|
// Metadata for every session run, filed under the CALLER'S own root. The
|
|
41567
41950
|
// runner service owns owner resolution (a wire-supplied clientId is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corenel/sidecar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Elastic-2.0",
|
|
6
6
|
"description": "Prompd sidecar — a local capability server. Run it next to Prompd (prompd.app) so the browser app can reach your machine: local LLMs (Ollama/LM Studio/vLLM) without CORS, your filesystem, and (opt-in) shell.",
|
|
@@ -36,15 +36,18 @@
|
|
|
36
36
|
"esbuild": "^0.21.5",
|
|
37
37
|
"tsx": "^4.19.2",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
|
-
"@corenel/protocol": "0.
|
|
40
|
-
"@corenel/
|
|
41
|
-
"@corenel/
|
|
42
|
-
"@corenel/
|
|
39
|
+
"@corenel/protocol": "0.4.0",
|
|
40
|
+
"@corenel/tools-node": "0.2.1",
|
|
41
|
+
"@corenel/crew": "0.0.0",
|
|
42
|
+
"@corenel/harness": "0.4.0"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
+
"prebuild": "pnpm --filter @corenel/protocol build && pnpm --filter @corenel/harness build",
|
|
45
46
|
"build": "node build.mjs",
|
|
47
|
+
"prestart": "pnpm --filter @corenel/protocol build && pnpm --filter @corenel/harness build",
|
|
46
48
|
"start": "tsx src/cli.ts",
|
|
47
49
|
"typecheck": "tsc --noEmit",
|
|
50
|
+
"preverify": "pnpm --filter @corenel/protocol build && pnpm --filter @corenel/harness build",
|
|
48
51
|
"verify": "tsx src/verify.ts"
|
|
49
52
|
}
|
|
50
53
|
}
|