github-router 0.3.150 → 0.3.152

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.
@@ -1176,35 +1176,172 @@ var ArtifactError = class extends Error {
1176
1176
  this.detail = args.detail;
1177
1177
  }
1178
1178
  };
1179
+ const AWAIT_DEFAULT_TIMEOUT_MS = 25e3;
1180
+ const AWAIT_ABORT_MARGIN_MS = 5e3;
1181
+ const DEFAULT_RETRY_BASE_MS = 250;
1182
+ const TRANSIENT_RETRIES = 2;
1183
+ const DEFAULT_RETRYABLE_CODES = new Set(["UNREACHABLE", "TIMEOUT"]);
1184
+ const AWAIT_RETRYABLE_CODES = new Set(["UNREACHABLE"]);
1179
1185
  var ArtifactClient = class {
1180
1186
  baseUrl;
1181
1187
  token;
1182
1188
  sessionId;
1183
1189
  fetchFn;
1184
1190
  insecureTLS;
1191
+ retryBaseMs;
1185
1192
  constructor(options) {
1186
1193
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
1187
1194
  this.token = options.token;
1188
1195
  this.sessionId = options.sessionId;
1189
1196
  this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
1190
1197
  this.insecureTLS = options.insecureTLS ?? false;
1198
+ this.retryBaseMs = options.retryBaseMs ?? DEFAULT_RETRY_BASE_MS;
1191
1199
  }
1192
- open(file, signal) {
1193
- return this.request("POST", `/api/artifact/${encodeURIComponent(this.sessionId)}/open`, { file }, signal);
1200
+ /**
1201
+ * Open (or replace) the review for this session. `mode` is advisory metadata
1202
+ * forwarded to the server (forward-compat: ignored if unknown); the actual
1203
+ * interactivity comes from the served HTML's `data-aod-*` markup. Retried on a
1204
+ * transient failure with a STABLE idempotency key so a retry-after-success does
1205
+ * not double-open.
1206
+ */
1207
+ open(file, opts = {}) {
1208
+ const idempotencyKey = opts.idempotencyKey ?? randomUUID();
1209
+ const body = { file };
1210
+ if (opts.mode) body.mode = opts.mode;
1211
+ return this.withRetry(() => this.requestOnce({
1212
+ method: "POST",
1213
+ pathname: this.path("/open"),
1214
+ body,
1215
+ signal: opts.signal,
1216
+ idempotencyKey
1217
+ }), TRANSIENT_RETRIES, opts.signal);
1194
1218
  }
1195
- poll(timeoutMsHint, signal) {
1196
- return this.request("GET", `/api/artifact/${encodeURIComponent(this.sessionId)}/poll`, void 0, signal, timeoutMsHint);
1219
+ /**
1220
+ * Replace the current review's content. Exactly one of `file` | `html` (the
1221
+ * caller-facing tool enforces that). `html` is written by the SERVER to the
1222
+ * review's existing sandboxed file, so `html` with no existing review is an
1223
+ * INVALID_REQUEST from the server. Retried with a stable idempotency key.
1224
+ */
1225
+ update(opts) {
1226
+ const idempotencyKey = opts.idempotencyKey ?? randomUUID();
1227
+ const body = {};
1228
+ if (opts.file !== void 0) body.file = opts.file;
1229
+ if (opts.html !== void 0) body.html = opts.html;
1230
+ return this.withRetry(() => this.requestOnce({
1231
+ method: "POST",
1232
+ pathname: this.path("/update"),
1233
+ body,
1234
+ signal: opts.signal,
1235
+ idempotencyKey,
1236
+ allowEmptyJson: true
1237
+ }), TRANSIENT_RETRIES, opts.signal);
1238
+ }
1239
+ /** Force a reload from disk (no content change). Single-shot (idempotent, cheap). */
1240
+ refresh(signal) {
1241
+ return this.requestOnce({
1242
+ method: "POST",
1243
+ pathname: this.path("/refresh"),
1244
+ signal,
1245
+ allowEmptyJson: true
1246
+ });
1247
+ }
1248
+ /** Hide the panel UI while keeping the review alive. Single-shot; server idempotent. */
1249
+ dismiss(signal) {
1250
+ return this.requestOnce({
1251
+ method: "POST",
1252
+ pathname: this.path("/dismiss"),
1253
+ signal,
1254
+ allowEmptyJson: true
1255
+ });
1256
+ }
1257
+ /**
1258
+ * Typed drain (contract v2.2 §1/§2). Long-holds up to the server cap, returns
1259
+ * events with `id > cursor` plus the new high-water `cursor`. Idempotent by
1260
+ * (cursor, event.id): re-calling with the same cursor replays the same window
1261
+ * from the server's bounded buffer, so a lost cursor (compaction) is
1262
+ * recoverable. Retried on a transient network failure (the cursor makes retry
1263
+ * safe — no double-consumption).
1264
+ */
1265
+ awaitEvents(opts = {}) {
1266
+ const serverTimeoutMs = typeof opts.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) && opts.timeoutMs > 0 ? opts.timeoutMs : AWAIT_DEFAULT_TIMEOUT_MS;
1267
+ const clientTimeoutMs = serverTimeoutMs + AWAIT_ABORT_MARGIN_MS;
1268
+ return this.withRetry(() => this.requestOnce({
1269
+ method: "GET",
1270
+ pathname: this.path("/await"),
1271
+ query: {
1272
+ cursor: opts.cursor,
1273
+ timeoutMs: String(serverTimeoutMs)
1274
+ },
1275
+ signal: opts.signal,
1276
+ timeoutMsHint: clientTimeoutMs
1277
+ }), TRANSIENT_RETRIES, opts.signal, AWAIT_RETRYABLE_CODES);
1197
1278
  }
1279
+ /** Free-text agent->human reply. SINGLE-SHOT (not retried): a retry-after-success
1280
+ * would render a duplicate chat bubble server-side. */
1198
1281
  agentReply(text, signal) {
1199
- return this.request("POST", `/api/artifact/${encodeURIComponent(this.sessionId)}/agent-reply`, { text }, signal, void 0, true);
1282
+ return this.requestOnce({
1283
+ method: "POST",
1284
+ pathname: this.path("/agent-reply"),
1285
+ body: { text },
1286
+ signal,
1287
+ allowEmptyJson: true
1288
+ });
1289
+ }
1290
+ /**
1291
+ * End the review. Retried with a stable idempotency key; a NOT_FOUND (the
1292
+ * review is already ended — whether by a prior call or by a retry landing after
1293
+ * the first attempt succeeded) is mapped to a successful `{ ok, status:"ended" }`
1294
+ * rather than surfaced as a 404 (contract v2.2 §1.1).
1295
+ */
1296
+ async end(signal) {
1297
+ const idempotencyKey = randomUUID();
1298
+ try {
1299
+ return await this.withRetry(() => this.requestOnce({
1300
+ method: "POST",
1301
+ pathname: this.path("/end"),
1302
+ signal,
1303
+ idempotencyKey,
1304
+ allowEmptyJson: true
1305
+ }), TRANSIENT_RETRIES, signal);
1306
+ } catch (err) {
1307
+ if (err instanceof ArtifactError && err.code === "NOT_FOUND") return {
1308
+ ok: true,
1309
+ status: "ended"
1310
+ };
1311
+ throw err;
1312
+ }
1200
1313
  }
1201
- end(signal) {
1202
- return this.request("POST", `/api/artifact/${encodeURIComponent(this.sessionId)}/end`, void 0, signal, void 0, true);
1314
+ /**
1315
+ * FROZEN legacy long-poll (contract v2.2 §1). Old payload shape,
1316
+ * comment-equivalent only. New agents use `awaitEvents`. Single request per
1317
+ * call (the caller-facing tool owns the bounded re-poll budget).
1318
+ */
1319
+ poll(timeoutMsHint, signal) {
1320
+ return this.requestOnce({
1321
+ method: "GET",
1322
+ pathname: this.path("/poll"),
1323
+ signal,
1324
+ timeoutMsHint
1325
+ });
1203
1326
  }
1204
- async request(method, pathname, body, signal, timeoutMsHint, allowEmptyJson = false) {
1327
+ path(suffix) {
1328
+ return `/api/artifact/${encodeURIComponent(this.sessionId)}${suffix}`;
1329
+ }
1330
+ async withRetry(fn, retries, signal, retryableCodes = DEFAULT_RETRYABLE_CODES) {
1331
+ let attempt = 0;
1332
+ for (;;) try {
1333
+ return await fn();
1334
+ } catch (err) {
1335
+ attempt += 1;
1336
+ if (!(err instanceof ArtifactError && err.retryable && retryableCodes.has(err.code)) || attempt > retries || signal?.aborted) throw err;
1337
+ const base = this.retryBaseMs;
1338
+ await sleep$1(base <= 0 ? 0 : Math.round(base * 2 ** (attempt - 1) * (.5 + Math.random() * .5)), signal);
1339
+ }
1340
+ }
1341
+ async requestOnce(o) {
1205
1342
  let url;
1206
1343
  try {
1207
- url = new URL(pathname, `${this.baseUrl}/`);
1344
+ url = new URL(o.pathname, `${this.baseUrl}/`);
1208
1345
  } catch (err) {
1209
1346
  throw new ArtifactError({
1210
1347
  code: "UNREACHABLE",
@@ -1213,48 +1350,73 @@ var ArtifactClient = class {
1213
1350
  detail: err
1214
1351
  });
1215
1352
  }
1216
- const timeout = combineSignalAndTimeout(signal, timeoutMsHint);
1217
- let response;
1353
+ if (o.query) {
1354
+ for (const [key, value] of Object.entries(o.query)) if (value !== void 0) url.searchParams.set(key, value);
1355
+ }
1356
+ const timeout = combineSignalAndTimeout(o.signal, o.timeoutMsHint);
1218
1357
  try {
1358
+ const headers = { Authorization: `Bearer ${this.token}` };
1359
+ if (o.body !== void 0) headers["Content-Type"] = "application/json";
1360
+ if (o.idempotencyKey) headers["Idempotency-Key"] = o.idempotencyKey;
1219
1361
  const init = {
1220
- method,
1221
- headers: {
1222
- Authorization: `Bearer ${this.token}`,
1223
- ...body === void 0 ? {} : { "Content-Type": "application/json" }
1224
- },
1225
- body: body === void 0 ? void 0 : JSON.stringify(body),
1362
+ method: o.method,
1363
+ headers,
1364
+ body: o.body === void 0 ? void 0 : JSON.stringify(o.body),
1226
1365
  redirect: "error",
1227
1366
  signal: timeout.signal
1228
1367
  };
1229
1368
  if (this.insecureTLS) applyInsecureTls(init);
1230
- response = await this.fetchFn(url.toString(), init);
1369
+ const response = await this.fetchFn(url.toString(), init);
1370
+ if (!response.ok) throw await mapHttpError$1(response);
1371
+ let text;
1372
+ try {
1373
+ text = await response.text();
1374
+ } catch (err) {
1375
+ if (isAbortLike$2(err)) throw mapNetworkError$1(err);
1376
+ throw new ArtifactError({
1377
+ code: "INVALID_RESPONSE",
1378
+ message: "artifact API response body could not be read",
1379
+ retryable: false,
1380
+ detail: err
1381
+ });
1382
+ }
1383
+ if (!text && o.allowEmptyJson) return {};
1384
+ try {
1385
+ return JSON.parse(text);
1386
+ } catch (err) {
1387
+ throw new ArtifactError({
1388
+ code: "INVALID_RESPONSE",
1389
+ message: "artifact API returned a non-JSON response",
1390
+ retryable: false,
1391
+ detail: err
1392
+ });
1393
+ }
1231
1394
  } catch (err) {
1395
+ if (err instanceof ArtifactError) throw err;
1232
1396
  throw mapNetworkError$1(err);
1233
1397
  } finally {
1234
1398
  timeout.cleanup();
1235
1399
  }
1236
- if (!response.ok) throw await mapHttpError$1(response);
1237
- const text = await response.text().catch((err) => {
1238
- throw new ArtifactError({
1239
- code: "INVALID_RESPONSE",
1240
- message: "artifact API response body could not be read",
1241
- retryable: false,
1242
- detail: err
1243
- });
1244
- });
1245
- if (!text && allowEmptyJson) return {};
1246
- try {
1247
- return JSON.parse(text);
1248
- } catch (err) {
1249
- throw new ArtifactError({
1250
- code: "INVALID_RESPONSE",
1251
- message: "artifact API returned a non-JSON response",
1252
- retryable: false,
1253
- detail: err
1254
- });
1255
- }
1256
1400
  }
1257
1401
  };
1402
+ function sleep$1(ms, signal) {
1403
+ if (ms <= 0) return Promise.resolve();
1404
+ return new Promise((resolve, reject) => {
1405
+ const onAbort = () => {
1406
+ clearTimeout(timer);
1407
+ reject(new DOMException("retry backoff aborted", "AbortError"));
1408
+ };
1409
+ const timer = setTimeout(() => {
1410
+ signal?.removeEventListener("abort", onAbort);
1411
+ resolve();
1412
+ }, ms);
1413
+ if (signal?.aborted) {
1414
+ onAbort();
1415
+ return;
1416
+ }
1417
+ signal?.addEventListener("abort", onAbort, { once: true });
1418
+ });
1419
+ }
1258
1420
  function combineSignalAndTimeout(signal, timeoutMsHint) {
1259
1421
  const timeoutMs = typeof timeoutMsHint === "number" && Number.isFinite(timeoutMsHint) && timeoutMsHint > 0 ? timeoutMsHint : void 0;
1260
1422
  if (timeoutMs === void 0) return {
@@ -1311,6 +1473,13 @@ async function mapHttpError$1(response) {
1311
1473
  status: response.status,
1312
1474
  detail
1313
1475
  });
1476
+ if (response.status === 400 && detailToCode(detail)?.toUpperCase() === "INVALID_REQUEST") return new ArtifactError({
1477
+ code: "INVALID_REQUEST",
1478
+ message: `artifact API rejected the request (400)${suffix}`,
1479
+ retryable: false,
1480
+ status: response.status,
1481
+ detail
1482
+ });
1314
1483
  return new ArtifactError({
1315
1484
  code: "UPSTREAM_ERROR",
1316
1485
  message: `artifact API returned HTTP ${response.status}${suffix}`,
@@ -1355,6 +1524,18 @@ function detailToMessage$1(detail) {
1355
1524
  }
1356
1525
  if (typeof record.message === "string") return record.message;
1357
1526
  }
1527
+ /** Extract a machine `code` from an upstream error body (`{error:{code}}` or
1528
+ * `{code}`) so a tagged 400 can be classified (INVALID_REQUEST). */
1529
+ function detailToCode(detail) {
1530
+ if (typeof detail !== "object" || detail === null) return void 0;
1531
+ const record = detail;
1532
+ const error = record.error;
1533
+ if (typeof error === "object" && error !== null) {
1534
+ const code = error.code;
1535
+ if (typeof code === "string") return code;
1536
+ }
1537
+ if (typeof record.code === "string") return record.code;
1538
+ }
1358
1539
  function isAbortLike$2(err) {
1359
1540
  return err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError");
1360
1541
  }
@@ -1383,27 +1564,86 @@ function tool(toolNameHttp, description, inputSchema, handler) {
1383
1564
  };
1384
1565
  }
1385
1566
  const ARTIFACT_TOOLS = Object.freeze([
1386
- tool("artifact_open", "Open a workspace file in ai-or-die's Artifact review panel for human review. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ file: stringProp$2("Workspace-relative or absolute file path to show in the Artifact panel.") }, ["file"]), async (args, signal) => {
1567
+ tool("artifact_open", "Open a workspace file in ai-or-die's Artifact review panel for human review. Pass mode:\"interactive\" when the HTML carries data-aod-* action controls. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1568
+ file: stringProp$2("Workspace-relative or absolute file path to show in the Artifact panel."),
1569
+ mode: enumProp(["static", "interactive"], "Advisory. \"interactive\" signals the HTML contains data-aod-* action controls the panel should wire; \"static\" (default) is a read-and-annotate artifact.")
1570
+ }, ["file"]), async (args, signal) => {
1387
1571
  const env = readArtifactEnv();
1388
1572
  if (!env) return missingEnvResult();
1389
1573
  const file = requiredString$2(args, "file");
1574
+ const mode = optionalEnum(args, "mode", ["static", "interactive"]);
1575
+ const response = await clientFromEnv(env).open(file, {
1576
+ mode,
1577
+ signal
1578
+ });
1390
1579
  return ok$2({
1391
- viewUrl: (await clientFromEnv(env).open(file, signal)).viewUrl,
1392
- next_step: "Tell the user to review at the Artifact panel, then call artifact_poll."
1580
+ viewUrl: response.viewUrl,
1581
+ sessionId: response.sessionId,
1582
+ key: response.key,
1583
+ next_step: "Tell the user to review at the Artifact panel, then call artifact_await to receive their feedback."
1393
1584
  });
1394
1585
  }),
1395
- tool("artifact_poll", "Wait for human Artifact review feedback from ai-or-die and return the prompts/layout warnings/DOM snapshot for the agent to act on. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1586
+ tool("artifact_update", "Replace the current Artifact review's content in place. Provide EXACTLY ONE of file (a workspace file path) or html (raw HTML the server writes to the review's sandboxed file). html requires an already-open review. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1587
+ file: stringProp$2("Workspace-relative or absolute file path to become the review's new content."),
1588
+ html: stringProp$2("Raw HTML to write into the review's existing sandboxed file, then reload."),
1589
+ idempotencyKey: stringProp$2("Optional stable key so a retried update is de-duplicated by the server.")
1590
+ }, []), async (args, signal) => {
1396
1591
  const env = readArtifactEnv();
1397
1592
  if (!env) return missingEnvResult();
1398
- return ok$2(formatPollResponse(await pollUntilReady(clientFromEnv(env), signal)));
1593
+ const file = optionalString$2(args, "file");
1594
+ const html = optionalString$2(args, "html");
1595
+ if (file === void 0 === (html === void 0)) throw new ArtifactToolInputError("INVALID_ARGUMENT", "artifact_update requires EXACTLY ONE of arguments.file or arguments.html");
1596
+ const idempotencyKey = optionalString$2(args, "idempotencyKey");
1597
+ return ok$2({
1598
+ ...await clientFromEnv(env).update({
1599
+ file,
1600
+ html,
1601
+ idempotencyKey,
1602
+ signal
1603
+ }),
1604
+ ok: true,
1605
+ next_step: "The panel now shows the updated content. Call artifact_await for further feedback."
1606
+ });
1607
+ }),
1608
+ tool("artifact_refresh", "Force the ai-or-die Artifact panel to reload the current artifact from disk (no content change). Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1609
+ const env = readArtifactEnv();
1610
+ if (!env) return missingEnvResult();
1611
+ return ok$2({
1612
+ ...await clientFromEnv(env).refresh(signal),
1613
+ ok: true,
1614
+ next_step: "The panel reloaded the artifact. Call artifact_await for feedback."
1615
+ });
1616
+ }),
1617
+ tool("artifact_await", "Wait for the human's next Artifact review events (typed drain: comments AND structured action-button/checkbox events) and return them with a cursor. Pass the returned cursor on the next call to receive only newer events. Supersedes artifact_poll. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({
1618
+ cursor: stringProp$2("High-water cursor from the previous artifact_await response. Omit on the first call."),
1619
+ timeoutMs: numberProp$2("Optional server long-hold budget in ms (default ~25000).")
1620
+ }, []), async (args, signal) => {
1621
+ const env = readArtifactEnv();
1622
+ if (!env) return missingEnvResult();
1623
+ const cursor = optionalString$2(args, "cursor");
1624
+ const timeoutMs = optionalNumber$2(args, "timeoutMs");
1625
+ return ok$2(formatAwaitResponse(await clientFromEnv(env).awaitEvents({
1626
+ cursor,
1627
+ timeoutMs,
1628
+ signal
1629
+ })));
1630
+ }),
1631
+ tool("artifact_dismiss", "Hide the ai-or-die Artifact panel UI while keeping the review alive (queued feedback preserved, channel open, re-openable). Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({}, []), async (_args, signal) => {
1632
+ const env = readArtifactEnv();
1633
+ if (!env) return missingEnvResult();
1634
+ return ok$2({
1635
+ ...await clientFromEnv(env).dismiss(signal),
1636
+ ok: true,
1637
+ next_step: "The panel is hidden but the review is still live. Re-open the artifact or call artifact_await when ready."
1638
+ });
1399
1639
  }),
1400
1640
  tool("artifact_reply", "Send the agent's reply back to the ai-or-die Artifact review panel after applying or responding to human feedback. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ text: stringProp$2("Agent reply text to deliver to the human Artifact review panel.") }, ["text"]), async (args, signal) => {
1401
1641
  const env = readArtifactEnv();
1402
1642
  if (!env) return missingEnvResult();
1403
1643
  const text = requiredString$2(args, "text");
1404
1644
  return ok$2({
1405
- ok: true,
1406
1645
  ...await clientFromEnv(env).agentReply(text, signal),
1646
+ ok: true,
1407
1647
  next_step: "Wait for further human review, or continue if the review loop is complete."
1408
1648
  });
1409
1649
  }),
