@theaileverage/marionette 0.2.1 → 0.2.2
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 +7 -0
- package/DESIGN.md +3 -3
- package/ORCHESTRATION.md +2 -2
- package/README.md +19 -4
- package/THIRD_PARTY_NOTICES.md +208 -0
- package/VERIFICATION.md +8 -0
- package/dist/cli.js +789 -121
- package/dist/herdr-protocol.d.ts +1916 -0
- package/dist/herdr-protocol.js +108 -0
- package/dist/herdr-sdk.d.ts +105 -0
- package/dist/herdr-sdk.js +105 -0
- package/dist/herdr-streams.d.ts +49 -0
- package/dist/herdr-streams.js +156 -0
- package/dist/herdr-transport.d.ts +50 -0
- package/dist/herdr-transport.js +232 -0
- package/dist/mcp.js +1 -1
- package/package.json +22 -5
- package/public/assets/{index-BCi4LMak.js → index-CCfdv-uF.js} +1 -1
- package/public/index.html +1 -1
- package/skills/marionette/SKILL.md +70 -0
- package/skills/marionette/references/coordination.md +63 -0
- package/skills/marionette/references/herdr-sdk.md +147 -0
- package/vendor/herdr-0.9.0/LICENSE +201 -0
- package/vendor/herdr-0.9.0/README.md +5 -0
package/dist/cli.js
CHANGED
|
@@ -886,15 +886,15 @@ var require_depd = __commonJS({
|
|
|
886
886
|
return relative5(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
|
|
887
887
|
}
|
|
888
888
|
function getStack() {
|
|
889
|
-
var
|
|
889
|
+
var limit2 = Error.stackTraceLimit;
|
|
890
890
|
var obj = {};
|
|
891
891
|
var prep = Error.prepareStackTrace;
|
|
892
892
|
Error.prepareStackTrace = prepareObjectStackTrace;
|
|
893
|
-
Error.stackTraceLimit = Math.max(10,
|
|
893
|
+
Error.stackTraceLimit = Math.max(10, limit2);
|
|
894
894
|
Error.captureStackTrace(obj);
|
|
895
895
|
var stack = obj.stack.slice(1);
|
|
896
896
|
Error.prepareStackTrace = prep;
|
|
897
|
-
Error.stackTraceLimit =
|
|
897
|
+
Error.stackTraceLimit = limit2;
|
|
898
898
|
return stack;
|
|
899
899
|
}
|
|
900
900
|
function prepareObjectStackTrace(obj, stack) {
|
|
@@ -5341,13 +5341,13 @@ var require_raw_body = __commonJS({
|
|
|
5341
5341
|
throw new TypeError("argument callback is required");
|
|
5342
5342
|
}
|
|
5343
5343
|
var encoding = opts.encoding !== true ? opts.encoding : "utf-8";
|
|
5344
|
-
var
|
|
5344
|
+
var limit2 = bytes.parse(opts.limit);
|
|
5345
5345
|
var length = opts.length != null && !isNaN(opts.length) ? parseInt(opts.length, 10) : null;
|
|
5346
5346
|
if (done) {
|
|
5347
|
-
return readStream(stream, encoding, length,
|
|
5347
|
+
return readStream(stream, encoding, length, limit2, wrap(done));
|
|
5348
5348
|
}
|
|
5349
5349
|
return new Promise(function executor(resolve13, reject) {
|
|
5350
|
-
readStream(stream, encoding, length,
|
|
5350
|
+
readStream(stream, encoding, length, limit2, function onRead(err, buf) {
|
|
5351
5351
|
if (err) return reject(err);
|
|
5352
5352
|
resolve13(buf);
|
|
5353
5353
|
});
|
|
@@ -5359,14 +5359,14 @@ var require_raw_body = __commonJS({
|
|
|
5359
5359
|
stream.pause();
|
|
5360
5360
|
}
|
|
5361
5361
|
}
|
|
5362
|
-
function readStream(stream, encoding, length,
|
|
5362
|
+
function readStream(stream, encoding, length, limit2, callback) {
|
|
5363
5363
|
var complete = false;
|
|
5364
5364
|
var sync = true;
|
|
5365
|
-
if (
|
|
5365
|
+
if (limit2 !== null && length !== null && length > limit2) {
|
|
5366
5366
|
return done(createError(413, "request entity too large", {
|
|
5367
5367
|
expected: length,
|
|
5368
5368
|
length,
|
|
5369
|
-
limit,
|
|
5369
|
+
limit: limit2,
|
|
5370
5370
|
type: "entity.too.large"
|
|
5371
5371
|
}));
|
|
5372
5372
|
}
|
|
@@ -5427,9 +5427,9 @@ var require_raw_body = __commonJS({
|
|
|
5427
5427
|
function onData(chunk) {
|
|
5428
5428
|
if (complete) return;
|
|
5429
5429
|
received += chunk.length;
|
|
5430
|
-
if (
|
|
5430
|
+
if (limit2 !== null && received > limit2) {
|
|
5431
5431
|
done(createError(413, "request entity too large", {
|
|
5432
|
-
limit,
|
|
5432
|
+
limit: limit2,
|
|
5433
5433
|
received,
|
|
5434
5434
|
type: "entity.too.large"
|
|
5435
5435
|
}));
|
|
@@ -15608,11 +15608,11 @@ var require_utils = __commonJS({
|
|
|
15608
15608
|
throw new TypeError("defaultType must be provided");
|
|
15609
15609
|
}
|
|
15610
15610
|
const inflate = options?.inflate !== false;
|
|
15611
|
-
const
|
|
15611
|
+
const limit2 = typeof options?.limit === "undefined" || options?.limit === null ? 102400 : bytes.parse(options.limit);
|
|
15612
15612
|
const type = options?.type || defaultType;
|
|
15613
15613
|
const verify = options?.verify || false;
|
|
15614
15614
|
const defaultCharset = options?.defaultCharset || "utf-8";
|
|
15615
|
-
if (
|
|
15615
|
+
if (limit2 === null) {
|
|
15616
15616
|
throw new TypeError(`option limit "${String(options.limit)}" is invalid`);
|
|
15617
15617
|
}
|
|
15618
15618
|
if (verify !== false && typeof verify !== "function") {
|
|
@@ -15621,7 +15621,7 @@ var require_utils = __commonJS({
|
|
|
15621
15621
|
const shouldParse = typeof type !== "function" ? typeChecker(type) : type;
|
|
15622
15622
|
return {
|
|
15623
15623
|
inflate,
|
|
15624
|
-
limit,
|
|
15624
|
+
limit: limit2,
|
|
15625
15625
|
verify,
|
|
15626
15626
|
defaultCharset,
|
|
15627
15627
|
shouldParse
|
|
@@ -17756,7 +17756,7 @@ var require_utils2 = __commonJS({
|
|
|
17756
17756
|
return strWithoutPlus;
|
|
17757
17757
|
}
|
|
17758
17758
|
};
|
|
17759
|
-
var
|
|
17759
|
+
var limit2 = 1024;
|
|
17760
17760
|
var encode = function encode2(str, defaultEncoder, charset, kind, format) {
|
|
17761
17761
|
if (str.length === 0) {
|
|
17762
17762
|
return str;
|
|
@@ -17773,9 +17773,9 @@ var require_utils2 = __commonJS({
|
|
|
17773
17773
|
});
|
|
17774
17774
|
}
|
|
17775
17775
|
var out = "";
|
|
17776
|
-
for (var j = 0; j < string.length; j +=
|
|
17777
|
-
var segment = string.length >=
|
|
17778
|
-
if (j +
|
|
17776
|
+
for (var j = 0; j < string.length; j += limit2) {
|
|
17777
|
+
var segment = string.length >= limit2 ? string.slice(j, j + limit2) : string;
|
|
17778
|
+
if (j + limit2 < string.length) {
|
|
17779
17779
|
var last = segment.charCodeAt(segment.length - 1);
|
|
17780
17780
|
if (last >= 55296 && last <= 56319) {
|
|
17781
17781
|
segment = segment.slice(0, -1);
|
|
@@ -18246,13 +18246,13 @@ var require_parse = __commonJS({
|
|
|
18246
18246
|
var obj = { __proto__: null };
|
|
18247
18247
|
var cleanStr = options.ignoreQueryPrefix ? str.replace(/^\?/, "") : str;
|
|
18248
18248
|
cleanStr = cleanStr.replace(/%5B/gi, "[").replace(/%5D/gi, "]");
|
|
18249
|
-
var
|
|
18249
|
+
var limit2 = options.parameterLimit === Infinity ? void 0 : options.parameterLimit;
|
|
18250
18250
|
var parts = cleanStr.split(
|
|
18251
18251
|
options.delimiter,
|
|
18252
|
-
options.throwOnLimitExceeded && typeof
|
|
18252
|
+
options.throwOnLimitExceeded && typeof limit2 !== "undefined" ? limit2 + 1 : limit2
|
|
18253
18253
|
);
|
|
18254
|
-
if (options.throwOnLimitExceeded && typeof
|
|
18255
|
-
throw new RangeError("Parameter limit exceeded. Only " +
|
|
18254
|
+
if (options.throwOnLimitExceeded && typeof limit2 !== "undefined" && parts.length > limit2) {
|
|
18255
|
+
throw new RangeError("Parameter limit exceeded. Only " + limit2 + " parameter" + (limit2 === 1 ? "" : "s") + " allowed.");
|
|
18256
18256
|
}
|
|
18257
18257
|
var skipIndex = -1;
|
|
18258
18258
|
var i;
|
|
@@ -18600,12 +18600,12 @@ var require_urlencoded = __commonJS({
|
|
|
18600
18600
|
}
|
|
18601
18601
|
};
|
|
18602
18602
|
}
|
|
18603
|
-
function parameterCount(body,
|
|
18603
|
+
function parameterCount(body, limit2) {
|
|
18604
18604
|
let count = 0;
|
|
18605
18605
|
let index = -1;
|
|
18606
18606
|
do {
|
|
18607
18607
|
count++;
|
|
18608
|
-
if (count >
|
|
18608
|
+
if (count > limit2) return void 0;
|
|
18609
18609
|
index = body.indexOf("&", index + 1);
|
|
18610
18610
|
} while (index !== -1);
|
|
18611
18611
|
return count;
|
|
@@ -28278,8 +28278,8 @@ var Store = class {
|
|
|
28278
28278
|
const r = this.db.prepare("INSERT INTO events(project_id,data) VALUES(?,?)").run(projectId, JSON.stringify(event));
|
|
28279
28279
|
return { ...event, id: Number(r.lastInsertRowid) };
|
|
28280
28280
|
}
|
|
28281
|
-
events(projectId, after = 0,
|
|
28282
|
-
return this.db.prepare("SELECT id,data FROM events WHERE project_id=? AND id>? ORDER BY id LIMIT ?").all(projectId, after,
|
|
28281
|
+
events(projectId, after = 0, limit2 = 200) {
|
|
28282
|
+
return this.db.prepare("SELECT id,data FROM events WHERE project_id=? AND id>? ORDER BY id LIMIT ?").all(projectId, after, limit2).map((r) => ({ ...JSON.parse(r.data), id: r.id }));
|
|
28283
28283
|
}
|
|
28284
28284
|
close() {
|
|
28285
28285
|
this.db.close();
|
|
@@ -28289,7 +28289,7 @@ var Store = class {
|
|
|
28289
28289
|
// src/service.ts
|
|
28290
28290
|
import { randomUUID as randomUUID6, randomBytes as randomBytes2 } from "node:crypto";
|
|
28291
28291
|
import { realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
|
|
28292
|
-
import { isAbsolute as
|
|
28292
|
+
import { isAbsolute as isAbsolute4 } from "node:path";
|
|
28293
28293
|
|
|
28294
28294
|
// src/continuation.ts
|
|
28295
28295
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -29163,6 +29163,7 @@ var Cleanup = class {
|
|
|
29163
29163
|
runs: runs.map((r) => ({
|
|
29164
29164
|
runId: r.id,
|
|
29165
29165
|
tabId: r.tabId,
|
|
29166
|
+
paneId: r.paneId,
|
|
29166
29167
|
cleanup: r.cleanup,
|
|
29167
29168
|
reasons: this.runReasons(t, r, true)
|
|
29168
29169
|
})),
|
|
@@ -29193,7 +29194,7 @@ var Cleanup = class {
|
|
|
29193
29194
|
this.saveRun(r, {
|
|
29194
29195
|
...r.cleanup,
|
|
29195
29196
|
state: "uncertain",
|
|
29196
|
-
error: "Restart during
|
|
29197
|
+
error: "Restart during terminal closure; inspect and reconcile. No automatic replay."
|
|
29197
29198
|
});
|
|
29198
29199
|
}
|
|
29199
29200
|
async stop() {
|
|
@@ -29246,7 +29247,7 @@ var Cleanup = class {
|
|
|
29246
29247
|
const tab = (await h.call("tab.get", { tab_id: r.tabId })).tab;
|
|
29247
29248
|
const panes = (await h.call("pane.list", { workspace_id: p.workspaceId })).panes;
|
|
29248
29249
|
const members = panes?.filter((pane) => pane.tab_id === r.tabId);
|
|
29249
|
-
if (tab?.workspace_id !== p.workspaceId || tab.tab_id !== r.tabId || tab.pane_count !== 1 || members?.length !== 1 || members
|
|
29250
|
+
if (tab?.workspace_id !== p.workspaceId || tab.tab_id !== r.tabId || !r.terminalScope && (tab.pane_count !== 1 || members?.length !== 1) || members?.filter((pane) => pane.pane_id === r.paneId && pane.terminal_id === r.terminalId).length !== 1)
|
|
29250
29251
|
refuse("Tab contents changed; refusing to close another pane");
|
|
29251
29252
|
const a = (await h.call("agent.get", { target: r.paneId })).agent;
|
|
29252
29253
|
if (a?.workspace_id !== p.workspaceId || a.pane_id !== r.paneId || a.terminal_id !== r.terminalId || a.name !== r.agentName || a.agent !== r.kind || a.agent_session?.value !== r.nativeSession || !["idle", "done"].includes(a.agent_status) || a.launch_pending || a.interactive_ready === false)
|
|
@@ -29255,6 +29256,17 @@ var Cleanup = class {
|
|
|
29255
29256
|
}
|
|
29256
29257
|
async absent(t, r) {
|
|
29257
29258
|
const h = this.s.port(this.s.project(t.projectId));
|
|
29259
|
+
if (r.terminalScope === "pane") {
|
|
29260
|
+
const panes2 = (await h.call("pane.list")).panes;
|
|
29261
|
+
if (!Array.isArray(panes2)) refuse("Cannot inspect worker terminal membership");
|
|
29262
|
+
if (panes2.some(
|
|
29263
|
+
(pane) => (pane.pane_id === r.paneId || pane.terminal_id === r.terminalId) && (pane.pane_id !== r.paneId || pane.tab_id !== r.tabId || pane.terminal_id !== r.terminalId)
|
|
29264
|
+
))
|
|
29265
|
+
refuse("Original terminal may have moved; inspect it before cleanup");
|
|
29266
|
+
return !panes2.some(
|
|
29267
|
+
(pane) => pane.pane_id === r.paneId || pane.terminal_id === r.terminalId
|
|
29268
|
+
);
|
|
29269
|
+
}
|
|
29258
29270
|
try {
|
|
29259
29271
|
await h.call("tab.get", { tab_id: r.tabId });
|
|
29260
29272
|
return false;
|
|
@@ -29279,7 +29291,7 @@ var Cleanup = class {
|
|
|
29279
29291
|
}) {
|
|
29280
29292
|
if (r.cleanup?.state === "closed") return r.cleanup;
|
|
29281
29293
|
if (r.cleanup && ["closing", "uncertain"].includes(r.cleanup.state))
|
|
29282
|
-
refuse("Reconcile ambiguous
|
|
29294
|
+
refuse("Reconcile ambiguous terminal closure first");
|
|
29283
29295
|
try {
|
|
29284
29296
|
const reasons = this.runReasons(this.s.task(t.id), r, automatic);
|
|
29285
29297
|
if (reasons.length) refuse(reasons.join("; "));
|
|
@@ -29306,12 +29318,13 @@ var Cleanup = class {
|
|
|
29306
29318
|
updatedAt: now(),
|
|
29307
29319
|
output: String(output).slice(-1e5)
|
|
29308
29320
|
});
|
|
29309
|
-
await h.call("
|
|
29321
|
+
if (r.terminalScope === "pane") await h.call("pane.close", { pane_id: r.paneId });
|
|
29322
|
+
else await h.call("tab.close", { tab_id: r.tabId });
|
|
29310
29323
|
this.saveRun(r, { ...this.s.store.get("run", r.id).cleanup, state: "closed" });
|
|
29311
29324
|
this.s.store.event(
|
|
29312
29325
|
t.projectId,
|
|
29313
29326
|
"cleanup.released",
|
|
29314
|
-
"Saved worker diagnostics and closed its settled
|
|
29327
|
+
"Saved worker diagnostics and closed its settled terminal",
|
|
29315
29328
|
t.id,
|
|
29316
29329
|
{ runId: r.id }
|
|
29317
29330
|
);
|
|
@@ -29609,7 +29622,7 @@ var Cleanup = class {
|
|
|
29609
29622
|
this.saveRun(r, { ...r.cleanup, state: "closed", reason, error: void 0 });
|
|
29610
29623
|
return this.s.store.get("run", r.id).cleanup;
|
|
29611
29624
|
}
|
|
29612
|
-
refuse("Original
|
|
29625
|
+
refuse("Original worker terminal is still present");
|
|
29613
29626
|
}
|
|
29614
29627
|
await this.pinned(t, r);
|
|
29615
29628
|
guard();
|
|
@@ -31267,68 +31280,620 @@ var Orchestration = class {
|
|
|
31267
31280
|
}
|
|
31268
31281
|
};
|
|
31269
31282
|
|
|
31270
|
-
// src/herdr.ts
|
|
31283
|
+
// src/herdr-protocol.ts
|
|
31284
|
+
var HERDR_METHODS = [
|
|
31285
|
+
"ping",
|
|
31286
|
+
"server.stop",
|
|
31287
|
+
"server.live_handoff",
|
|
31288
|
+
"server.reload_config",
|
|
31289
|
+
"server.agent_manifests",
|
|
31290
|
+
"server.reload_agent_manifests",
|
|
31291
|
+
"notification.show",
|
|
31292
|
+
"product_announcement.dismiss",
|
|
31293
|
+
"release_notes.dismiss",
|
|
31294
|
+
"command.invoke",
|
|
31295
|
+
"client.window_title.set",
|
|
31296
|
+
"client.window_title.clear",
|
|
31297
|
+
"client_shell.surface.set",
|
|
31298
|
+
"session.snapshot",
|
|
31299
|
+
"workspace.create",
|
|
31300
|
+
"workspace.list",
|
|
31301
|
+
"workspace.get",
|
|
31302
|
+
"workspace.focus",
|
|
31303
|
+
"workspace.rename",
|
|
31304
|
+
"workspace.move",
|
|
31305
|
+
"workspace.move_block",
|
|
31306
|
+
"workspace.report_metadata",
|
|
31307
|
+
"workspace.close",
|
|
31308
|
+
"worktree.list",
|
|
31309
|
+
"worktree.create",
|
|
31310
|
+
"worktree.open",
|
|
31311
|
+
"worktree.remove",
|
|
31312
|
+
"tab.create",
|
|
31313
|
+
"tab.list",
|
|
31314
|
+
"tab.get",
|
|
31315
|
+
"tab.focus",
|
|
31316
|
+
"tab.rename",
|
|
31317
|
+
"tab.move",
|
|
31318
|
+
"tab.close",
|
|
31319
|
+
"agent.list",
|
|
31320
|
+
"agent.get",
|
|
31321
|
+
"agent.read",
|
|
31322
|
+
"agent.explain",
|
|
31323
|
+
"agent.send_keys",
|
|
31324
|
+
"agent.rename",
|
|
31325
|
+
"agent.view.set",
|
|
31326
|
+
"agent.view.clear",
|
|
31327
|
+
"agent.focus",
|
|
31328
|
+
"agent.start",
|
|
31329
|
+
"agent.prompt",
|
|
31330
|
+
"agent.wait",
|
|
31331
|
+
"pane.split",
|
|
31332
|
+
"pane.swap",
|
|
31333
|
+
"pane.move",
|
|
31334
|
+
"pane.zoom",
|
|
31335
|
+
"pane.layout",
|
|
31336
|
+
"pane.process_info",
|
|
31337
|
+
"layout.export",
|
|
31338
|
+
"layout.apply",
|
|
31339
|
+
"layout.set_split_ratio",
|
|
31340
|
+
"pane.neighbor",
|
|
31341
|
+
"pane.edges",
|
|
31342
|
+
"pane.focus_direction",
|
|
31343
|
+
"pane.resize",
|
|
31344
|
+
"pane.scroll",
|
|
31345
|
+
"pane.edit_scrollback",
|
|
31346
|
+
"pane.selection.read",
|
|
31347
|
+
"pane.copy_motion",
|
|
31348
|
+
"pane.copy_search",
|
|
31349
|
+
"pane.list",
|
|
31350
|
+
"pane.current",
|
|
31351
|
+
"pane.get",
|
|
31352
|
+
"pane.focus",
|
|
31353
|
+
"pane.input.set",
|
|
31354
|
+
"pane.link.activate",
|
|
31355
|
+
"pane.rename",
|
|
31356
|
+
"pane.send_text",
|
|
31357
|
+
"pane.send_keys",
|
|
31358
|
+
"pane.send_input",
|
|
31359
|
+
"pane.read",
|
|
31360
|
+
"pane.graphics.set",
|
|
31361
|
+
"pane.graphics.clear",
|
|
31362
|
+
"pane.graphics.info",
|
|
31363
|
+
"pane.report_agent",
|
|
31364
|
+
"pane.report_agent_session",
|
|
31365
|
+
"pane.report_metadata",
|
|
31366
|
+
"pane.clear_agent_authority",
|
|
31367
|
+
"pane.release_agent",
|
|
31368
|
+
"pane.close",
|
|
31369
|
+
"popup.close",
|
|
31370
|
+
"events.subscribe",
|
|
31371
|
+
"events.wait",
|
|
31372
|
+
"pane.wait_for_output",
|
|
31373
|
+
"integration.list",
|
|
31374
|
+
"integration.install",
|
|
31375
|
+
"integration.uninstall",
|
|
31376
|
+
"plugin.link",
|
|
31377
|
+
"plugin.list",
|
|
31378
|
+
"plugin.unlink",
|
|
31379
|
+
"plugin.enable",
|
|
31380
|
+
"plugin.disable",
|
|
31381
|
+
"plugin.action.list",
|
|
31382
|
+
"plugin.action.invoke",
|
|
31383
|
+
"plugin.log.list",
|
|
31384
|
+
"plugin.pane.open",
|
|
31385
|
+
"plugin.pane.focus",
|
|
31386
|
+
"plugin.pane.close"
|
|
31387
|
+
];
|
|
31388
|
+
|
|
31389
|
+
// src/herdr-streams.ts
|
|
31390
|
+
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
31391
|
+
|
|
31392
|
+
// src/herdr-transport.ts
|
|
31271
31393
|
import net from "node:net";
|
|
31272
31394
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
31273
|
-
|
|
31395
|
+
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
31396
|
+
var HerdrError = class extends Error {
|
|
31397
|
+
constructor(code, message) {
|
|
31398
|
+
super(message);
|
|
31399
|
+
this.code = code;
|
|
31400
|
+
this.name = "HerdrError";
|
|
31401
|
+
}
|
|
31402
|
+
code;
|
|
31403
|
+
};
|
|
31404
|
+
function validateSocketPath(path) {
|
|
31405
|
+
if (!isAbsolute2(path) && !/^\\\\[.?]\\pipe\\[^\\]/.test(path))
|
|
31406
|
+
throw new TypeError("An absolute Herdr socket path or Windows named pipe is required");
|
|
31407
|
+
}
|
|
31408
|
+
function validateTimeout(value) {
|
|
31409
|
+
if (value !== null && (!Number.isFinite(value) || value <= 0 || value > 2147483647))
|
|
31410
|
+
throw new TypeError("timeoutMs must be a positive bounded duration or null");
|
|
31411
|
+
}
|
|
31412
|
+
function limit(value, name) {
|
|
31413
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
31414
|
+
throw new TypeError(`${name} must be a positive integer`);
|
|
31415
|
+
return value;
|
|
31416
|
+
}
|
|
31417
|
+
var JsonConnection = class {
|
|
31418
|
+
constructor(socketPath, options = {}) {
|
|
31419
|
+
this.options = options;
|
|
31420
|
+
validateSocketPath(socketPath);
|
|
31421
|
+
this.maxMessageBytes = limit(options.maxResponseBytes ?? 32 * 1024 * 1024, "maxResponseBytes");
|
|
31422
|
+
this.maxQueuedEvents = limit(options.maxQueuedEvents ?? 1024, "maxQueuedEvents");
|
|
31423
|
+
this.maxQueuedBytes = limit(options.maxQueuedBytes ?? this.maxMessageBytes, "maxQueuedBytes");
|
|
31424
|
+
if (options.signal?.aborted)
|
|
31425
|
+
throw new HerdrError("herdr_aborted", "Herdr operation was aborted");
|
|
31426
|
+
this.closed = new Promise((resolve13, reject) => {
|
|
31427
|
+
this.resolveClosed = resolve13;
|
|
31428
|
+
this.rejectClosed = reject;
|
|
31429
|
+
});
|
|
31430
|
+
void this.closed.catch(() => {
|
|
31431
|
+
});
|
|
31432
|
+
this.socket = net.createConnection(socketPath);
|
|
31433
|
+
this.socket.setEncoding("utf8");
|
|
31434
|
+
this.abort = () => this.fail(new HerdrError("herdr_aborted", "Herdr operation was aborted"), true);
|
|
31435
|
+
options.signal?.addEventListener("abort", this.abort, { once: true });
|
|
31436
|
+
this.socket.on(
|
|
31437
|
+
"error",
|
|
31438
|
+
(error) => this.fail(new HerdrError("herdr_unavailable", error.message))
|
|
31439
|
+
);
|
|
31440
|
+
this.socket.on("close", () => {
|
|
31441
|
+
this.ended = true;
|
|
31442
|
+
this.detachAbort();
|
|
31443
|
+
this.rejectWrites(this.error());
|
|
31444
|
+
if (this.failure || !this.explicitClose) this.rejectClosed(this.error());
|
|
31445
|
+
else this.resolveClosed();
|
|
31446
|
+
this.deliver();
|
|
31447
|
+
});
|
|
31448
|
+
this.socket.on("data", (data) => this.receive(data));
|
|
31449
|
+
}
|
|
31450
|
+
options;
|
|
31451
|
+
id = randomUUID5();
|
|
31452
|
+
closed;
|
|
31453
|
+
resolveClosed;
|
|
31454
|
+
rejectClosed;
|
|
31455
|
+
socket;
|
|
31456
|
+
buffer = "";
|
|
31457
|
+
queue = [];
|
|
31458
|
+
queuedBytes = 0;
|
|
31459
|
+
reader;
|
|
31460
|
+
writes = /* @__PURE__ */ new Set();
|
|
31461
|
+
ended = false;
|
|
31462
|
+
failure;
|
|
31463
|
+
explicitClose = false;
|
|
31464
|
+
maxMessageBytes;
|
|
31465
|
+
maxQueuedEvents;
|
|
31466
|
+
maxQueuedBytes;
|
|
31467
|
+
abort;
|
|
31468
|
+
error() {
|
|
31469
|
+
return this.failure ?? new HerdrError(
|
|
31470
|
+
"herdr_disconnected",
|
|
31471
|
+
"Herdr disconnected before acknowledgement or stream completion"
|
|
31472
|
+
);
|
|
31473
|
+
}
|
|
31474
|
+
detachAbort() {
|
|
31475
|
+
this.options.signal?.removeEventListener("abort", this.abort);
|
|
31476
|
+
}
|
|
31477
|
+
rejectWrites(error) {
|
|
31478
|
+
for (const reject of this.writes) reject(error);
|
|
31479
|
+
this.writes.clear();
|
|
31480
|
+
}
|
|
31481
|
+
fail(error, discard = false) {
|
|
31482
|
+
if (this.explicitClose) return;
|
|
31483
|
+
this.failure ??= error;
|
|
31484
|
+
this.ended = true;
|
|
31485
|
+
this.buffer = "";
|
|
31486
|
+
if (discard) {
|
|
31487
|
+
this.queue = [];
|
|
31488
|
+
this.queuedBytes = 0;
|
|
31489
|
+
}
|
|
31490
|
+
this.detachAbort();
|
|
31491
|
+
this.socket.destroy();
|
|
31492
|
+
this.rejectWrites(this.failure);
|
|
31493
|
+
this.deliver();
|
|
31494
|
+
}
|
|
31495
|
+
close() {
|
|
31496
|
+
this.explicitClose = true;
|
|
31497
|
+
this.ended = true;
|
|
31498
|
+
this.buffer = "";
|
|
31499
|
+
this.queue = [];
|
|
31500
|
+
this.queuedBytes = 0;
|
|
31501
|
+
this.detachAbort();
|
|
31502
|
+
this.socket.destroy();
|
|
31503
|
+
this.rejectWrites(this.error());
|
|
31504
|
+
this.deliver();
|
|
31505
|
+
}
|
|
31506
|
+
deliver() {
|
|
31507
|
+
if (!this.reader) return;
|
|
31508
|
+
const reader = this.reader;
|
|
31509
|
+
if (this.failure) {
|
|
31510
|
+
this.reader = void 0;
|
|
31511
|
+
reader.reject(this.failure);
|
|
31512
|
+
return;
|
|
31513
|
+
}
|
|
31514
|
+
const next = this.queue.shift();
|
|
31515
|
+
if (next) {
|
|
31516
|
+
this.reader = void 0;
|
|
31517
|
+
this.queuedBytes -= next.bytes;
|
|
31518
|
+
reader.resolve(next.value);
|
|
31519
|
+
} else if (this.ended) {
|
|
31520
|
+
this.reader = void 0;
|
|
31521
|
+
if (this.explicitClose) reader.resolve(void 0);
|
|
31522
|
+
else reader.reject(this.error());
|
|
31523
|
+
}
|
|
31524
|
+
}
|
|
31525
|
+
receive(data) {
|
|
31526
|
+
if (this.ended) return;
|
|
31527
|
+
this.buffer += data;
|
|
31528
|
+
let index;
|
|
31529
|
+
while ((index = this.buffer.indexOf("\n")) >= 0) {
|
|
31530
|
+
const line = this.buffer.slice(0, index);
|
|
31531
|
+
this.buffer = this.buffer.slice(index + 1);
|
|
31532
|
+
const bytes = Buffer.byteLength(line);
|
|
31533
|
+
if (bytes > this.maxMessageBytes)
|
|
31534
|
+
return this.fail(
|
|
31535
|
+
new HerdrError("herdr_response_too_large", "Herdr message exceeded maxResponseBytes"),
|
|
31536
|
+
true
|
|
31537
|
+
);
|
|
31538
|
+
let value;
|
|
31539
|
+
try {
|
|
31540
|
+
value = JSON.parse(line);
|
|
31541
|
+
} catch (error) {
|
|
31542
|
+
return this.fail(new HerdrError("herdr_invalid_response", String(error)), true);
|
|
31543
|
+
}
|
|
31544
|
+
if (value?.id !== void 0 && value.id !== this.id && !String(value.id).startsWith(this.id + ":"))
|
|
31545
|
+
continue;
|
|
31546
|
+
if (value?.error)
|
|
31547
|
+
return this.fail(new HerdrError(value.error.code, value.error.message), true);
|
|
31548
|
+
if (this.queue.length >= this.maxQueuedEvents || this.queuedBytes + bytes > this.maxQueuedBytes)
|
|
31549
|
+
return this.fail(
|
|
31550
|
+
new HerdrError(
|
|
31551
|
+
"herdr_stream_overflow",
|
|
31552
|
+
"Herdr consumer fell behind; stream closed without silently dropping events"
|
|
31553
|
+
),
|
|
31554
|
+
true
|
|
31555
|
+
);
|
|
31556
|
+
this.queue.push({ value, bytes });
|
|
31557
|
+
this.queuedBytes += bytes;
|
|
31558
|
+
this.deliver();
|
|
31559
|
+
}
|
|
31560
|
+
if (Buffer.byteLength(this.buffer) > this.maxMessageBytes)
|
|
31561
|
+
this.fail(
|
|
31562
|
+
new HerdrError("herdr_response_too_large", "Herdr message exceeded maxResponseBytes"),
|
|
31563
|
+
true
|
|
31564
|
+
);
|
|
31565
|
+
}
|
|
31566
|
+
read() {
|
|
31567
|
+
if (this.reader)
|
|
31568
|
+
return Promise.reject(new Error("Only one reader may consume a Herdr connection"));
|
|
31569
|
+
return new Promise((resolve13, reject) => {
|
|
31570
|
+
this.reader = { resolve: resolve13, reject };
|
|
31571
|
+
this.deliver();
|
|
31572
|
+
});
|
|
31573
|
+
}
|
|
31574
|
+
write(data) {
|
|
31575
|
+
if (this.ended) return Promise.reject(this.error());
|
|
31576
|
+
return new Promise((resolve13, reject) => {
|
|
31577
|
+
this.writes.add(reject);
|
|
31578
|
+
this.socket.write(data, (error) => {
|
|
31579
|
+
this.writes.delete(reject);
|
|
31580
|
+
if (error) {
|
|
31581
|
+
this.fail(new HerdrError("herdr_unavailable", error.message));
|
|
31582
|
+
reject(this.error());
|
|
31583
|
+
} else if (this.failure) reject(this.failure);
|
|
31584
|
+
else resolve13();
|
|
31585
|
+
});
|
|
31586
|
+
});
|
|
31587
|
+
}
|
|
31588
|
+
async within(timeoutMs, action) {
|
|
31589
|
+
validateTimeout(timeoutMs);
|
|
31590
|
+
const timer = timeoutMs === null ? void 0 : setTimeout(
|
|
31591
|
+
() => this.fail(
|
|
31592
|
+
new HerdrError(
|
|
31593
|
+
"herdr_timeout",
|
|
31594
|
+
"Herdr response timed out; delivery may be ambiguous"
|
|
31595
|
+
),
|
|
31596
|
+
true
|
|
31597
|
+
),
|
|
31598
|
+
timeoutMs
|
|
31599
|
+
);
|
|
31600
|
+
try {
|
|
31601
|
+
return await action();
|
|
31602
|
+
} finally {
|
|
31603
|
+
clearTimeout(timer);
|
|
31604
|
+
}
|
|
31605
|
+
}
|
|
31606
|
+
async start(method, params, timeoutMs) {
|
|
31607
|
+
return this.within(timeoutMs, async () => {
|
|
31608
|
+
await this.write(JSON.stringify({ id: this.id, method, params }) + "\n");
|
|
31609
|
+
const message = await this.read();
|
|
31610
|
+
if (!message || message.id !== this.id || !Object.hasOwn(message, "result"))
|
|
31611
|
+
throw new HerdrError("herdr_invalid_response", `${method}: missing correlated result`);
|
|
31612
|
+
return message.result;
|
|
31613
|
+
});
|
|
31614
|
+
}
|
|
31615
|
+
};
|
|
31616
|
+
async function socketRequest(socketPath, method, params, options = {}) {
|
|
31617
|
+
const timeoutMs = options.timeoutMs === void 0 ? 1e4 : options.timeoutMs;
|
|
31618
|
+
validateTimeout(timeoutMs);
|
|
31619
|
+
JSON.stringify(params);
|
|
31620
|
+
const connection = new JsonConnection(socketPath, options);
|
|
31621
|
+
try {
|
|
31622
|
+
return await connection.start(method, params, timeoutMs);
|
|
31623
|
+
} finally {
|
|
31624
|
+
connection.close();
|
|
31625
|
+
}
|
|
31626
|
+
}
|
|
31627
|
+
|
|
31628
|
+
// src/herdr-streams.ts
|
|
31629
|
+
var HerdrEventStream = class _HerdrEventStream {
|
|
31630
|
+
constructor(connection) {
|
|
31631
|
+
this.connection = connection;
|
|
31632
|
+
}
|
|
31633
|
+
connection;
|
|
31634
|
+
static async open(socketPath, subscriptions, options = {}) {
|
|
31635
|
+
const timeout = options.timeoutMs === void 0 ? 1e4 : options.timeoutMs;
|
|
31636
|
+
validateTimeout(timeout);
|
|
31637
|
+
JSON.stringify(subscriptions);
|
|
31638
|
+
const connection = new JsonConnection(socketPath, options);
|
|
31639
|
+
try {
|
|
31640
|
+
const result = await connection.start("events.subscribe", { subscriptions }, timeout);
|
|
31641
|
+
if (result?.type !== "subscription_started")
|
|
31642
|
+
throw new HerdrError("herdr_invalid_response", "Missing subscription acknowledgement");
|
|
31643
|
+
return new _HerdrEventStream(connection);
|
|
31644
|
+
} catch (error) {
|
|
31645
|
+
connection.close();
|
|
31646
|
+
throw error;
|
|
31647
|
+
}
|
|
31648
|
+
}
|
|
31649
|
+
get closed() {
|
|
31650
|
+
return this.connection.closed;
|
|
31651
|
+
}
|
|
31652
|
+
[Symbol.asyncIterator]() {
|
|
31653
|
+
return this;
|
|
31654
|
+
}
|
|
31655
|
+
async next() {
|
|
31656
|
+
const value = await this.connection.read();
|
|
31657
|
+
if (value === void 0) return { done: true, value: void 0 };
|
|
31658
|
+
if (!value || typeof value.event !== "string" || !Object.hasOwn(value, "data")) {
|
|
31659
|
+
const error = new HerdrError("herdr_invalid_response", "Expected a Herdr event envelope");
|
|
31660
|
+
this.connection.fail(error, true);
|
|
31661
|
+
throw error;
|
|
31662
|
+
}
|
|
31663
|
+
return { done: false, value };
|
|
31664
|
+
}
|
|
31665
|
+
close() {
|
|
31666
|
+
this.connection.close();
|
|
31667
|
+
}
|
|
31668
|
+
async return() {
|
|
31669
|
+
this.close();
|
|
31670
|
+
return { done: true, value: void 0 };
|
|
31671
|
+
}
|
|
31672
|
+
};
|
|
31673
|
+
var maxInlineBytes = 16 * 1024 * 1024;
|
|
31674
|
+
function integer(value, name, min = 0, max = Number.MAX_SAFE_INTEGER) {
|
|
31675
|
+
if (!Number.isSafeInteger(value) || value < min || value > max)
|
|
31676
|
+
throw new TypeError(`Invalid ${name}`);
|
|
31677
|
+
}
|
|
31678
|
+
function frameHeader(frame) {
|
|
31679
|
+
integer(frame.image_width, "image_width", 1, 4294967295);
|
|
31680
|
+
integer(frame.image_height, "image_height", 1, 4294967295);
|
|
31681
|
+
if (!["png", "rgb", "rgba", "bgra"].includes(frame.format))
|
|
31682
|
+
throw new TypeError("Invalid frame format");
|
|
31683
|
+
return {
|
|
31684
|
+
format: frame.format,
|
|
31685
|
+
image_width: frame.image_width,
|
|
31686
|
+
image_height: frame.image_height,
|
|
31687
|
+
...frame.placement ? { placement: frame.placement } : {}
|
|
31688
|
+
};
|
|
31689
|
+
}
|
|
31690
|
+
function encodeHeader(header) {
|
|
31691
|
+
const line = JSON.stringify(header) + "\n";
|
|
31692
|
+
if (Buffer.byteLength(line) > 64 * 1024) throw new TypeError("Graphics header exceeds 64 KiB");
|
|
31693
|
+
return line;
|
|
31694
|
+
}
|
|
31695
|
+
var HerdrGraphicsStream = class _HerdrGraphicsStream {
|
|
31696
|
+
constructor(connection) {
|
|
31697
|
+
this.connection = connection;
|
|
31698
|
+
}
|
|
31699
|
+
connection;
|
|
31700
|
+
busy = false;
|
|
31701
|
+
static async open(socketPath, params, options = {}) {
|
|
31702
|
+
const timeout = options.timeoutMs === void 0 ? 1e4 : options.timeoutMs;
|
|
31703
|
+
validateTimeout(timeout);
|
|
31704
|
+
JSON.stringify(params);
|
|
31705
|
+
const connection = new JsonConnection(socketPath, options);
|
|
31706
|
+
try {
|
|
31707
|
+
const result = await connection.start("pane.graphics.stream", params, timeout);
|
|
31708
|
+
if (result?.type !== "ok")
|
|
31709
|
+
throw new HerdrError("herdr_invalid_response", "Missing graphics stream acknowledgement");
|
|
31710
|
+
return new _HerdrGraphicsStream(connection);
|
|
31711
|
+
} catch (error) {
|
|
31712
|
+
connection.close();
|
|
31713
|
+
throw error;
|
|
31714
|
+
}
|
|
31715
|
+
}
|
|
31716
|
+
/** Resolves after local close; rejects on remote failure, cancellation, or disconnect. */
|
|
31717
|
+
get closed() {
|
|
31718
|
+
return this.connection.closed;
|
|
31719
|
+
}
|
|
31720
|
+
async exclusive(action) {
|
|
31721
|
+
if (this.busy)
|
|
31722
|
+
throw new HerdrError("herdr_stream_busy", "Await the previous frame before sending another");
|
|
31723
|
+
this.busy = true;
|
|
31724
|
+
try {
|
|
31725
|
+
return await action();
|
|
31726
|
+
} finally {
|
|
31727
|
+
this.busy = false;
|
|
31728
|
+
}
|
|
31729
|
+
}
|
|
31730
|
+
/** Resolves after local socket write, not a render ACK: inline success has no wire reply. */
|
|
31731
|
+
async frame(frame, data, timeoutMs = 3e4) {
|
|
31732
|
+
validateTimeout(timeoutMs);
|
|
31733
|
+
if (!data.byteLength || data.byteLength > maxInlineBytes)
|
|
31734
|
+
throw new TypeError("Inline graphics data must be 1 byte to 16 MiB");
|
|
31735
|
+
const header = encodeHeader({ ...frameHeader(frame), data_length: data.byteLength });
|
|
31736
|
+
return this.exclusive(async () => {
|
|
31737
|
+
const bytes = Buffer.from(data);
|
|
31738
|
+
await this.connection.within(timeoutMs, async () => {
|
|
31739
|
+
await this.connection.write(header);
|
|
31740
|
+
await this.connection.write(bytes);
|
|
31741
|
+
});
|
|
31742
|
+
});
|
|
31743
|
+
}
|
|
31744
|
+
/** Keep the immutable source file until this method acknowledges its sequence and revision. */
|
|
31745
|
+
async fileFrame(frame, timeoutMs = 3e4) {
|
|
31746
|
+
validateTimeout(timeoutMs);
|
|
31747
|
+
if (!["rgba", "bgra"].includes(frame.format) || !isAbsolute3(frame.file.path))
|
|
31748
|
+
throw new TypeError("File frames require rgba/bgra and an absolute source path");
|
|
31749
|
+
integer(frame.sequence, "sequence");
|
|
31750
|
+
integer(frame.revision, "revision");
|
|
31751
|
+
const { sequence, revision } = frame;
|
|
31752
|
+
const header = encodeHeader({
|
|
31753
|
+
...frameHeader(frame),
|
|
31754
|
+
file: { path: frame.file.path },
|
|
31755
|
+
sequence,
|
|
31756
|
+
revision
|
|
31757
|
+
});
|
|
31758
|
+
return this.exclusive(
|
|
31759
|
+
() => this.connection.within(timeoutMs, async () => {
|
|
31760
|
+
await this.connection.write(header);
|
|
31761
|
+
const message = await this.connection.read();
|
|
31762
|
+
const result = message?.result;
|
|
31763
|
+
if (message?.id !== `${this.connection.id}:file:${sequence}` || result?.type !== "pane_graphics_frame_ack" || result.sequence !== sequence || result.revision !== revision) {
|
|
31764
|
+
const error = new HerdrError(
|
|
31765
|
+
"herdr_invalid_response",
|
|
31766
|
+
"Graphics acknowledgement did not match the file frame"
|
|
31767
|
+
);
|
|
31768
|
+
this.connection.fail(error, true);
|
|
31769
|
+
throw error;
|
|
31770
|
+
}
|
|
31771
|
+
return result;
|
|
31772
|
+
})
|
|
31773
|
+
);
|
|
31774
|
+
}
|
|
31775
|
+
close() {
|
|
31776
|
+
this.connection.close();
|
|
31777
|
+
}
|
|
31778
|
+
};
|
|
31779
|
+
|
|
31780
|
+
// src/herdr-sdk.ts
|
|
31781
|
+
function deadline(method, params) {
|
|
31782
|
+
if (method === "agent.start") return (params.timeout_ms ?? 3e4) + 5e3;
|
|
31783
|
+
const wait = method === "agent.prompt" ? params.wait : ["agent.wait", "events.wait", "pane.wait_for_output"].includes(method) ? params : void 0;
|
|
31784
|
+
if (wait) return wait.timeout_ms == null ? null : wait.timeout_ms + 5e3;
|
|
31785
|
+
return 1e4;
|
|
31786
|
+
}
|
|
31787
|
+
var HerdrClient = class _HerdrClient {
|
|
31274
31788
|
constructor(socketPath) {
|
|
31275
31789
|
this.socketPath = socketPath;
|
|
31790
|
+
validateSocketPath(socketPath);
|
|
31276
31791
|
}
|
|
31277
31792
|
socketPath;
|
|
31793
|
+
static fromEnv(env = process.env) {
|
|
31794
|
+
if (env.HERDR_ENV !== "1" || !env.HERDR_SOCKET_PATH)
|
|
31795
|
+
throw new Error("Run inside Herdr or supply an explicitly selected socket path");
|
|
31796
|
+
return new _HerdrClient(env.HERDR_SOCKET_PATH);
|
|
31797
|
+
}
|
|
31798
|
+
/** Escape hatch for future one-shot methods. Streams require their dedicated transports. */
|
|
31278
31799
|
call(method, params = {}, timeoutMs = 1e4) {
|
|
31279
|
-
|
|
31280
|
-
|
|
31281
|
-
|
|
31282
|
-
let settled2 = false;
|
|
31283
|
-
const socket = net.createConnection(this.socketPath);
|
|
31284
|
-
const finish = (error, result) => {
|
|
31285
|
-
if (settled2) return;
|
|
31286
|
-
settled2 = true;
|
|
31287
|
-
clearTimeout(timer);
|
|
31288
|
-
socket.destroy();
|
|
31289
|
-
error ? reject(error) : resolve13(result);
|
|
31290
|
-
};
|
|
31291
|
-
const timer = setTimeout(
|
|
31292
|
-
() => finish(
|
|
31293
|
-
new AppError(
|
|
31294
|
-
"herdr_timeout",
|
|
31295
|
-
`${method}: response timed out; delivery may be ambiguous`,
|
|
31296
|
-
503
|
|
31297
|
-
)
|
|
31298
|
-
),
|
|
31299
|
-
timeoutMs
|
|
31300
|
-
);
|
|
31301
|
-
socket.on("connect", () => socket.write(JSON.stringify({ id, method, params }) + "\n"));
|
|
31302
|
-
socket.on(
|
|
31303
|
-
"error",
|
|
31304
|
-
(e) => finish(new AppError("herdr_unavailable", `${method}: ${e.message}`, 503))
|
|
31800
|
+
if (method === "events.subscribe" || method === "pane.graphics.stream")
|
|
31801
|
+
return Promise.reject(
|
|
31802
|
+
new TypeError("Use subscribe(), graphicsStream(), or api for a streaming method")
|
|
31305
31803
|
);
|
|
31306
|
-
|
|
31307
|
-
|
|
31308
|
-
|
|
31309
|
-
|
|
31804
|
+
return socketRequest(this.socketPath, method, params, { timeoutMs });
|
|
31805
|
+
}
|
|
31806
|
+
/** All schema-defined one-shot methods with exact typed parameter objects. */
|
|
31807
|
+
request(method, ...args2) {
|
|
31808
|
+
const [params = {}, options = {}] = args2;
|
|
31809
|
+
if (!HERDR_METHODS.includes(method) || method === "events.subscribe")
|
|
31810
|
+
return Promise.reject(
|
|
31811
|
+
new TypeError(
|
|
31812
|
+
"Unknown one-shot method; use call() for extensions or subscribe() for events"
|
|
31310
31813
|
)
|
|
31311
31814
|
);
|
|
31312
|
-
|
|
31313
|
-
|
|
31314
|
-
|
|
31315
|
-
return finish(new Error("Herdr response exceeded 8 MiB"));
|
|
31316
|
-
let index;
|
|
31317
|
-
while ((index = buffer.indexOf("\n")) >= 0) {
|
|
31318
|
-
const line = buffer.slice(0, index);
|
|
31319
|
-
buffer = buffer.slice(index + 1);
|
|
31320
|
-
try {
|
|
31321
|
-
const v = JSON.parse(line);
|
|
31322
|
-
if (v.id !== id) continue;
|
|
31323
|
-
if (v.error) finish(new AppError(v.error.code, v.error.message, 502));
|
|
31324
|
-
else finish(void 0, v.result);
|
|
31325
|
-
} catch (e) {
|
|
31326
|
-
finish(e);
|
|
31327
|
-
}
|
|
31328
|
-
}
|
|
31329
|
-
});
|
|
31815
|
+
return socketRequest(this.socketPath, method, params, {
|
|
31816
|
+
...options,
|
|
31817
|
+
timeoutMs: options.timeoutMs === void 0 ? deadline(method, params) : options.timeoutMs
|
|
31330
31818
|
});
|
|
31331
31819
|
}
|
|
31820
|
+
subscribe(subscriptions, options) {
|
|
31821
|
+
return HerdrEventStream.open(this.socketPath, subscriptions, options);
|
|
31822
|
+
}
|
|
31823
|
+
graphicsStream(params, options) {
|
|
31824
|
+
return HerdrGraphicsStream.open(this.socketPath, params, options);
|
|
31825
|
+
}
|
|
31826
|
+
/** One-to-one method names, including streams, for discovery and generated agent code. */
|
|
31827
|
+
api = Object.freeze(
|
|
31828
|
+
Object.fromEntries([
|
|
31829
|
+
...HERDR_METHODS.map((method) => [
|
|
31830
|
+
method,
|
|
31831
|
+
method === "events.subscribe" ? (params, options) => this.subscribe(params.subscriptions, options) : (params = {}, options) => this.request(method, params, options)
|
|
31832
|
+
]),
|
|
31833
|
+
[
|
|
31834
|
+
"pane.graphics.stream",
|
|
31835
|
+
(params, options) => this.graphicsStream(params, options)
|
|
31836
|
+
]
|
|
31837
|
+
])
|
|
31838
|
+
);
|
|
31839
|
+
workspace = {
|
|
31840
|
+
list: () => this.call("workspace.list"),
|
|
31841
|
+
create: (options) => this.call("workspace.create", {
|
|
31842
|
+
focus: false,
|
|
31843
|
+
...options
|
|
31844
|
+
})
|
|
31845
|
+
};
|
|
31846
|
+
tab = {
|
|
31847
|
+
create: (workspaceId, options) => this.call("tab.create", {
|
|
31848
|
+
focus: false,
|
|
31849
|
+
...options,
|
|
31850
|
+
workspace_id: workspaceId
|
|
31851
|
+
}),
|
|
31852
|
+
list: (workspaceId) => this.call("tab.list", { workspace_id: workspaceId }),
|
|
31853
|
+
close: (tabId) => this.call("tab.close", { tab_id: tabId })
|
|
31854
|
+
};
|
|
31855
|
+
pane = {
|
|
31856
|
+
list: (workspaceId) => this.call("pane.list", { workspace_id: workspaceId }),
|
|
31857
|
+
layout: (paneId) => this.call("pane.layout", { pane_id: paneId }),
|
|
31858
|
+
split: (paneId, options) => this.call("pane.split", { focus: false, ...options, target_pane_id: paneId }),
|
|
31859
|
+
read: (paneId, lines = 120) => this.call("pane.read", {
|
|
31860
|
+
pane_id: paneId,
|
|
31861
|
+
source: "recent_unwrapped",
|
|
31862
|
+
format: "text",
|
|
31863
|
+
lines
|
|
31864
|
+
}),
|
|
31865
|
+
sendText: (paneId, text2) => this.call("pane.send_text", { pane_id: paneId, text: text2 }),
|
|
31866
|
+
sendKeys: (paneId, keys) => this.call("pane.send_keys", { pane_id: paneId, keys }),
|
|
31867
|
+
close: (paneId) => this.call("pane.close", { pane_id: paneId })
|
|
31868
|
+
};
|
|
31869
|
+
agent = {
|
|
31870
|
+
start: (paneId, name, kind, args2 = [], timeoutMs = 3e4) => this.call(
|
|
31871
|
+
"agent.start",
|
|
31872
|
+
{ pane_id: paneId, name, kind, args: args2, timeout_ms: timeoutMs },
|
|
31873
|
+
timeoutMs + 5e3
|
|
31874
|
+
),
|
|
31875
|
+
get: (target) => this.call("agent.get", { target }),
|
|
31876
|
+
prompt: (target, text2, wait) => this.call(
|
|
31877
|
+
"agent.prompt",
|
|
31878
|
+
{ target, text: text2, ...wait ? { wait } : {} },
|
|
31879
|
+
wait ? wait.timeout_ms + 5e3 : 1e4
|
|
31880
|
+
),
|
|
31881
|
+
wait: (target, options) => this.call("agent.wait", { target, ...options }, options.timeout_ms + 5e3),
|
|
31882
|
+
sendKeys: (target, keys) => this.call("agent.send_keys", { target, keys })
|
|
31883
|
+
};
|
|
31884
|
+
};
|
|
31885
|
+
|
|
31886
|
+
// src/herdr.ts
|
|
31887
|
+
var Herdr = class extends HerdrClient {
|
|
31888
|
+
async call(method, params = {}, timeoutMs = 1e4) {
|
|
31889
|
+
try {
|
|
31890
|
+
return await super.call(method, params, timeoutMs);
|
|
31891
|
+
} catch (error) {
|
|
31892
|
+
if (error instanceof HerdrError)
|
|
31893
|
+
throw new AppError(error.code, error.message, error.code.startsWith("herdr_") ? 503 : 502);
|
|
31894
|
+
throw error;
|
|
31895
|
+
}
|
|
31896
|
+
}
|
|
31332
31897
|
};
|
|
31333
31898
|
|
|
31334
31899
|
// src/service.ts
|
|
@@ -31454,7 +32019,7 @@ var Service = class {
|
|
|
31454
32019
|
agentArgs: external_exports.record(kindSchema, external_exports.array(external_exports.string())).default({}),
|
|
31455
32020
|
trustAgyWorkspaces: external_exports.boolean().default(false)
|
|
31456
32021
|
}).parse(raw);
|
|
31457
|
-
if (!
|
|
32022
|
+
if (!isAbsolute4(input.root) || !isAbsolute4(input.socketPath))
|
|
31458
32023
|
throw new AppError("absolute_paths", "root and socketPath must be absolute");
|
|
31459
32024
|
const root = realpathSync4(input.root);
|
|
31460
32025
|
if (!statSync4(root).isDirectory())
|
|
@@ -31701,29 +32266,55 @@ var Service = class {
|
|
|
31701
32266
|
"No task prompt was attempted. Inspect the workspace and reconcile creation as not-delivered."
|
|
31702
32267
|
);
|
|
31703
32268
|
const project = this.project(t.projectId);
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
(
|
|
31707
|
-
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
32269
|
+
if (r.creation?.mode === "pane") {
|
|
32270
|
+
const all = (await h.call("pane.list", { workspace_id: project.workspaceId })).panes;
|
|
32271
|
+
if (!Array.isArray(all) || !r.creation.beforePaneIds)
|
|
32272
|
+
throw new AppError(
|
|
32273
|
+
"identity_changed",
|
|
32274
|
+
"Missing split membership; inspect the workspace",
|
|
32275
|
+
409
|
|
32276
|
+
);
|
|
32277
|
+
const added = all.filter(
|
|
32278
|
+
(pane) => !r.creation.beforePaneIds.includes(pane.pane_id)
|
|
31713
32279
|
);
|
|
31714
|
-
|
|
31715
|
-
|
|
31716
|
-
if (panes.length !== 1 || panes[0].agent || panes[0].launch_pending || panes[0].workspace_id !== project.workspaceId || panes[0].cwd !== t.cwd)
|
|
32280
|
+
const panes = added.filter((pane) => pane.tab_id === r.creation.tabId);
|
|
32281
|
+
if (added.length !== panes.length || panes.length > 1 || panes.length === 1 && (panes[0].agent || panes[0].launch_pending || panes[0].workspace_id !== project.workspaceId || panes[0].cwd !== t.cwd))
|
|
31717
32282
|
throw new AppError(
|
|
31718
32283
|
"identity_changed",
|
|
31719
|
-
"
|
|
32284
|
+
"Interrupted split is ambiguous or no longer an untouched shell; inspect it before recovery",
|
|
31720
32285
|
409
|
|
31721
32286
|
);
|
|
31722
|
-
|
|
31723
|
-
|
|
31724
|
-
|
|
31725
|
-
|
|
31726
|
-
|
|
32287
|
+
if (panes.length === 1)
|
|
32288
|
+
Object.assign(r, {
|
|
32289
|
+
paneId: panes[0].pane_id,
|
|
32290
|
+
terminalId: panes[0].terminal_id,
|
|
32291
|
+
tabId: panes[0].tab_id
|
|
32292
|
+
});
|
|
32293
|
+
} else {
|
|
32294
|
+
const tabs = (await h.call("tab.list", { workspace_id: project.workspaceId })).tabs ?? [];
|
|
32295
|
+
const matches = tabs.filter(
|
|
32296
|
+
(tab) => tab.workspace_id === project.workspaceId && tab.label === r.agentName
|
|
32297
|
+
);
|
|
32298
|
+
if (matches.length > 1)
|
|
32299
|
+
throw new AppError(
|
|
32300
|
+
"identity_changed",
|
|
32301
|
+
"Multiple tabs match the interrupted creation; manual inspection is required",
|
|
32302
|
+
409
|
|
32303
|
+
);
|
|
32304
|
+
if (matches.length === 1) {
|
|
32305
|
+
const panes = ((await h.call("pane.list", { workspace_id: project.workspaceId })).panes ?? []).filter((pane) => pane.tab_id === matches[0].tab_id);
|
|
32306
|
+
if (panes.length !== 1 || panes[0].agent || panes[0].launch_pending || panes[0].workspace_id !== project.workspaceId || panes[0].cwd !== t.cwd)
|
|
32307
|
+
throw new AppError(
|
|
32308
|
+
"identity_changed",
|
|
32309
|
+
"The orphan tab is no longer an untouched shell; inspect it before recovery",
|
|
32310
|
+
409
|
|
32311
|
+
);
|
|
32312
|
+
Object.assign(r, {
|
|
32313
|
+
paneId: panes[0].pane_id,
|
|
32314
|
+
terminalId: panes[0].terminal_id,
|
|
32315
|
+
tabId: panes[0].tab_id
|
|
32316
|
+
});
|
|
32317
|
+
}
|
|
31727
32318
|
}
|
|
31728
32319
|
} else {
|
|
31729
32320
|
const a = (await h.call("agent.get", { target: r.paneId })).agent;
|
|
@@ -31919,7 +32510,7 @@ var Service = class {
|
|
|
31919
32510
|
"Task directory must be within the registered root. Register external worktrees as projects."
|
|
31920
32511
|
);
|
|
31921
32512
|
for (const path of a.ownership) {
|
|
31922
|
-
if (a.execution?.mode === "worktree" &&
|
|
32513
|
+
if (a.execution?.mode === "worktree" && isAbsolute4(path))
|
|
31923
32514
|
throw new AppError(
|
|
31924
32515
|
"worktree_path",
|
|
31925
32516
|
"Worktree ownership paths must be relative to the task directory"
|
|
@@ -31933,7 +32524,7 @@ var Service = class {
|
|
|
31933
32524
|
}
|
|
31934
32525
|
for (const check of a.checks)
|
|
31935
32526
|
if (check.type === "file") {
|
|
31936
|
-
if (a.execution?.mode === "worktree" &&
|
|
32527
|
+
if (a.execution?.mode === "worktree" && isAbsolute4(check.path))
|
|
31937
32528
|
throw new AppError(
|
|
31938
32529
|
"worktree_path",
|
|
31939
32530
|
"Worktree file checks must be relative to the task directory"
|
|
@@ -32067,6 +32658,52 @@ var Service = class {
|
|
|
32067
32658
|
}
|
|
32068
32659
|
};
|
|
32069
32660
|
|
|
32661
|
+
// src/worker-layout.ts
|
|
32662
|
+
async function planWorkerPane(h, workspaceId, runs) {
|
|
32663
|
+
const panes = (await h.call("pane.list", { workspace_id: workspaceId })).panes;
|
|
32664
|
+
if (!Array.isArray(panes)) throw new Error("Herdr did not return workspace panes");
|
|
32665
|
+
const candidates = runs.filter(
|
|
32666
|
+
(r) => r.terminalScope === "pane" && r.paneId && r.tabId && !r.cleanup && r.phase !== "creating"
|
|
32667
|
+
);
|
|
32668
|
+
for (const tabId of new Set(candidates.map((r) => r.tabId))) {
|
|
32669
|
+
if (runs.some((r) => r.phase === "creating" && r.creation?.tabId === tabId)) continue;
|
|
32670
|
+
const members = panes.filter((p) => p.workspace_id === workspaceId && p.tab_id === tabId);
|
|
32671
|
+
if (!members.length || members.length >= 4) continue;
|
|
32672
|
+
if (!members.every(
|
|
32673
|
+
(p) => candidates.some(
|
|
32674
|
+
(r) => r.paneId === p.pane_id && r.terminalId === p.terminal_id && r.tabId === p.tab_id
|
|
32675
|
+
)
|
|
32676
|
+
))
|
|
32677
|
+
continue;
|
|
32678
|
+
const layout = (await h.call("pane.layout", { pane_id: members[0].pane_id })).layout;
|
|
32679
|
+
if (layout?.workspace_id !== workspaceId || layout.tab_id !== tabId || !Array.isArray(layout.panes))
|
|
32680
|
+
continue;
|
|
32681
|
+
const choices = layout.panes.flatMap((p) => {
|
|
32682
|
+
if (!members.some((m) => m.pane_id === p.pane_id)) return [];
|
|
32683
|
+
const { width, height } = p.rect;
|
|
32684
|
+
const right = width >= 121 && height >= 12;
|
|
32685
|
+
const down = height >= 25 && width >= 60;
|
|
32686
|
+
if (!right && !down) return [];
|
|
32687
|
+
return [
|
|
32688
|
+
{
|
|
32689
|
+
paneId: p.pane_id,
|
|
32690
|
+
area: width * height,
|
|
32691
|
+
direction: right && (!down || width >= height * 3) ? "right" : "down"
|
|
32692
|
+
}
|
|
32693
|
+
];
|
|
32694
|
+
}).sort((a, b) => b.area - a.area);
|
|
32695
|
+
if (choices.length)
|
|
32696
|
+
return {
|
|
32697
|
+
mode: "pane",
|
|
32698
|
+
tabId,
|
|
32699
|
+
targetPaneId: choices[0].paneId,
|
|
32700
|
+
direction: choices[0].direction,
|
|
32701
|
+
beforePaneIds: panes.map((p) => p.pane_id)
|
|
32702
|
+
};
|
|
32703
|
+
}
|
|
32704
|
+
return { mode: "tab" };
|
|
32705
|
+
}
|
|
32706
|
+
|
|
32070
32707
|
// src/prompts.ts
|
|
32071
32708
|
var leadContract = `Own the user's outcome through verified completion. Establish a persistent outcome with objective, explicit scope, observable criteria and required evidence before dispatch. Use criteria suited to the work: executable behavior checks for software, corroborated sources and traceable findings for research, reproducible calculations for analysis, and documented alternatives, tradeoffs and unresolved disagreements for decisions. Ask for clarification only when the evidence cannot resolve a necessary ambiguity.
|
|
32072
32709
|
Choose exact available model profiles and an appropriate bounded collaboration strategy. Preserve a user's explicit model choice; if unavailable, report it and make any proposed fallback explicit. Stage independent council, debate or competing-proposal participants with deferStart: true, then use strategy_create to establish common evaluation criteria and release their initial assessments. Do not contaminate initial assessments by sharing another participant's conclusions. Use verified participant results in synthesis and preserve material disagreements. For later debate rounds, obtain fresh verified participant revisions before contributing their rebuttals.
|
|
@@ -32165,6 +32802,22 @@ var Supervisor = class {
|
|
|
32165
32802
|
timer;
|
|
32166
32803
|
busy = /* @__PURE__ */ new Set();
|
|
32167
32804
|
stopped = false;
|
|
32805
|
+
layouts = /* @__PURE__ */ new Map();
|
|
32806
|
+
async withLayout(projectId, action) {
|
|
32807
|
+
const previous = this.layouts.get(projectId) ?? Promise.resolve();
|
|
32808
|
+
let release;
|
|
32809
|
+
const pending = new Promise((resolve13) => {
|
|
32810
|
+
release = resolve13;
|
|
32811
|
+
});
|
|
32812
|
+
this.layouts.set(projectId, pending);
|
|
32813
|
+
await previous;
|
|
32814
|
+
try {
|
|
32815
|
+
return await action();
|
|
32816
|
+
} finally {
|
|
32817
|
+
release();
|
|
32818
|
+
if (this.layouts.get(projectId) === pending) this.layouts.delete(projectId);
|
|
32819
|
+
}
|
|
32820
|
+
}
|
|
32168
32821
|
start() {
|
|
32169
32822
|
this.recover();
|
|
32170
32823
|
this.service.continuation.recover();
|
|
@@ -32498,23 +33151,38 @@ ${JSON.stringify(children.map((child) => ({ id: child.id, title: child.title, st
|
|
|
32498
33151
|
t = s.updateTask(s.task(id), { runId: run.id, attempt: run.attempt });
|
|
32499
33152
|
});
|
|
32500
33153
|
try {
|
|
32501
|
-
|
|
32502
|
-
|
|
32503
|
-
|
|
32504
|
-
|
|
32505
|
-
|
|
32506
|
-
|
|
32507
|
-
|
|
32508
|
-
|
|
32509
|
-
|
|
32510
|
-
|
|
32511
|
-
|
|
32512
|
-
|
|
32513
|
-
|
|
32514
|
-
|
|
32515
|
-
|
|
33154
|
+
await this.withLayout(p.id, async () => {
|
|
33155
|
+
const taskIds = new Set(s.tasks(p.id).map((task) => task.id));
|
|
33156
|
+
const runs = s.store.all("run").filter((r) => taskIds.has(r.taskId) && !s.cleanup.active(r.taskId));
|
|
33157
|
+
run.creation = await planWorkerPane(h, p.workspaceId, runs);
|
|
33158
|
+
run.terminalScope = "pane";
|
|
33159
|
+
this.saveRun(run);
|
|
33160
|
+
const options = {
|
|
33161
|
+
workspace_id: p.workspaceId,
|
|
33162
|
+
cwd: t.cwd,
|
|
33163
|
+
focus: false,
|
|
33164
|
+
env: { MARIONETTE_WORKER_TOKEN: token, MARIONETTE_TASK_ID: id, MARIONETTE_URL: this.url }
|
|
33165
|
+
};
|
|
33166
|
+
const splitting = run.creation.mode === "pane";
|
|
33167
|
+
const created = await h.call(
|
|
33168
|
+
splitting ? "pane.split" : "tab.create",
|
|
33169
|
+
splitting ? {
|
|
33170
|
+
...options,
|
|
33171
|
+
target_pane_id: run.creation.targetPaneId,
|
|
33172
|
+
direction: run.creation.direction
|
|
33173
|
+
} : { ...options, label: run.agentName }
|
|
33174
|
+
);
|
|
33175
|
+
const pane = splitting ? created.pane : created.root_pane;
|
|
33176
|
+
if (!pane?.pane_id || !pane.terminal_id || !pane.tab_id || pane.workspace_id !== p.workspaceId || splitting && (pane.tab_id !== run.creation.tabId || run.creation.beforePaneIds.includes(pane.pane_id)))
|
|
33177
|
+
throw new Error("Herdr did not return the expected new scoped pane");
|
|
33178
|
+
Object.assign(run, {
|
|
33179
|
+
paneId: pane.pane_id,
|
|
33180
|
+
terminalId: pane.terminal_id,
|
|
33181
|
+
tabId: pane.tab_id,
|
|
33182
|
+
phase: "starting"
|
|
33183
|
+
});
|
|
33184
|
+
this.saveRun(run);
|
|
32516
33185
|
});
|
|
32517
|
-
this.saveRun(run);
|
|
32518
33186
|
for (let attempt = 0; ; attempt++) {
|
|
32519
33187
|
try {
|
|
32520
33188
|
await h.call(
|
|
@@ -32934,7 +33602,7 @@ ${result.output}`;
|
|
|
32934
33602
|
};
|
|
32935
33603
|
|
|
32936
33604
|
// src/version.ts
|
|
32937
|
-
var VERSION = "0.2.
|
|
33605
|
+
var VERSION = "0.2.2";
|
|
32938
33606
|
var SETUP_VERSION = 2;
|
|
32939
33607
|
|
|
32940
33608
|
// src/server.ts
|