@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/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 limit = Error.stackTraceLimit;
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, limit);
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 = limit;
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 limit = bytes.parse(opts.limit);
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, limit, wrap(done));
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, limit, function onRead(err, buf) {
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, limit, callback) {
5362
+ function readStream(stream, encoding, length, limit2, callback) {
5363
5363
  var complete = false;
5364
5364
  var sync = true;
5365
- if (limit !== null && length !== null && length > limit) {
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 (limit !== null && received > limit) {
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 limit = typeof options?.limit === "undefined" || options?.limit === null ? 102400 : bytes.parse(options.limit);
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 (limit === null) {
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 limit = 1024;
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 += limit) {
17777
- var segment = string.length >= limit ? string.slice(j, j + limit) : string;
17778
- if (j + limit < string.length) {
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 limit = options.parameterLimit === Infinity ? void 0 : options.parameterLimit;
18249
+ var limit2 = options.parameterLimit === Infinity ? void 0 : options.parameterLimit;
18250
18250
  var parts = cleanStr.split(
18251
18251
  options.delimiter,
18252
- options.throwOnLimitExceeded && typeof limit !== "undefined" ? limit + 1 : limit
18252
+ options.throwOnLimitExceeded && typeof limit2 !== "undefined" ? limit2 + 1 : limit2
18253
18253
  );
18254
- if (options.throwOnLimitExceeded && typeof limit !== "undefined" && parts.length > limit) {
18255
- throw new RangeError("Parameter limit exceeded. Only " + limit + " parameter" + (limit === 1 ? "" : "s") + " allowed.");
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, limit) {
18603
+ function parameterCount(body, limit2) {
18604
18604
  let count = 0;
18605
18605
  let index = -1;
18606
18606
  do {
18607
18607
  count++;
18608
- if (count > limit) return void 0;
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, limit = 200) {
28282
- return this.db.prepare("SELECT id,data FROM events WHERE project_id=? AND id>? ORDER BY id LIMIT ?").all(projectId, after, limit).map((r) => ({ ...JSON.parse(r.data), id: r.id }));
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 isAbsolute2 } from "node:path";
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 tab closure; inspect and reconcile. No automatic replay."
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[0].pane_id !== r.paneId || members[0].terminal_id !== r.terminalId)
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 tab closure first");
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("tab.close", { tab_id: r.tabId });
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 tab",
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 tab is still present");
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
- var Herdr = class {
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
- return new Promise((resolve13, reject) => {
31280
- const id = randomUUID5();
31281
- let buffer = "";
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
- socket.on(
31307
- "close",
31308
- () => finish(
31309
- new AppError("herdr_disconnected", `${method}: disconnected before acknowledgement`, 503)
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
- socket.on("data", (data) => {
31313
- buffer += data.toString();
31314
- if (buffer.length > 8 * 1024 * 1024)
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 (!isAbsolute2(input.root) || !isAbsolute2(input.socketPath))
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
- const tabs = (await h.call("tab.list", { workspace_id: project.workspaceId })).tabs ?? [];
31705
- const matches = tabs.filter(
31706
- (tab) => tab.workspace_id === project.workspaceId && tab.label === r.agentName
31707
- );
31708
- if (matches.length > 1)
31709
- throw new AppError(
31710
- "identity_changed",
31711
- "Multiple tabs match the interrupted creation; manual inspection is required",
31712
- 409
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
- if (matches.length === 1) {
31715
- const panes = ((await h.call("pane.list", { workspace_id: project.workspaceId })).panes ?? []).filter((pane) => pane.tab_id === matches[0].tab_id);
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
- "The orphan tab is no longer an untouched shell; inspect it before recovery",
32284
+ "Interrupted split is ambiguous or no longer an untouched shell; inspect it before recovery",
31720
32285
  409
31721
32286
  );
31722
- Object.assign(r, {
31723
- paneId: panes[0].pane_id,
31724
- terminalId: panes[0].terminal_id,
31725
- tabId: panes[0].tab_id
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" && isAbsolute2(path))
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" && isAbsolute2(check.path))
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
- const created = await h.call("tab.create", {
32502
- workspace_id: p.workspaceId,
32503
- cwd: t.cwd,
32504
- label: run.agentName,
32505
- focus: false,
32506
- env: { MARIONETTE_WORKER_TOKEN: token, MARIONETTE_TASK_ID: id, MARIONETTE_URL: this.url }
32507
- });
32508
- const pane = created.root_pane;
32509
- if (!pane?.pane_id || pane.workspace_id !== p.workspaceId)
32510
- throw new Error("Herdr did not return the expected scoped root pane");
32511
- Object.assign(run, {
32512
- paneId: pane.pane_id,
32513
- terminalId: pane.terminal_id,
32514
- tabId: pane.tab_id,
32515
- phase: "starting"
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.1";
33605
+ var VERSION = "0.2.2";
32938
33606
  var SETUP_VERSION = 2;
32939
33607
 
32940
33608
  // src/server.ts