@@ -1411,10 +1651,15 @@ const ARTIFACT_TOOLS = Object.freeze([
1411
1651
  const env = readArtifactEnv();
1412
1652
  if (!env) return missingEnvResult();
1413
1653
  return ok$2({
1414
- ok: true,
1415
1654
  ...await clientFromEnv(env).end(signal),
1655
+ ok: true,
1416
1656
  next_step: "Artifact review loop ended."
1417
1657
  });
1658
+ }),
1659
+ tool("artifact_poll", "FROZEN legacy alias for artifact_await (old payload, human comments only, no structured actions). New agents should call artifact_await instead. Only works inside an ai-or-die tab-backed Claude session.", objectSchema$2({ timeoutMs: numberProp$2("Optional per-call budget hint in ms (advisory).") }, []), async (_args, signal) => {
1660
+ const env = readArtifactEnv();
1661
+ if (!env) return missingEnvResult();
1662
+ return ok$2(formatPollResponse(await pollUntilReady(clientFromEnv(env), signal)));
1418
1663
  })
1419
1664
  ]);
1420
1665
  function readArtifactEnv() {
@@ -1489,6 +1734,30 @@ function formatPollResponse(response) {
1489
1734
  next_step: response.next_step ?? defaultPollNextStep(response.status)
1490
1735
  });
1491
1736
  }
1737
+ /**
1738
+ * Shape the typed drain for the model: pass events through verbatim (unknown
1739
+ * `kind`s preserved — the model ignores what it does not understand), echo the
1740
+ * cursor to thread into the next call, and pick a next_step from the events.
1741
+ */
1742
+ function formatAwaitResponse(response) {
1743
+ const events$1 = Array.isArray(response.events) ? response.events : [];
1744
+ const status = typeof response.status === "string" ? response.status : void 0;
1745
+ return definedObject$1({
1746
+ events: events$1,
1747
+ status,
1748
+ cursor: response.cursor,
1749
+ next_step: awaitNextStep(status ?? "", events$1)
1750
+ });
1751
+ }
1752
+ function awaitNextStep(status, events$1) {
1753
+ if ((status ?? "").toLowerCase() === "ended") return "The review has ended. No further feedback will arrive.";
1754
+ if (events$1.length === 0) return "No feedback yet. Call artifact_await again, passing the returned cursor.";
1755
+ const hasAction = events$1.some((e) => e.kind === "action");
1756
+ const hasComment = events$1.some((e) => e.kind === "comment");
1757
+ if (hasAction && hasComment) return "Act on the action events (buttons/checkboxes) and the comments, reply with artifact_reply, then call artifact_await with the returned cursor.";
1758
+ if (hasAction) return "The human triggered action controls. Act on them, optionally artifact_reply, then call artifact_await with the returned cursor.";
1759
+ return "Apply the human comments, call artifact_reply with a concise summary, then artifact_await with the returned cursor.";
1760
+ }
1492
1761
  function defaultPollNextStep(status) {
1493
1762
  return isWaitingStatus(status) ? "No human feedback is ready yet. Call artifact_poll again." : "Apply the human Artifact review feedback, then call artifact_reply with a concise summary.";
1494
1763
  }
@@ -1501,6 +1770,24 @@ function requiredString$2(args, key) {
1501
1770
  if (typeof value !== "string" || value.trim() === "") throw new ArtifactToolInputError("INVALID_ARGUMENT", `arguments.${key} is required and must be a non-empty string`);
1502
1771
  return value;
1503
1772
  }
1773
+ function optionalString$2(args, key) {
1774
+ const value = args[key];
1775
+ if (value === void 0 || value === null) return void 0;
1776
+ if (typeof value !== "string" || value.trim() === "") throw new ArtifactToolInputError("INVALID_ARGUMENT", `arguments.${key} must be a non-empty string when provided`);
1777
+ return value;
1778
+ }
1779
+ function optionalNumber$2(args, key) {
1780
+ const value = args[key];
1781
+ if (value === void 0 || value === null) return void 0;
1782
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new ArtifactToolInputError("INVALID_ARGUMENT", `arguments.${key} must be a positive number when provided`);
1783
+ return value;
1784
+ }
1785
+ function optionalEnum(args, key, allowed) {
1786
+ const value = optionalString$2(args, key);
1787
+ if (value === void 0) return void 0;
1788
+ if (!allowed.includes(value)) throw new ArtifactToolInputError("INVALID_ARGUMENT", `arguments.${key} must be one of ${allowed.join(", ")}`);
1789
+ return value;
1790
+ }
1504
1791
  var ArtifactToolInputError = class extends Error {
1505
1792
  code;
1506
1793
  constructor(code, message) {
@@ -1565,6 +1852,19 @@ function stringProp$2(description) {
1565
1852
  description
1566
1853
  };
1567
1854
  }
1855
+ function numberProp$2(description) {
1856
+ return {
1857
+ type: "number",
1858
+ description
1859
+ };
1860
+ }
1861
+ function enumProp(values, description) {
1862
+ return {
1863
+ type: "string",
1864
+ enum: [...values],
1865
+ description
1866
+ };
1867
+ }
1568
1868
 
1569
1869
  //#endregion
1570
1870
  //#region src/lib/fleet/mesh-egress-agent.ts
@@ -2673,6 +2973,464 @@ var MergedFleetRegistry = class {
2673
2973
  }
2674
2974
  };
2675
2975
 
2976
+ //#endregion
2977
+ //#region src/lib/fleet/driver.ts
2978
+ /** ai-or-die named keys (raw:false maps these; never a literal "\r"). C4. */
2979
+ const SUBMIT_KEY = "enter";
2980
+ const INTERRUPT_KEY = "ctrl-c";
2981
+ /** The control events that RELIABLY mark a turn boundary (transcript-derived). */
2982
+ const TURN_SETTLE_KINDS = ["turn_ended", "waiting_input"];
2983
+ const DEFAULT_READY_POLL_MS = 500;
2984
+ const DEFAULT_TURN_POLL_MS = 25e3;
2985
+ const DEFAULT_PRIME_TIMEOUT_MS = 0;
2986
+ const DEFAULT_IDLE_WAIT_MS = 2e3;
2987
+ const DEFAULT_RECOVER_MS = 15e3;
2988
+ const DEFAULT_TAIL_LINES = 200;
2989
+ function realSleep(ms) {
2990
+ if (ms <= 0) return Promise.resolve();
2991
+ return new Promise((resolve) => setTimeout(resolve, ms));
2992
+ }
2993
+ /** Map a named op to the ai-or-die named key. `submit` = Enter, `interrupt` = Ctrl-C.
2994
+ * Callers send the returned key with raw:false so ai-or-die interprets the NAMED
2995
+ * key — never a literal control byte. */
2996
+ function mapNamedKeyOp(op) {
2997
+ switch (op) {
2998
+ case "submit": return SUBMIT_KEY;
2999
+ case "interrupt": return INTERRUPT_KEY;
3000
+ }
3001
+ }
3002
+ function isNamedKeyOp(value) {
3003
+ return value === "submit" || value === "interrupt";
3004
+ }
3005
+ /** Read `awaiting.kind` off a status defensively (the shape is `{ kind, ... }`). */
3006
+ function readAwaitingKind(status) {
3007
+ const awaiting = status?.awaiting;
3008
+ if (awaiting && typeof awaiting === "object") {
3009
+ const kind = awaiting.kind;
3010
+ if (typeof kind === "string" && kind.trim() !== "") return kind;
3011
+ }
3012
+ }
3013
+ /**
3014
+ * Classify whether a free-text message may be submitted to a session RIGHT NOW.
3015
+ * Ready only when the session is idle or explicitly awaiting the next message.
3016
+ * A pending non-message prompt (plan_approval / choice_question / tool_approval /
3017
+ * trust_prompt) is `awaiting_other` — the caller should use `respond`, not a raw
3018
+ * message. `busy` / `terminal` are hard refusals; `unknown` carries no positive
3019
+ * evidence of a busy composer (the caller fails OPEN and lets the send surface its
3020
+ * own transport result).
3021
+ */
3022
+ function classifyMessageReadiness(status) {
3023
+ const interactionState = typeof status?.interactionState === "string" ? status.interactionState : void 0;
3024
+ const awaitingKind = readAwaitingKind(status);
3025
+ if (interactionState === "busy") return {
3026
+ ready: false,
3027
+ reason: "busy",
3028
+ interactionState,
3029
+ awaitingKind
3030
+ };
3031
+ if (interactionState === "exited" || interactionState === "crashed") return {
3032
+ ready: false,
3033
+ reason: "terminal",
3034
+ interactionState
3035
+ };
3036
+ if (awaitingKind !== void 0 && awaitingKind !== "next_message") return {
3037
+ ready: false,
3038
+ reason: "awaiting_other",
3039
+ interactionState,
3040
+ awaitingKind
3041
+ };
3042
+ if (interactionState === "idle") return {
3043
+ ready: true,
3044
+ reason: "idle",
3045
+ interactionState
3046
+ };
3047
+ if (awaitingKind === "next_message" || interactionState === "waiting_input") return {
3048
+ ready: true,
3049
+ reason: "awaiting_message",
3050
+ interactionState,
3051
+ awaitingKind
3052
+ };
3053
+ return {
3054
+ ready: false,
3055
+ reason: "unknown",
3056
+ interactionState,
3057
+ awaitingKind
3058
+ };
3059
+ }
3060
+ /** A refusal reason with POSITIVE evidence the composer must not be typed into. */
3061
+ function isHardNotReady(reason) {
3062
+ return reason === "busy" || reason === "awaiting_other" || reason === "terminal";
3063
+ }
3064
+ /**
3065
+ * Poll `/status` until the session is ready for a message or the wait budget is
3066
+ * spent. A status probe that THROWS fails OPEN (reports `unknown` and returns) so a
3067
+ * transient status hiccup never wedges a legitimate send — the send itself carries
3068
+ * ai-or-die's own submission signal.
3069
+ */
3070
+ async function waitForMessageReady(client, localId, options = {}) {
3071
+ const now = options.now ?? Date.now;
3072
+ const sleep$2 = options.sleep ?? realSleep;
3073
+ const waitMs = Math.max(0, options.waitMs ?? 0);
3074
+ const pollMs = Math.max(1, options.pollMs ?? DEFAULT_READY_POLL_MS);
3075
+ const deadline = now() + waitMs;
3076
+ let last = {
3077
+ ready: false,
3078
+ reason: "unknown"
3079
+ };
3080
+ for (;;) {
3081
+ let status;
3082
+ try {
3083
+ status = (await client.status(localId, options.signal)).status;
3084
+ } catch {
3085
+ return {
3086
+ ready: false,
3087
+ readiness: {
3088
+ ready: false,
3089
+ reason: "unknown"
3090
+ },
3091
+ statusError: true
3092
+ };
3093
+ }
3094
+ last = classifyMessageReadiness(status);
3095
+ if (last.ready) return {
3096
+ ready: true,
3097
+ readiness: last
3098
+ };
3099
+ const remaining = deadline - now();
3100
+ if (remaining <= 0) return {
3101
+ ready: false,
3102
+ readiness: last
3103
+ };
3104
+ await sleep$2(Math.min(pollMs, remaining));
3105
+ }
3106
+ }
3107
+ /**
3108
+ * Classify a batch of already-stamped events per session for the `await_turn`
3109
+ * summary. `turn_ended` -> completed, `waiting_input` -> awaiting_input (both
3110
+ * reliable / transcript-derived); a bare `became_idle` is surfaced as `idle_flicker`
3111
+ * with reliable:false so a caller NEVER mistakes the PTY heuristic for completion.
3112
+ * `became_busy` and other kinds are ignored (not a settle signal).
3113
+ */
3114
+ function classifyTurnEvents(events$1) {
3115
+ const rank = {
3116
+ completed: 3,
3117
+ awaiting_input: 2,
3118
+ idle_flicker: 1
3119
+ };
3120
+ const best = /* @__PURE__ */ new Map();
3121
+ for (const event of events$1) {
3122
+ const sessionId = typeof event.sessionId === "string" ? event.sessionId : void 0;
3123
+ const kind = typeof event.kind === "string" ? event.kind : void 0;
3124
+ if (sessionId === void 0 || kind === void 0) continue;
3125
+ let status;
3126
+ if (kind === "turn_ended") status = "completed";
3127
+ else if (kind === "waiting_input") status = "awaiting_input";
3128
+ else if (kind === "became_idle") status = "idle_flicker";
3129
+ if (status === void 0) continue;
3130
+ const prior = best.get(sessionId);
3131
+ if (prior === void 0 || rank[status] > rank[prior]) best.set(sessionId, status);
3132
+ }
3133
+ return [...best.entries()].map(([sessionId, status]) => ({
3134
+ sessionId,
3135
+ status,
3136
+ reliable: status !== "idle_flicker"
3137
+ }));
3138
+ }
3139
+ function pickSettleEvent(events$1, localId) {
3140
+ let awaiting;
3141
+ for (const event of events$1) {
3142
+ if (event.sessionId !== localId) continue;
3143
+ if (event.kind === "turn_ended") return event;
3144
+ if (event.kind === "waiting_input" && awaiting === void 0) awaiting = event;
3145
+ }
3146
+ return awaiting;
3147
+ }
3148
+ /** Client-side backoff after a transient waitEvents failure so the loop cannot
3149
+ * hot-spin (hammer the control plane) when polls fail fast (network down). */
3150
+ const TURN_POLL_ERROR_BACKOFF_MS = 250;
3151
+ /** Attempts to obtain a starting cursor before a drive send (see driveTask step 2). */
3152
+ const PRIME_CURSOR_ATTEMPTS = 3;
3153
+ /**
3154
+ * Obtain a starting `/events` cursor before sending, retrying a bounded number of
3155
+ * times. A cursor is what prevents a stale prior-turn `turn_ended` from satisfying
3156
+ * the post-send wait; a cursorless request may replay history. Retries are cheap
3157
+ * (a zero/short poll). Returns undefined only if every attempt fails (rare) or the
3158
+ * signal aborts — the caller then proceeds best-effort.
3159
+ */
3160
+ async function primeTurnCursor(client, localId, timeoutMs, signal) {
3161
+ for (let attempt = 0; attempt < PRIME_CURSOR_ATTEMPTS; attempt++) {
3162
+ if (signal?.aborted) return void 0;
3163
+ try {
3164
+ return (await client.waitEvents({
3165
+ sessionIds: [localId],
3166
+ kinds: [...TURN_SETTLE_KINDS],
3167
+ timeoutMs
3168
+ }, signal)).cursor;
3169
+ } catch {}
3170
+ }
3171
+ }
3172
+ /**
3173
+ * Wait until the CURRENT turn actually ends (`turn_ended`) or the session is
3174
+ * awaiting input (`waiting_input`), long-polling `/events` filtered to those kinds
3175
+ * and advancing the server cursor across windows. Returns `{settled:false,
3176
+ * reason:"timeout"}` when neither fires within `timeoutMs`. It NEVER settles on
3177
+ * `became_idle`. Prime a cursor BEFORE the send (a zero/short poll) and pass it in
3178
+ * so a stale prior-turn event cannot satisfy the wait.
3179
+ */
3180
+ async function waitForTurnSettled(client, localId, options) {
3181
+ const now = options.now ?? Date.now;
3182
+ const sleep$2 = options.sleep ?? realSleep;
3183
+ const pollTimeoutMs = Math.max(1, options.pollTimeoutMs ?? DEFAULT_TURN_POLL_MS);
3184
+ const budget = Number.isFinite(options.timeoutMs) ? Math.max(0, options.timeoutMs) : 0;
3185
+ const deadline = now() + budget;
3186
+ let cursor = options.cursor;
3187
+ do {
3188
+ if (options.signal?.aborted) return {
3189
+ settled: false,
3190
+ reason: "aborted",
3191
+ cursor
3192
+ };
3193
+ const remaining = deadline - now();
3194
+ const poll = remaining <= 0 ? 0 : Math.min(pollTimeoutMs, remaining);
3195
+ let response;
3196
+ try {
3197
+ response = await client.waitEvents({
3198
+ sessionIds: [localId],
3199
+ kinds: [...TURN_SETTLE_KINDS],
3200
+ timeoutMs: poll,
3201
+ cursor
3202
+ }, options.signal);
3203
+ } catch {
3204
+ if (options.signal?.aborted) return {
3205
+ settled: false,
3206
+ reason: "aborted",
3207
+ cursor
3208
+ };
3209
+ if (now() >= deadline) return {
3210
+ settled: false,
3211
+ reason: "timeout",
3212
+ cursor
3213
+ };
3214
+ await sleep$2(Math.min(TURN_POLL_ERROR_BACKOFF_MS, Math.max(0, deadline - now())));
3215
+ continue;
3216
+ }
3217
+ cursor = response.cursor;
3218
+ const hit = pickSettleEvent(response.events, localId);
3219
+ if (hit) return {
3220
+ settled: true,
3221
+ reason: hit.kind === "turn_ended" ? "turn_ended" : "waiting_input",
3222
+ cursor,
3223
+ event: hit
3224
+ };
3225
+ } while (now() < deadline);
3226
+ return {
3227
+ settled: false,
3228
+ reason: "timeout",
3229
+ cursor
3230
+ };
3231
+ }
3232
+ const OPERATOR_REPORT_HEADER = "=== OPERATOR REPORT ===";
3233
+ const OPERATOR_REPORT_FOOTER = "=== END OPERATOR REPORT ===";
3234
+ const OPERATOR_REPORT_LABELS = [
3235
+ "REPORT_ID",
3236
+ "STATE",
3237
+ "SUMMARY",
3238
+ "ASK",
3239
+ "ARTIFACT"
3240
+ ];
3241
+ /**
3242
+ * Parse the LAST OPERATOR REPORT trailer in a transcript tail into typed fields.
3243
+ * Robust to: no trailer (returns state:"unknown", found:false), a missing footer,
3244
+ * multi-line field values (a value runs until the next known label or the footer),
3245
+ * and case-insensitive labels. Lines before the first label are ignored.
3246
+ */
3247
+ function parseOperatorReport(text) {
3248
+ const raw = text ?? "";
3249
+ const headerIdx = raw.lastIndexOf(OPERATOR_REPORT_HEADER);
3250
+ if (headerIdx === -1) return {
3251
+ state: "unknown",
3252
+ raw,
3253
+ found: false
3254
+ };
3255
+ let block = raw.slice(headerIdx + 23);
3256
+ const footerIdx = block.indexOf(OPERATOR_REPORT_FOOTER);
3257
+ if (footerIdx !== -1) block = block.slice(0, footerIdx);
3258
+ const values = {};
3259
+ let current;
3260
+ for (const line of block.split(/\r?\n/)) {
3261
+ const match = /^\s*([A-Za-z_]+)\s*:\s*(.*)$/.exec(line);
3262
+ const label = match?.[1]?.toUpperCase();
3263
+ if (match && label && OPERATOR_REPORT_LABELS.includes(label)) {
3264
+ current = label;
3265
+ values[current] = [match[2] ?? ""];
3266
+ } else if (current) values[current].push(line);
3267
+ }
3268
+ const join$1 = (key) => {
3269
+ const parts = values[key];
3270
+ if (parts === void 0) return void 0;
3271
+ const joined = parts.join("\n").trim();
3272
+ if (joined === "") return void 0;
3273
+ if (/^<[^>\n]*\s[^>\n]*>$/.test(joined)) return void 0;
3274
+ return joined;
3275
+ };
3276
+ return {
3277
+ state: join$1("STATE") ?? "unknown",
3278
+ summary: join$1("SUMMARY"),
3279
+ ask: join$1("ASK"),
3280
+ artifact: join$1("ARTIFACT"),
3281
+ reportId: join$1("REPORT_ID"),
3282
+ raw,
3283
+ found: true
3284
+ };
3285
+ }
3286
+ /** The trailer instruction `drive_task` appends when `expectReport` is on, so a
3287
+ * driven session ends its turn with a parseable {@link parseOperatorReport} block.
3288
+ * When `reportId` is supplied it is embedded as a `REPORT_ID` line the session must
3289
+ * copy verbatim, so the driver can confirm the parsed report is for THIS turn and not
3290
+ * a stale prior-turn trailer still inside the transcript tail window. */
3291
+ function operatorReportInstruction(reportId) {
3292
+ const lines = [
3293
+ "",
3294
+ reportId !== void 0 ? "When you have completely finished this task, end your FINAL message with EXACTLY this trailer. Copy the REPORT_ID line verbatim and fill in each other field:" : "When you have completely finished this task, end your FINAL message with EXACTLY this trailer, filling in each field:",
3295
+ OPERATOR_REPORT_HEADER
3296
+ ];
3297
+ if (reportId !== void 0) lines.push(`REPORT_ID: ${reportId}`);
3298
+ lines.push("STATE: <done | blocked | needs_input | in_progress>", "SUMMARY: <1-3 sentence summary of what you did>", "ASK: <what you need from the operator, or 'none'>", "ARTIFACT: <path or URL to the primary artifact, or 'none'>", OPERATOR_REPORT_FOOTER);
3299
+ return lines.join("\n");
3300
+ }
3301
+ function notReadyState(reason) {
3302
+ switch (reason) {
3303
+ case "busy": return "busy";
3304
+ case "awaiting_other": return "awaiting_other";
3305
+ case "terminal": return "dead";
3306
+ default: return "unknown";
3307
+ }
3308
+ }
3309
+ async function readTail(client, localId, lines, signal) {
3310
+ try {
3311
+ const response = await client.readSession(localId, lines, signal);
3312
+ return typeof response.text === "string" ? response.text : "";
3313
+ } catch {
3314
+ return "";
3315
+ }
3316
+ }
3317
+ /**
3318
+ * Drive one prompt on a session to completion and return the parsed operator report.
3319
+ * Flow: ensure the composer is idle (C1) -> send + surface whether the bytes reached
3320
+ * the composer (C2 `submitted`; a delivered-but-unconfirmed send still proceeds, since
3321
+ * the turn wait + timeout recovery covers the "nothing landed" case) -> wait for
3322
+ * `turn_ended`/`waiting_input` (C2) -> read the transcript tail -> parse the operator
3323
+ * report trailer -> if the turn did not settle in `timeoutMs`, AUTO-RECOVER via a
3324
+ * Ctrl-C interrupt (C4) rather than blocking (~10 min stop-hook hang), then re-wait
3325
+ * briefly and re-read. Robust to a busy session, a missing trailer (state:"unknown"),
3326
+ * and a hung stop hook.
3327
+ */
3328
+ async function driveTask(deps) {
3329
+ const { client, localId, prompt, timeoutMs, expectReport, idempotencyKey, interruptKey, reportId, signal } = deps;
3330
+ const now = deps.now ?? Date.now;
3331
+ const sleep$2 = deps.sleep ?? realSleep;
3332
+ const tailLines = deps.tailLines ?? DEFAULT_TAIL_LINES;
3333
+ const pollTimeoutMs = deps.pollTimeoutMs;
3334
+ const readyResult = await waitForMessageReady(client, localId, {
3335
+ waitMs: deps.idleWaitMs ?? DEFAULT_IDLE_WAIT_MS,
3336
+ now,
3337
+ sleep: sleep$2,
3338
+ signal
3339
+ });
3340
+ if (!readyResult.ready && isHardNotReady(readyResult.readiness.reason)) return {
3341
+ submitted: false,
3342
+ delivered: false,
3343
+ settled: "timeout",
3344
+ state: notReadyState(readyResult.readiness.reason),
3345
+ raw: "",
3346
+ reportFound: false,
3347
+ interrupted: false,
3348
+ recovered: false,
3349
+ notReady: true,
3350
+ readiness: readyResult.readiness,
3351
+ error: "not_ready"
3352
+ };
3353
+ let cursor = await primeTurnCursor(client, localId, deps.primeTimeoutMs ?? DEFAULT_PRIME_TIMEOUT_MS, signal);
3354
+ const cursorPrimed = cursor !== void 0;
3355
+ const message = expectReport ? `${prompt}\n${operatorReportInstruction(reportId)}` : prompt;
3356
+ const send = await client.sendMessage(localId, {
3357
+ message,
3358
+ idempotencyKey,
3359
+ awaitMs: 0
3360
+ }, signal);
3361
+ const submitted = send.submission?.status === "submitted";
3362
+ if (send.delivered === false || send.delivery?.status === "failed" || send.delivery?.status === "error") return {
3363
+ submitted: false,
3364
+ delivered: false,
3365
+ settled: "timeout",
3366
+ state: "send_failed",
3367
+ raw: "",
3368
+ reportFound: false,
3369
+ interrupted: false,
3370
+ recovered: false,
3371
+ cursorPrimed,
3372
+ sendConfirmation: send.confirmation,
3373
+ error: "delivery_failed"
3374
+ };
3375
+ let settle = await waitForTurnSettled(client, localId, {
3376
+ timeoutMs,
3377
+ pollTimeoutMs,
3378
+ cursor,
3379
+ now,
3380
+ sleep: sleep$2,
3381
+ signal
3382
+ });
3383
+ cursor = settle.cursor;
3384
+ const isCurrentReport = (r) => expectReport && r.found && r.reportId !== void 0 && r.reportId === reportId;
3385
+ let tail = signal?.aborted ? "" : await readTail(client, localId, tailLines, signal);
3386
+ let report = parseOperatorReport(tail);
3387
+ let interrupted = false;
3388
+ let recovered = false;
3389
+ const aborted = settle.reason === "aborted" || signal?.aborted === true;
3390
+ if (!settle.settled && !aborted) {
3391
+ interrupted = true;
3392
+ await client.sendKeys(localId, {
3393
+ keys: INTERRUPT_KEY,
3394
+ idempotencyKey: interruptKey,
3395
+ raw: false
3396
+ }, signal).catch(() => {});
3397
+ const recovery = await waitForTurnSettled(client, localId, {
3398
+ timeoutMs: deps.recoverTimeoutMs ?? DEFAULT_RECOVER_MS,
3399
+ pollTimeoutMs,
3400
+ cursor,
3401
+ now,
3402
+ sleep: sleep$2,
3403
+ signal
3404
+ });
3405
+ recovered = recovery.settled;
3406
+ if (recovery.settled) settle = recovery;
3407
+ const recoveredTail = await readTail(client, localId, tailLines, signal);
3408
+ const recoveredReport = parseOperatorReport(recoveredTail);
3409
+ if (recoveredTail !== "" && (isCurrentReport(recoveredReport) || !isCurrentReport(report))) {
3410
+ tail = recoveredTail;
3411
+ report = recoveredReport;
3412
+ }
3413
+ }
3414
+ const settledReason = settle.settled ? settle.reason : settle.reason === "aborted" || signal?.aborted === true ? "aborted" : "timeout";
3415
+ const settleDerivedState = settledReason === "waiting_input" ? "awaiting_input" : settledReason === "aborted" ? "aborted" : settledReason === "timeout" ? "timeout" : "unknown";
3416
+ const reportCurrent = isCurrentReport(report);
3417
+ return {
3418
+ submitted,
3419
+ delivered: true,
3420
+ settled: settledReason,
3421
+ state: settledReason === "waiting_input" ? "awaiting_input" : reportCurrent && report.state !== "unknown" ? report.state : settleDerivedState,
3422
+ summary: reportCurrent ? report.summary : void 0,
3423
+ ask: reportCurrent ? report.ask : void 0,
3424
+ artifact: reportCurrent ? report.artifact : void 0,
3425
+ raw: tail,
3426
+ reportFound: reportCurrent,
3427
+ interrupted,
3428
+ recovered,
3429
+ cursorPrimed,
3430
+ sendConfirmation: send.confirmation
3431
+ };
3432
+ }
3433
+
2676
3434
  //#endregion
2677
3435
  //#region src/lib/fleet/tools.ts
2678
3436
  const FLEET_GROUP = "fleet";
@@ -2681,6 +3439,7 @@ const INSTANCE_PROBE_CACHE_TTL_MS = 5e3;
2681
3439
  const CAPABILITIES_CACHE_TTL_MS = 6e4;
2682
3440
  const AWAIT_TURN_DEFAULT_TIMEOUT_MS = 3e4;
2683
3441
  const AWAIT_TURN_TIMEOUT_SLACK_MS = 5e3;
3442
+ const DRIVE_TASK_DEFAULT_TIMEOUT_MS = 12e4;
2684
3443
  const LIST_INSTANCES_FANOUT_CONCURRENCY = 16;
2685
3444
  const AWAIT_TURN_FANOUT_CONCURRENCY = 256;
2686
3445
  const INSTANCE_PROBE_RATE_LIMIT_MAX_RETRIES = 1;
@@ -2874,22 +3633,47 @@ function createFleetTools(options = {}) {
2874
3633
  sessionId: globalId
2875
3634
  });
2876
3635
  }),
2877
- tool$1("send_message", "Send a message to a fleet session. isError reflects DELIVERY ONLY: it is true only when the message could not be delivered to the session (transport/precondition failure). A delivered message whose confirmation did not arrive within awaitMs is NOT an error — it returns delivered:true with confirmationPending/confirmationTimedOut, because a long turn legitimately outruns awaitMs. Recommended pattern: send with awaitMs:0 for a fast delivery ack that never blocks on confirmation, then call await_turn (filtered to this sessionId) to observe the session's actual turn completion. The idempotencyKey makes a retried send safe (a retry never re-types the message).", objectSchema$1({
3636
+ tool$1("send_message", "Send a message to a fleet session. By DEFAULT it first checks the session is idle / awaiting the next message and REFUSES (structured notReady, isError) rather than blind-type into a busy composer or a pending prompt — set requireIdle:false to force the legacy unconditional send, or waitForIdleMs to wait briefly for idle first. isError reflects DELIVERY: true when the message was not delivered (transport/precondition failure) OR refused as notReady. A delivered message whose confirmation did not arrive within awaitMs is NOT an error — it returns delivered:true with confirmationPending/confirmationTimedOut, because a long turn legitimately outruns awaitMs. The additive `submitted` field is true only when ai-or-die's submission sub-status proves the message reached the composer. Recommended pattern: send with awaitMs:0 for a fast delivery ack, then call await_turn (filtered to this sessionId) to observe the session's actual turn completion. The idempotencyKey makes a retried send safe (a retry never re-types the message).", objectSchema$1({
2878
3637
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
2879
3638
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
2880
3639
  message: stringProp$1("Message text to deliver to the session."),
2881
3640
  idempotencyKey: stringProp$1("Optional caller idempotency key; AUTO-GENERATED when omitted, so you normally never pass it. Supply your OWN stable key only when you will retry the SAME send and need the upstream to dedupe it."),
2882
- awaitMs: numberProp$1("Optional best-effort confirmation wait (ms) — NOT a deadline. Prefer awaitMs:0 plus await_turn; a turn that outruns awaitMs returns confirmationPending, not an error.")
3641
+ awaitMs: numberProp$1("Optional best-effort confirmation wait (ms) — NOT a deadline. Prefer awaitMs:0 plus await_turn; a turn that outruns awaitMs returns confirmationPending, not an error."),
3642
+ requireIdle: booleanProp("Default true: check status and refuse a busy/awaiting-prompt/dead session with a structured notReady result. Set false to force an unconditional send (unsafe: may type into a busy composer)."),
3643
+ waitForIdleMs: numberProp$1("When requireIdle, wait up to this many ms for the session to become idle before deciding (default 0 = decide immediately).")
2883
3644
  }, ["sessionId", "message"]), async (args, signal) => {
2884
3645
  const { instance, localId, globalId } = await resolveSession(args);
2885
3646
  const awaitMs = optionalNumber$1(args, "awaitMs");
2886
- const response = await clientFor(instance).sendMessage(localId, {
3647
+ const requireIdle = optionalBoolean(args, "requireIdle") ?? true;
3648
+ const client = clientFor(instance);
3649
+ if (requireIdle) {
3650
+ const readyResult = await waitForMessageReady(client, localId, {
3651
+ waitMs: optionalNumber$1(args, "waitForIdleMs") ?? 0,
3652
+ signal
3653
+ });
3654
+ if (!readyResult.ready && isHardNotReady(readyResult.readiness.reason)) {
3655
+ const advise = readyResult.readiness.reason === "awaiting_other" ? " The session is awaiting a prompt — use `respond`, not a free-text message." : readyResult.readiness.reason === "terminal" ? " The session has exited." : " Wait for it to go idle (await_turn) or set requireIdle:false to force.";
3656
+ return jsonResult$1({
3657
+ resolvedInstance: publicInstance(instance),
3658
+ sessionId: globalId,
3659
+ delivered: false,
3660
+ submitted: false,
3661
+ notReady: true,
3662
+ reason: readyResult.readiness.reason,
3663
+ interactionState: readyResult.readiness.interactionState,
3664
+ awaitingKind: readyResult.readiness.awaitingKind,
3665
+ message: `not sent: session is not ready for a message (${readyResult.readiness.reason}).${advise}`
3666
+ }, true);
3667
+ }
3668
+ }
3669
+ const response = await client.sendMessage(localId, {
2887
3670
  message: requiredString$1(args, "message"),
2888
3671
  idempotencyKey: optionalString$1(args, "idempotencyKey") ?? randomUUID(),
2889
3672
  ...awaitMs === void 0 ? {} : { awaitMs }
2890
3673
  }, signal);
2891
3674
  const delivered = !(response.delivered === false || response.delivery?.status === "failed" || response.delivery?.status === "error");
2892
3675
  const confirmed = delivered && response.confirmed === true;
3676
+ const submitted = delivered && response.submission?.status === "submitted";
2893
3677
  const confirmationTimedOut = delivered && !confirmed && (awaitMs !== void 0 && awaitMs > 0 || response.confirmationTimedOut === true);
2894
3678
  const isError = !delivered;
2895
3679
  return jsonResult$1({
@@ -2898,6 +3682,7 @@ function createFleetTools(options = {}) {
2898
3682
  ...response,
2899
3683
  delivered,
2900
3684
  confirmed,
3685
+ submitted,
2901
3686
  ...confirmationTimedOut ? {
2902
3687
  confirmationPending: true,
2903
3688
  confirmationTimedOut: true
@@ -2905,23 +3690,41 @@ function createFleetTools(options = {}) {
2905
3690
  ...isError ? { message: "message was not delivered to the session by the upstream instance" } : confirmationTimedOut ? { message: "delivered; turn completion not confirmed in the await window. Use await_turn filtered to this sessionId to observe completion (the idempotencyKey makes a retried send safe)." } : {}
2906
3691
  }, isError);
2907
3692
  }),
2908
- tool$1("send_keys", "Send key input to a fleet session.", objectSchema$1({
3693
+ tool$1("send_keys", "Send key input to a fleet session. Prefer the higher-level `op`: 'submit' presses Enter and 'interrupt' sends Ctrl-C, each mapped to ai-or-die's NAMED key (never a literal control byte like \"\\r\"). Use `keys` only for literal input; `raw` is strictly for literal bytes. Provide exactly one of `op` or `keys`.", objectSchema$1({
2909
3694
  sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
2910
3695
  instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
2911
- keys: stringProp$1("Key sequence to send."),
3696
+ op: stringProp$1("Higher-level named op: 'submit' (Enter) or 'interrupt' (Ctrl-C). Mapped to the ai-or-die named key with raw off. Do NOT also pass keys."),
3697
+ keys: stringProp$1("Literal key sequence to send. Provide instead of op."),
2912
3698
  idempotencyKey: stringProp$1("Optional caller idempotency key; auto-generated when omitted."),
2913
- raw: booleanProp("Pass keys through as raw input when the instance supports it.")
2914
- }, ["sessionId", "keys"]), async (args, signal) => {
3699
+ raw: booleanProp("Pass keys through as raw literal bytes when the instance supports it. Ignored when op is set.")
3700
+ }, ["sessionId"]), async (args, signal) => {
2915
3701
  const { instance, localId, globalId } = await resolveSession(args);
2916
- const raw = optionalBoolean(args, "raw");
3702
+ const op = optionalString$1(args, "op");
3703
+ const literalKeys = optionalString$1(args, "keys");
3704
+ if (op !== void 0 && literalKeys !== void 0) throw new FleetToolInputError("INVALID_ARGUMENT", "provide either arguments.op or arguments.keys, not both");
3705
+ if (op === void 0 && literalKeys === void 0) throw new FleetToolInputError("INVALID_ARGUMENT", "one of arguments.op or arguments.keys is required");
3706
+ let keys;
3707
+ let raw;
3708
+ if (op !== void 0) {
3709
+ if (!isNamedKeyOp(op)) throw new FleetToolInputError("INVALID_ARGUMENT", `arguments.op must be 'submit' or 'interrupt' (got ${JSON.stringify(op)})`);
3710
+ keys = mapNamedKeyOp(op);
3711
+ raw = false;
3712
+ } else {
3713
+ keys = literalKeys;
3714
+ raw = optionalBoolean(args, "raw");
3715
+ }
2917
3716
  const response = await clientFor(instance).sendKeys(localId, {
2918
- keys: requiredString$1(args, "keys"),
3717
+ keys,
2919
3718
  idempotencyKey: optionalString$1(args, "idempotencyKey") ?? randomUUID(),
2920
3719
  ...raw === void 0 ? {} : { raw }
2921
3720
  }, signal);
2922
3721
  return ok$1({
2923
3722
  resolvedInstance: publicInstance(instance),
2924
3723
  sessionId: globalId,
3724
+ ...op === void 0 ? {} : {
3725
+ op,
3726
+ mappedKeys: keys
3727
+ },
2925
3728
  ...response
2926
3729
  });
2927
3730
  }),
@@ -2956,15 +3759,18 @@ function createFleetTools(options = {}) {
2956
3759
  start: booleanProp("Whether the remote instance should start the session immediately."),
2957
3760
  readyTimeoutMs: numberProp$1("F17: bounded ms to wait for the agent to become driveable before returning. The response carries ready/bound/blocker."),
2958
3761
  permissionMode: stringProp$1("F10 (claude only): permission mode the launched agent starts in — one of plan | acceptEdits | default | bypassPermissions. Rejected with BAD_REQUEST if unknown or if agentArgs also sets it."),
2959
- agentArgs: arrayProp("F10 (claude only): extra launcher args appended after the github-router prefix. Must NOT include --permission-mode or --dangerously-skip-permissions (use permissionMode) — rejected with BAD_REQUEST.")
3762
+ agentArgs: arrayProp("F10 (claude only): extra launcher args appended after the github-router prefix. Must NOT include --permission-mode or --dangerously-skip-permissions (use permissionMode) — rejected with BAD_REQUEST."),
3763
+ disableStopGate: booleanProp("C3 (claude only): disable the structural Stop-gate on the launched session by injecting --no-stop-gate into agentArgs, so a driven session's turn-end never hangs on a blocking Stop hook. Requires a remote github-router that understands the flag (uses the agent_args capability).")
2960
3764
  }, ["instance", "agent"]), async (args, signal) => {
2961
3765
  const instance = await resolve(requiredString$1(args, "instance"));
2962
3766
  const agent = requiredString$1(args, "agent");
2963
3767
  const idempotencyKey = optionalString$1(args, "idempotencyKey") ?? randomUUID();
2964
3768
  const permissionMode = optionalString$1(args, "permissionMode");
2965
- const agentArgs = optionalStringArray(args, "agentArgs");
3769
+ const disableStopGate = optionalBoolean(args, "disableStopGate") === true;
3770
+ const requestedAgentArgs = optionalStringArray(args, "agentArgs");
3771
+ const agentArgs = disableStopGate ? [...requestedAgentArgs ?? [], "--no-stop-gate"] : requestedAgentArgs;
2966
3772
  if (permissionMode !== void 0) await assertCapability(instance, "permission_mode", "permissionMode", signal);
2967
- if (agentArgs !== void 0) await assertCapability(instance, "agent_args", "agentArgs", signal);
3773
+ if (agentArgs !== void 0) await assertCapability(instance, "agent_args", disableStopGate ? "disableStopGate" : "agentArgs", signal);
2968
3774
  const response = await clientFor(instance).createSession(definedObject({
2969
3775
  agent,
2970
3776
  name: optionalString$1(args, "name"),
@@ -3052,10 +3858,12 @@ function createFleetTools(options = {}) {
3052
3858
  instance: publicInstance(instance),
3053
3859
  ...gap
3054
3860
  })));
3861
+ const settled = classifyTurnEvents(events$1);
3055
3862
  return ok$1({
3056
3863
  resolvedInstances: target.instances.map(publicInstance),
3057
3864
  events: events$1,
3058
3865
  gaps,
3866
+ ...settled.length > 0 ? { settled } : {},
3059
3867
  cursors: responses.map(({ instance, response }) => ({
3060
3868
  instance: publicInstance(instance),
3061
3869
  cursor: response.cursor
@@ -3064,6 +3872,31 @@ function createFleetTools(options = {}) {
3064
3872
  ...errors.length > 0 ? { errors } : {}
3065
3873
  });
3066
3874
  }),
3875
+ tool$1("drive_task", "Drive one prompt on a session to completion and return the parsed operator report. Composes the reliable path: ensure the composer is idle (else return a structured busy/not-ready result), send and surface whether the message reached the composer (submitted; a delivered-but-unconfirmed send still proceeds), wait for the RELIABLE turn boundary (turn_ended / waiting_input — never the became_idle flicker), read the transcript tail, and parse the OPERATOR REPORT trailer into {state, summary, ask, artifact, raw}. A per-call REPORT_ID nonce is embedded in the trailer instruction and the parsed report is trusted ONLY when it echoes that nonce, so a stale prior-turn trailer left in the tail can never be returned as this turn's result. A reliable waiting_input outranks the model's self-reported STATE (a still-blocked session is never reported as done). If the turn does not end within timeoutMs (e.g. a blocking Stop hook that would otherwise hang ~10 min), it AUTO-RECOVERS with a Ctrl-C interrupt rather than blocking, then re-waits briefly and re-reads; a caller ABORT is distinct (settled:'aborted', state:'aborted') and never injects a Ctrl-C. Read `state` TOGETHER with `settled`/`interrupted`/`recovered`: state:'done' with settled:'timeout' + interrupted:true means the model reported done but the turn had to be interrupted to recover, so treat it as needs-verification rather than a clean completion; `submitted` is a best-effort positive signal that CAN be false even on a successful turn. Robust to a busy session (state:'busy'), a missing/stale/placeholder trailer (state falls back to the settle-derived value, reportFound:false), and a hung hook (interrupted:true, recovered:true/false). By default it appends the trailer instruction so the driven session emits a parseable report; set expectReport:false to send the prompt verbatim (a trailer left in the tail is then never trusted).", objectSchema$1({
3876
+ sessionId: stringProp$1("Global session id in the form instanceId:localSessionId."),
3877
+ instance: stringProp$1("Optional instance id/label; when supplied it must agree with sessionId."),
3878
+ prompt: stringProp$1("The task/prompt to drive on the session."),
3879
+ timeoutMs: numberProp$1(`Ms to wait for the turn to end before auto-recovering via interrupt (default ${DRIVE_TASK_DEFAULT_TIMEOUT_MS}). Set generously — exceeding it triggers a Ctrl-C recovery.`),
3880
+ expectReport: booleanProp("Default true: append the OPERATOR REPORT trailer instruction so the driven session ends its turn with a parseable {state,summary,ask,artifact}. Set false to send the prompt verbatim.")
3881
+ }, ["sessionId", "prompt"]), async (args, signal) => {
3882
+ const { instance, localId, globalId } = await resolveSession(args);
3883
+ const result = await driveTask({
3884
+ client: clientFor(instance),
3885
+ localId,
3886
+ prompt: requiredString$1(args, "prompt"),
3887
+ timeoutMs: optionalNumber$1(args, "timeoutMs") ?? DRIVE_TASK_DEFAULT_TIMEOUT_MS,
3888
+ expectReport: optionalBoolean(args, "expectReport") ?? true,
3889
+ idempotencyKey: randomUUID(),
3890
+ interruptKey: randomUUID(),
3891
+ reportId: randomUUID(),
3892
+ signal
3893
+ });
3894
+ return jsonResult$1({
3895
+ resolvedInstance: publicInstance(instance),
3896
+ sessionId: globalId,
3897
+ ...result
3898
+ }, result.error !== void 0);
3899
+ }),
3067
3900
  tool$1("read_file", "Read a file from one fleet instance via its existing /api/files/content endpoint.", objectSchema$1({
3068
3901
  instance: stringProp$1("Instance id or label. Defaults to the registry default, or the sole instance."),
3069
3902
  path: stringProp$1("Remote file path to read.")
@@ -24305,6 +25138,16 @@ function stopGateId(env = process.env) {
24305
25138
  const v = (env.GH_ROUTER_STOP_GATE_ID ?? "").trim();
24306
25139
  return v.length > 0 ? v : "default-ci";
24307
25140
  }
25141
+ /**
25142
+ * C3: whether the structural Stop-gate is disabled for THIS launch. True when the
25143
+ * `GH_ROUTER_DISABLE_STOP_GATE` env is set OR the `--no-stop-gate` launcher flag was
25144
+ * passed (`args["no-stop-gate"] === true`). A DRIVEN session sets the flag so a
25145
+ * blocking Stop hook never hangs its turn-end (~10 min) waiting on the fleet driver.
25146
+ * Pure so it is unit-testable without the live launch path.
25147
+ */
25148
+ function stopGateDisabled(args, env = process.env) {
25149
+ return parseBoolEnv(env.GH_ROUTER_DISABLE_STOP_GATE) === true || args["no-stop-gate"] === true;
25150
+ }
24308
25151
  /** True when a settings `Stop` entry already registers `command` (so the merge
24309
25152
  * is idempotent across re-launches). */
24310
25153
  function entryHasCommand(entry, command) {
@@ -26346,5 +27189,5 @@ async function runStandInToolCall(args, signal) {
26346
27189
  }
26347
27190
 
26348
27191
  //#endregion
26349
- export { readIteratorWithTimeout as $, copilotHeaders as $t, DEFAULT_MODEL as A, UPSTREAM_INACTIVITY_TIMEOUT_MS as At, toolbeltSkipSet as B, cacheModels as Bt, repoRoot as C, collapsePathKeys as Ct, resolveSealedGate as D, DEFAULT_CODEX_MODEL_FALLBACKS as Dt, trustRepo as E, DEFAULT_CODEX_MODEL as Et, runWorkerAgent as F, setupCopilotToken as Ft, ADVISOR_INTERNAL_TOOL_NAME as G, resolveModel as Gt, TOOLBELT_TOOLS$1 as H, filterBetaHeader as Ht, withNoOutputRetry as I, setupGitHubAgentToken as It, injectAdvisorTool as J, fetchWithTransientRetry as Jt, ADVISOR_TOOL_INSTRUCTIONS as K, sleep as Kt, availableToolCommands as L, setupGitHubToken as Lt, PLAN_DEFAULT_MODEL as M, pickClaudeDefault as Mt, REVIEW_DEFAULT_MODEL as N, getPackageVersion as Nt, liveExec as O, DEFAULT_PORT as Ot, appendPlanReminder as P, withInstallLock as Pt, logStreamError as Q, copilotBaseUrl as Qt, buildToolbeltAwareness as R, tryRefreshAndRetry as Rt, repoFingerprint as S, ArtifactClient as St, stopReviewStateDir as T, DEFAULT_CLAUDE_MODEL_FALLBACKS as Tt, assetFor as U, isNullish as Ut, vscodeRipgrepPath as V, cacheVSCodeVersion as Vt, searchWeb as W, resolveCodexModel as Wt, buildOpenAIErrorEvent as X, forwardError as Xt, isAdvisorRequested as Y, HTTPError as Yt, isControllerClosedError as Z, GITHUB_API_BASE_URL as Zt, fileBaselineStore as _, hasSupportedBrowserInstalled as _t, buildPeerAwarenessSnippet as a, fleetToolsEnabled as at, fileReviewDebounce as b, extractZipMember as bt, buildSessionBindHookCommand as c, countTokens as ct, decideStopHook as d, createResponses as dt, githubHeaders as en, relayAnthropicStream as et, fileBlockBudget as f, createChatCompletions as ft, stopReviewEnabled as g, provisionBrowserAssets as gt, stopGateId as h, parseJsonOrDiagnose as ht, buildAgentPrompt as i, browserToolsEnabled as it, IMPLEMENT_DEFAULT_MODEL as j, generateRandomPort as jt, BROWSE_DEFAULT_MODEL as k, UPSTREAM_FETCH_TIMEOUT_MS as kt, buildStopHookCommand as l, createMessages as lt, launchBaselineKey as m, readResponseBodyCapped as mt, MCP_GROUPS as n, handleMcpPost as nt, personasFor as o, standInToolEnabled as ot, injectStopHookIntoSettingsFile as p, MAX_RESPONSE_BODY_BYTES as pt, buildAdvisorStream as q, getModels as qt, assertMcpToolSurfaceConsistent as r, agentToolsEnabled as rt, buildArtifactOpenHookCommand as s, workerToolsEnabled as st, GROUP_META as t, state as tn, handleMcpDelete as tt, captureLaunchBaseline as u, getTokenCount as ut, fileFindingsStore as v, provisionAndIndexColbert as vt, stopGateEnabledForRepo as w, toolbeltPathOverride as wt, isSubagentContext as x, shouldUseInsecureTls as xt, fileLastPromptStore as y, extractTarGzMember as yt, toolbeltEnabled as z, cacheCopilotVersion as zt };
26350
- //# sourceMappingURL=peer-mcp-personas-D_HyWhUb.js.map
27192
+ export { logStreamError as $, copilotBaseUrl as $t, BROWSE_DEFAULT_MODEL as A, UPSTREAM_FETCH_TIMEOUT_MS as At, toolbeltEnabled as B, cacheCopilotVersion as Bt, repoFingerprint as C, ArtifactClient as Ct, trustRepo as D, DEFAULT_CODEX_MODEL as Dt, stopReviewStateDir as E, DEFAULT_CLAUDE_MODEL_FALLBACKS as Et, appendPlanReminder as F, withInstallLock as Ft, searchWeb as G, resolveCodexModel as Gt, vscodeRipgrepPath as H, cacheVSCodeVersion as Ht, runWorkerAgent as I, setupCopilotToken as It, buildAdvisorStream as J, getModels as Jt, ADVISOR_INTERNAL_TOOL_NAME as K, resolveModel as Kt, withNoOutputRetry as L, setupGitHubAgentToken as Lt, IMPLEMENT_DEFAULT_MODEL as M, generateRandomPort as Mt, PLAN_DEFAULT_MODEL as N, pickClaudeDefault as Nt, resolveSealedGate as O, DEFAULT_CODEX_MODEL_FALLBACKS as Ot, REVIEW_DEFAULT_MODEL as P, getPackageVersion as Pt, isControllerClosedError as Q, GITHUB_API_BASE_URL as Qt, availableToolCommands as R, setupGitHubToken as Rt, isSubagentContext as S, shouldUseInsecureTls as St, stopGateEnabledForRepo as T, toolbeltPathOverride as Tt, TOOLBELT_TOOLS$1 as U, filterBetaHeader as Ut, toolbeltSkipSet as V, cacheModels as Vt, assetFor as W, isNullish as Wt, isAdvisorRequested as X, HTTPError as Xt, injectAdvisorTool as Y, fetchWithTransientRetry as Yt, buildOpenAIErrorEvent as Z, forwardError as Zt, stopReviewEnabled as _, provisionBrowserAssets as _t, buildPeerAwarenessSnippet as a, browserToolsEnabled as at, fileLastPromptStore as b, extractTarGzMember as bt, buildSessionBindHookCommand as c, workerToolsEnabled as ct, decideStopHook as d, getTokenCount as dt, copilotHeaders as en, readIteratorWithTimeout as et, fileBlockBudget as f, createResponses as ft, stopGateId as g, parseJsonOrDiagnose as gt, stopGateDisabled as h, readResponseBodyCapped as ht, buildAgentPrompt as i, agentToolsEnabled as it, DEFAULT_MODEL as j, UPSTREAM_INACTIVITY_TIMEOUT_MS as jt, liveExec as k, DEFAULT_PORT as kt, buildStopHookCommand as l, countTokens as lt, launchBaselineKey as m, MAX_RESPONSE_BODY_BYTES as mt, MCP_GROUPS as n, state as nn, handleMcpDelete as nt, personasFor as o, fleetToolsEnabled as ot, injectStopHookIntoSettingsFile as p, createChatCompletions as pt, ADVISOR_TOOL_INSTRUCTIONS as q, sleep as qt, assertMcpToolSurfaceConsistent as r, handleMcpPost as rt, buildArtifactOpenHookCommand as s, standInToolEnabled as st, GROUP_META as t, githubHeaders as tn, relayAnthropicStream as tt, captureLaunchBaseline as u, createMessages as ut, fileBaselineStore as v, hasSupportedBrowserInstalled as vt, repoRoot as w, collapsePathKeys as wt, fileReviewDebounce as x, extractZipMember as xt, fileFindingsStore as y, provisionAndIndexColbert as yt, buildToolbeltAwareness as z, tryRefreshAndRetry as zt };
27193
+ //# sourceMappingURL=peer-mcp-personas-BQVOxB1i.js.map