@mindstudio-ai/remy 0.1.245 → 0.1.247

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.
@@ -19,7 +19,10 @@
19
19
  * page size (default 500, hard cap 2000). Response: {event:"history",
20
20
  * messages, startIndex, endIndex, totalMessageCount, ...}. Walk backward by
21
21
  * passing the previous response's `startIndex` as the next `before`. When
22
- * `startIndex === 0`, no older messages remain.
22
+ * `startIndex === 0`, no older messages remain. Indices are GLOBAL — they span
23
+ * the sealed session archives followed by the live tail (see getHistoryPage in
24
+ * session.ts), so scrollback continues past a rotation to the conversation
25
+ * start, not just to the start of the live (post-rotation) array.
23
26
  */
24
27
  interface HeadlessOptions {
25
28
  apiKey?: string;
package/dist/headless.js CHANGED
@@ -476,6 +476,9 @@ var ALLOWED_MODELS_BY_TYPE = {
476
476
  "claude-fable-5",
477
477
  "claude-5-sonnet",
478
478
  "gpt-5.5",
479
+ "gpt-5.6-sol",
480
+ "gpt-5.6-terra",
481
+ "gpt-5.6-luna",
479
482
  "gemini-3-pro",
480
483
  "gemini-3.1-pro",
481
484
  "gemini-3-flash",
@@ -1624,6 +1627,15 @@ var confirmDestructiveActionTool = {
1624
1627
 
1625
1628
  // src/subagents/common/runCli.ts
1626
1629
  import { spawn } from "child_process";
1630
+ var SCRAPE_MAX_BUFFER = 4 * 1024 * 1024;
1631
+ var SEARCH_MAX_BUFFER = 512 * 1024;
1632
+ var SIGKILL_GRACE_MS = 2e3;
1633
+ function formatCliResult(r) {
1634
+ const logBlock = r.logs.length > 0 ? r.logs.join("\n") + "\n\n" : "";
1635
+ const body = r.ok ? r.output : `Error: ${r.output}`;
1636
+ const truncNote = r.truncated ? "\n\n[output truncated]" : "";
1637
+ return logBlock + body + truncNote;
1638
+ }
1627
1639
  function runCli(command, args, options) {
1628
1640
  return new Promise((resolve2) => {
1629
1641
  const timeout = options?.timeout ?? 6e4;
@@ -1632,35 +1644,71 @@ function runCli(command, args, options) {
1632
1644
  if (options?.jsonLogs && !args.includes("--json-logs")) {
1633
1645
  finalArgs = args.length > 0 ? [args[0], "--json-logs", ...args.slice(1)] : ["--json-logs"];
1634
1646
  }
1635
- const child = spawn(command, finalArgs, {
1636
- stdio: [options?.stdin ? "pipe" : "ignore", "pipe", "pipe"]
1637
- });
1638
- if (options?.stdin) {
1639
- child.stdin.write(options.stdin);
1640
- child.stdin.end();
1641
- }
1642
1647
  const logs = [];
1643
1648
  let stdout = "";
1644
1649
  let stderr = "";
1645
1650
  let stdoutSize = 0;
1646
1651
  let stderrSize = 0;
1647
1652
  let killed = false;
1653
+ let timedOut = false;
1654
+ let truncated = false;
1655
+ let settled = false;
1656
+ let timer;
1657
+ let killTimer;
1658
+ const finish = (result) => {
1659
+ if (settled) {
1660
+ return;
1661
+ }
1662
+ settled = true;
1663
+ if (timer) {
1664
+ clearTimeout(timer);
1665
+ }
1666
+ if (killTimer) {
1667
+ clearTimeout(killTimer);
1668
+ }
1669
+ resolve2(result);
1670
+ };
1671
+ const child = spawn(command, finalArgs, {
1672
+ stdio: [options?.stdin ? "pipe" : "ignore", "pipe", "pipe"]
1673
+ });
1674
+ child.on("error", (err) => {
1675
+ finish({
1676
+ ok: false,
1677
+ output: err.message,
1678
+ logs,
1679
+ timedOut: false,
1680
+ truncated: false
1681
+ });
1682
+ });
1683
+ if (options?.stdin) {
1684
+ child.stdin.on("error", () => {
1685
+ });
1686
+ try {
1687
+ child.stdin.write(options.stdin);
1688
+ child.stdin.end();
1689
+ } catch {
1690
+ }
1691
+ }
1692
+ const killForOverflow = () => {
1693
+ if (killed) {
1694
+ return;
1695
+ }
1696
+ killed = true;
1697
+ truncated = true;
1698
+ child.kill();
1699
+ };
1648
1700
  child.stdout.on("data", (chunk) => {
1649
1701
  stdoutSize += chunk.length;
1650
1702
  if (stdoutSize <= maxBuffer) {
1651
1703
  stdout += chunk.toString();
1652
- } else if (!killed) {
1653
- killed = true;
1654
- child.kill();
1704
+ } else {
1705
+ killForOverflow();
1655
1706
  }
1656
1707
  });
1657
1708
  child.stderr.on("data", (chunk) => {
1658
1709
  stderrSize += chunk.length;
1659
1710
  if (stderrSize > maxBuffer) {
1660
- if (!killed) {
1661
- killed = true;
1662
- child.kill();
1663
- }
1711
+ killForOverflow();
1664
1712
  return;
1665
1713
  }
1666
1714
  const text = chunk.toString();
@@ -1685,24 +1733,38 @@ function runCli(command, args, options) {
1685
1733
  }
1686
1734
  }
1687
1735
  });
1688
- const timer = setTimeout(() => {
1736
+ timer = setTimeout(() => {
1737
+ if (killed) {
1738
+ return;
1739
+ }
1689
1740
  killed = true;
1741
+ timedOut = true;
1690
1742
  child.kill();
1743
+ killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_GRACE_MS);
1691
1744
  }, timeout);
1692
1745
  child.on("close", (code) => {
1693
- clearTimeout(timer);
1694
- const logBlock = !options?.onLog && logs.length > 0 ? logs.join("\n") + "\n\n" : "";
1695
1746
  const out = stdout.trim();
1696
- if (out) {
1697
- resolve2(logBlock + out);
1747
+ if (timedOut) {
1748
+ finish({
1749
+ ok: false,
1750
+ output: stderr.trim() || "Process timed out",
1751
+ logs,
1752
+ timedOut: true,
1753
+ truncated
1754
+ });
1698
1755
  return;
1699
1756
  }
1700
- if (code !== 0 || killed) {
1701
- const errMsg = stderr.trim() || (killed ? "Process timed out" : `Exit code ${code}`);
1702
- resolve2(logBlock + `Error: ${errMsg}`);
1757
+ if (out) {
1758
+ finish({ ok: true, output: out, logs, timedOut: false, truncated });
1703
1759
  return;
1704
1760
  }
1705
- resolve2(logBlock + "(no response)");
1761
+ finish({
1762
+ ok: false,
1763
+ output: stderr.trim() || (code !== 0 ? `Exit code ${code}` : "(no response)"),
1764
+ logs,
1765
+ timedOut: false,
1766
+ truncated
1767
+ });
1706
1768
  });
1707
1769
  });
1708
1770
  }
@@ -1726,11 +1788,12 @@ var askMindStudioSdkTool = {
1726
1788
  },
1727
1789
  async execute(input, context) {
1728
1790
  const query = input.query;
1729
- return runCli("mindstudio", ["ask", query], {
1791
+ const result = await runCli("mindstudio", ["ask", query], {
1730
1792
  timeout: 2e5,
1731
1793
  maxBuffer: 512 * 1024,
1732
1794
  onLog: context?.onLog
1733
1795
  });
1796
+ return formatCliResult(result);
1734
1797
  }
1735
1798
  };
1736
1799
 
@@ -1768,17 +1831,21 @@ function stripFlags(args) {
1768
1831
  }
1769
1832
  return out;
1770
1833
  }
1771
- async function runMindstudioCli(args, options) {
1834
+ async function runMindstudioCliResult(args, options) {
1772
1835
  const cleanArgs = stripFlags(args);
1773
1836
  const cliAction = args[0];
1774
1837
  const agentName = options?.caller ?? "mindstudio-cli";
1775
1838
  const start = Date.now();
1776
- const raw = await runCli("mindstudio", cleanArgs, options);
1839
+ const res = await runCli("mindstudio", cleanArgs, options);
1840
+ if (!res.ok) {
1841
+ return { ok: false, value: formatCliResult(res) };
1842
+ }
1843
+ const truncNote = res.truncated ? "\n\n[output truncated]" : "";
1777
1844
  let envelope;
1778
1845
  try {
1779
- envelope = JSON.parse(raw);
1846
+ envelope = JSON.parse(res.output);
1780
1847
  } catch {
1781
- return raw;
1848
+ return { ok: true, value: res.output + truncNote };
1782
1849
  }
1783
1850
  if (envelope && typeof envelope === "object" && Array.isArray(envelope.results)) {
1784
1851
  const durationMs = Date.now() - start;
@@ -1796,7 +1863,7 @@ async function runMindstudioCli(args, options) {
1796
1863
  });
1797
1864
  }
1798
1865
  }
1799
- return JSON.stringify(envelope.results);
1866
+ return { ok: true, value: JSON.stringify(envelope.results) + truncNote };
1800
1867
  }
1801
1868
  if (typeof envelope?.$billingCost === "number") {
1802
1869
  recordUsage({
@@ -1817,11 +1884,18 @@ async function runMindstudioCli(args, options) {
1817
1884
  if (options?.outputKey) {
1818
1885
  const v = envelope?.[options.outputKey];
1819
1886
  if (v === void 0 || v === null) {
1820
- return JSON.stringify(stripDollarKeys(envelope));
1887
+ return { ok: false, value: JSON.stringify(stripDollarKeys(envelope)) };
1821
1888
  }
1822
- return typeof v === "string" ? v : JSON.stringify(v);
1889
+ const value = typeof v === "string" ? v : JSON.stringify(v);
1890
+ return { ok: true, value: value + truncNote };
1823
1891
  }
1824
- return JSON.stringify(stripDollarKeys(envelope));
1892
+ return {
1893
+ ok: true,
1894
+ value: JSON.stringify(stripDollarKeys(envelope)) + truncNote
1895
+ };
1896
+ }
1897
+ async function runMindstudioCli(args, options) {
1898
+ return (await runMindstudioCliResult(args, options)).value;
1825
1899
  }
1826
1900
  function stripDollarKeys(envelope) {
1827
1901
  if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
@@ -1857,7 +1931,12 @@ var searchGoogleTool = {
1857
1931
  const query = input.query;
1858
1932
  return runMindstudioCli(
1859
1933
  ["search-google", "--query", query, "--export-type", "json"],
1860
- { outputKey: "results", maxBuffer: 512 * 1024, onLog: context?.onLog }
1934
+ {
1935
+ outputKey: "results",
1936
+ maxBuffer: SEARCH_MAX_BUFFER,
1937
+ onLog: context?.onLog,
1938
+ caller: "parent"
1939
+ }
1861
1940
  );
1862
1941
  }
1863
1942
  };
@@ -4146,7 +4225,12 @@ var definition = {
4146
4225
  async function execute(input, onLog) {
4147
4226
  return runMindstudioCli(
4148
4227
  ["search-google", "--query", input.query, "--export-type", "json"],
4149
- { outputKey: "results", onLog, caller: "designExpert" }
4228
+ {
4229
+ outputKey: "results",
4230
+ onLog,
4231
+ caller: "designExpert",
4232
+ maxBuffer: SEARCH_MAX_BUFFER
4233
+ }
4150
4234
  );
4151
4235
  }
4152
4236
 
@@ -4173,9 +4257,6 @@ var definition2 = {
4173
4257
  };
4174
4258
  async function execute2(input, onLog) {
4175
4259
  const pageOptions = { onlyMainContent: true };
4176
- if (input.screenshot) {
4177
- pageOptions.screenshot = true;
4178
- }
4179
4260
  return runMindstudioCli(
4180
4261
  [
4181
4262
  "scrape-url",
@@ -4184,7 +4265,7 @@ async function execute2(input, onLog) {
4184
4265
  "--page-options",
4185
4266
  JSON.stringify(pageOptions)
4186
4267
  ],
4187
- { onLog, caller: "designExpert" }
4268
+ { onLog, caller: "designExpert", maxBuffer: SCRAPE_MAX_BUFFER }
4188
4269
  );
4189
4270
  }
4190
4271
 
@@ -4242,7 +4323,7 @@ async function execute3(input, onLog, context) {
4242
4323
  const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
4243
4324
  let imageUrl = url;
4244
4325
  if (!isImageUrl) {
4245
- const ssUrl = await runMindstudioCli(
4326
+ const ss = await runMindstudioCliResult(
4246
4327
  [
4247
4328
  "screenshot-url",
4248
4329
  "--url",
@@ -4261,10 +4342,10 @@ async function execute3(input, onLog, context) {
4261
4342
  caller: "designExpert"
4262
4343
  }
4263
4344
  );
4264
- if (ssUrl.startsWith("Error")) {
4265
- return `Could not screenshot ${url}: ${ssUrl}`;
4345
+ if (!ss.ok) {
4346
+ return `Could not screenshot ${url}: ${ss.value}`;
4266
4347
  }
4267
- imageUrl = ssUrl;
4348
+ imageUrl = ss.value;
4268
4349
  }
4269
4350
  const analysis = await analyzeImage({
4270
4351
  prompt: analysisPrompt,
@@ -4430,7 +4511,7 @@ ${context}
4430
4511
  <brief>
4431
4512
  ${brief}
4432
4513
  </brief>`;
4433
- const enhanced = await runMindstudioCli(
4514
+ const enhanced = await runMindstudioCliResult(
4434
4515
  [
4435
4516
  "generate-text",
4436
4517
  "--message",
@@ -4440,7 +4521,11 @@ ${brief}
4440
4521
  ],
4441
4522
  { outputKey: "content", timeout: 6e4, onLog, caller: "designExpert" }
4442
4523
  );
4443
- return enhanced.trim();
4524
+ if (!enhanced.ok) {
4525
+ onLog?.(`[enhancePrompt] enhancement failed, using original brief`);
4526
+ return brief;
4527
+ }
4528
+ return enhanced.value.trim();
4444
4529
  }
4445
4530
 
4446
4531
  // src/subagents/designExpert/tools/images/imageGenerator.ts
@@ -4492,7 +4577,7 @@ async function generateImageAssets(opts) {
4492
4577
  config
4493
4578
  }
4494
4579
  });
4495
- const url = await runMindstudioCli(["generate-image"], {
4580
+ const res = await runMindstudioCliResult(["generate-image"], {
4496
4581
  outputKey: "imageUrl",
4497
4582
  jsonLogs: true,
4498
4583
  timeout: 2e5,
@@ -4500,7 +4585,7 @@ async function generateImageAssets(opts) {
4500
4585
  stdin: step,
4501
4586
  caller: "designExpert"
4502
4587
  });
4503
- imageUrls = [url];
4588
+ imageUrls = [res.ok ? res.value : `Error: ${res.value}`];
4504
4589
  } else {
4505
4590
  const steps = enhancedPrompts.map((prompt) => ({
4506
4591
  stepType: "generateImage",
@@ -4512,20 +4597,23 @@ async function generateImageAssets(opts) {
4512
4597
  }
4513
4598
  }
4514
4599
  }));
4515
- const batchResult = await runMindstudioCli(["batch"], {
4600
+ const batchRes = await runMindstudioCliResult(["batch"], {
4516
4601
  jsonLogs: true,
4517
4602
  timeout: 2e5,
4518
4603
  onLog,
4519
4604
  stdin: JSON.stringify(steps),
4520
4605
  caller: "designExpert"
4521
4606
  });
4607
+ if (!batchRes.ok) {
4608
+ return batchRes.value;
4609
+ }
4522
4610
  try {
4523
- const parsed = JSON.parse(batchResult);
4611
+ const parsed = JSON.parse(batchRes.value);
4524
4612
  imageUrls = parsed.map(
4525
4613
  (r) => r.output?.imageUrl ?? `Error: ${r.error}`
4526
4614
  );
4527
4615
  } catch {
4528
- return batchResult;
4616
+ return batchRes.value;
4529
4617
  }
4530
4618
  }
4531
4619
  if (transparentBackground) {
@@ -4534,7 +4622,7 @@ async function generateImageAssets(opts) {
4534
4622
  if (url.startsWith("Error")) {
4535
4623
  return url;
4536
4624
  }
4537
- const result = await runMindstudioCli(
4625
+ const result = await runMindstudioCliResult(
4538
4626
  ["remove-background-from-image", "--image-url", url],
4539
4627
  {
4540
4628
  outputKey: "imageUrl",
@@ -4543,7 +4631,7 @@ async function generateImageAssets(opts) {
4543
4631
  caller: "designExpert"
4544
4632
  }
4545
4633
  );
4546
- return result.startsWith("Error") ? url : result;
4634
+ return result.ok ? result.value : url;
4547
4635
  })
4548
4636
  );
4549
4637
  }
@@ -5773,7 +5861,11 @@ var scrapeWebUrlTool = {
5773
5861
  "--page-options",
5774
5862
  JSON.stringify(pageOptions)
5775
5863
  ],
5776
- { onLog: context?.onLog }
5864
+ {
5865
+ onLog: context?.onLog,
5866
+ maxBuffer: SCRAPE_MAX_BUFFER,
5867
+ caller: "parent"
5868
+ }
5777
5869
  );
5778
5870
  }
5779
5871
  };
@@ -6309,6 +6401,14 @@ var ARCHIVE_DIR = ".logs/sessions";
6309
6401
  var ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
6310
6402
  var RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
6311
6403
  var ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
6404
+ var ARCHIVE_NAME_RE = /^(cleared|rotated)-.*\.json$/;
6405
+ var archiveSortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
6406
+ var ARCHIVE_COUNT_RE = /\.c(\d+)\.json$/;
6407
+ var HISTORY_DEFAULT_LIMIT = 500;
6408
+ var HISTORY_MAX_LIMIT = 2e3;
6409
+ var archiveCountCache = /* @__PURE__ */ new Map();
6410
+ var archiveMsgCache = /* @__PURE__ */ new Map();
6411
+ var ARCHIVE_MSG_CACHE_MAX = 3;
6312
6412
  function loadSession(state) {
6313
6413
  pruneArchives();
6314
6414
  try {
@@ -6387,31 +6487,34 @@ function buildPayload(state) {
6387
6487
  function archiveMessages(messages, label, models) {
6388
6488
  fs21.mkdirSync(ARCHIVE_DIR, { recursive: true });
6389
6489
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6390
- let dest = path11.join(ARCHIVE_DIR, `${label}-${ts}.json`);
6490
+ const count = messages.length;
6491
+ let dest = path11.join(ARCHIVE_DIR, `${label}-${ts}.c${count}.json`);
6391
6492
  let n = 1;
6392
6493
  while (fs21.existsSync(dest)) {
6393
- dest = path11.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.json`);
6494
+ dest = path11.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.c${count}.json`);
6394
6495
  }
6395
6496
  const payload = { messages };
6396
6497
  if (models && Object.keys(models).length > 0) {
6397
6498
  payload.models = models;
6398
6499
  }
6399
6500
  fs21.writeFileSync(dest, JSON.stringify(payload), "utf-8");
6400
- log9.info("Session archived", { label, dest, messageCount: messages.length });
6501
+ archiveCountCache.set(path11.basename(dest), count);
6502
+ log9.info("Session archived", { label, dest, messageCount: count });
6401
6503
  pruneArchives();
6402
6504
  return dest;
6403
6505
  }
6404
6506
  function pruneArchives() {
6405
6507
  try {
6406
- const entries = fs21.readdirSync(ARCHIVE_DIR).filter((name) => /^(cleared|rotated)-.*\.json$/.test(name));
6508
+ const entries = fs21.readdirSync(ARCHIVE_DIR).filter((name) => ARCHIVE_NAME_RE.test(name));
6407
6509
  if (entries.length <= 1) {
6408
6510
  return;
6409
6511
  }
6410
- const sortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
6411
6512
  const archives = entries.map((name) => ({
6412
6513
  name,
6413
6514
  size: fs21.statSync(path11.join(ARCHIVE_DIR, name)).size
6414
- })).sort((a, b) => sortKey(b.name).localeCompare(sortKey(a.name)));
6515
+ })).sort(
6516
+ (a, b) => archiveSortKey(b.name).localeCompare(archiveSortKey(a.name))
6517
+ );
6415
6518
  let kept = 0;
6416
6519
  let cut = archives.length;
6417
6520
  for (let i = 0; i < archives.length; i++) {
@@ -6442,6 +6545,129 @@ function pruneArchives() {
6442
6545
  } catch {
6443
6546
  }
6444
6547
  }
6548
+ function parseArchive(name) {
6549
+ const cached3 = archiveMsgCache.get(name);
6550
+ if (cached3) {
6551
+ archiveMsgCache.delete(name);
6552
+ archiveMsgCache.set(name, cached3);
6553
+ return cached3;
6554
+ }
6555
+ try {
6556
+ const raw = fs21.readFileSync(path11.join(ARCHIVE_DIR, name), "utf-8");
6557
+ const data = JSON.parse(raw);
6558
+ const messages = Array.isArray(data?.messages) ? data.messages : [];
6559
+ archiveCountCache.set(name, messages.length);
6560
+ archiveMsgCache.set(name, messages);
6561
+ while (archiveMsgCache.size > ARCHIVE_MSG_CACHE_MAX) {
6562
+ const oldest = archiveMsgCache.keys().next().value;
6563
+ if (oldest === void 0) {
6564
+ break;
6565
+ }
6566
+ archiveMsgCache.delete(oldest);
6567
+ }
6568
+ return messages;
6569
+ } catch (err) {
6570
+ log9.warn("Session archive unreadable", { name, error: err?.message });
6571
+ return null;
6572
+ }
6573
+ }
6574
+ function archiveCount(name) {
6575
+ const cached3 = archiveCountCache.get(name);
6576
+ if (cached3 !== void 0) {
6577
+ return cached3;
6578
+ }
6579
+ const m = ARCHIVE_COUNT_RE.exec(name);
6580
+ if (m) {
6581
+ const n = Number(m[1]);
6582
+ archiveCountCache.set(name, n);
6583
+ return n;
6584
+ }
6585
+ const msgs = parseArchive(name);
6586
+ return msgs ? msgs.length : null;
6587
+ }
6588
+ function readArchiveMessages(name) {
6589
+ return parseArchive(name) ?? [];
6590
+ }
6591
+ function listConversationArchives() {
6592
+ let names;
6593
+ try {
6594
+ names = fs21.readdirSync(ARCHIVE_DIR).filter((n) => ARCHIVE_NAME_RE.test(n));
6595
+ } catch {
6596
+ return { slots: [], archivedCount: 0 };
6597
+ }
6598
+ let maxClearedKey = null;
6599
+ for (const name of names) {
6600
+ if (name.startsWith("cleared-")) {
6601
+ const key = archiveSortKey(name);
6602
+ if (maxClearedKey === null || key > maxClearedKey) {
6603
+ maxClearedKey = key;
6604
+ }
6605
+ }
6606
+ }
6607
+ const rotated = names.filter((n) => n.startsWith("rotated-")).filter((n) => maxClearedKey === null || archiveSortKey(n) > maxClearedKey).sort((a, b) => archiveSortKey(a).localeCompare(archiveSortKey(b)));
6608
+ const slots = [];
6609
+ let offset = 0;
6610
+ for (const name of rotated) {
6611
+ const count = archiveCount(name);
6612
+ if (count === null || count <= 0) {
6613
+ continue;
6614
+ }
6615
+ slots.push({ name, count, offset });
6616
+ offset += count;
6617
+ }
6618
+ return { slots, archivedCount: offset };
6619
+ }
6620
+ function getHistoryPage(state, opts) {
6621
+ const { slots, archivedCount } = listConversationArchives();
6622
+ const liveLen = state.messages.length;
6623
+ const total = archivedCount + liveLen;
6624
+ const rawLimit = opts?.limit;
6625
+ const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(Math.max(1, rawLimit | 0), HISTORY_MAX_LIMIT) : HISTORY_DEFAULT_LIMIT;
6626
+ const rawBefore = opts?.before;
6627
+ const before = typeof rawBefore === "number" && Number.isFinite(rawBefore) ? Math.max(0, Math.min(rawBefore | 0, total)) : total;
6628
+ const peekGlobal = (i) => {
6629
+ if (i >= archivedCount) {
6630
+ return state.messages[i - archivedCount];
6631
+ }
6632
+ for (const slot of slots) {
6633
+ if (i < slot.offset + slot.count) {
6634
+ return readArchiveMessages(slot.name)[i - slot.offset];
6635
+ }
6636
+ }
6637
+ return void 0;
6638
+ };
6639
+ let startIndex = Math.max(0, before - limit);
6640
+ while (startIndex > 0) {
6641
+ const msg = peekGlobal(startIndex);
6642
+ if (msg && msg.role === "user" && msg.toolCallId) {
6643
+ startIndex--;
6644
+ } else {
6645
+ break;
6646
+ }
6647
+ }
6648
+ const endIndex = before;
6649
+ const messages = [];
6650
+ for (const slot of slots) {
6651
+ const slotEnd = slot.offset + slot.count;
6652
+ if (slotEnd <= startIndex || slot.offset >= endIndex) {
6653
+ continue;
6654
+ }
6655
+ const from = Math.max(startIndex, slot.offset) - slot.offset;
6656
+ const to = Math.min(endIndex, slotEnd) - slot.offset;
6657
+ const msgs = readArchiveMessages(slot.name);
6658
+ for (let i = from; i < to; i++) {
6659
+ messages.push(msgs[i]);
6660
+ }
6661
+ }
6662
+ if (endIndex > archivedCount) {
6663
+ const from = Math.max(startIndex, archivedCount) - archivedCount;
6664
+ const to = endIndex - archivedCount;
6665
+ for (let i = from; i < to; i++) {
6666
+ messages.push(state.messages[i]);
6667
+ }
6668
+ }
6669
+ return { messages, startIndex, endIndex, totalMessageCount: total };
6670
+ }
6445
6671
  function rotate(state) {
6446
6672
  const messages = state.messages;
6447
6673
  if (messages.length === 0) {
@@ -7979,8 +8205,6 @@ var USER_FACING_TOOLS = /* @__PURE__ */ new Set([
7979
8205
  "presentPublishPlan"
7980
8206
  ]);
7981
8207
  var FORCED_COMPACTION_THRESHOLD_TOKENS = 85e4;
7982
- var HISTORY_DEFAULT_LIMIT = 500;
7983
- var HISTORY_MAX_LIMIT = 2e3;
7984
8208
  var HeadlessSession = class {
7985
8209
  // Configuration
7986
8210
  opts;
@@ -8726,30 +8950,24 @@ var HeadlessSession = class {
8726
8950
  }
8727
8951
  if (action === "get_history") {
8728
8952
  this.applyPendingBlockUpdates();
8729
- const total = this.state.messages.length;
8730
- const rawLimit = parsed.limit;
8731
- const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(Math.max(1, rawLimit | 0), HISTORY_MAX_LIMIT) : HISTORY_DEFAULT_LIMIT;
8732
- const rawBefore = parsed.before;
8733
- const before = typeof rawBefore === "number" && Number.isFinite(rawBefore) ? Math.max(0, Math.min(rawBefore | 0, total)) : total;
8734
- let startIndex = Math.max(0, before - limit);
8735
- while (startIndex > 0 && this.state.messages[startIndex].role === "user" && this.state.messages[startIndex].toolCallId) {
8736
- startIndex--;
8737
- }
8738
- const endIndex = before;
8953
+ const page = getHistoryPage(this.state, {
8954
+ ...typeof parsed.before === "number" ? { before: parsed.before } : {},
8955
+ ...typeof parsed.limit === "number" ? { limit: parsed.limit } : {}
8956
+ });
8739
8957
  log15.info("History response", {
8740
8958
  requestId,
8741
- startIndex,
8742
- endIndex,
8743
- count: endIndex - startIndex,
8744
- totalMessageCount: total,
8745
- beforeParam: rawBefore,
8746
- limitParam: rawLimit
8959
+ startIndex: page.startIndex,
8960
+ endIndex: page.endIndex,
8961
+ count: page.endIndex - page.startIndex,
8962
+ totalMessageCount: page.totalMessageCount,
8963
+ beforeParam: parsed.before,
8964
+ limitParam: parsed.limit
8747
8965
  });
8748
8966
  this.dispatchSimple(requestId, "history", () => ({
8749
- messages: this.state.messages.slice(startIndex, endIndex),
8750
- startIndex,
8751
- endIndex,
8752
- totalMessageCount: total,
8967
+ messages: page.messages,
8968
+ startIndex: page.startIndex,
8969
+ endIndex: page.endIndex,
8970
+ totalMessageCount: page.totalMessageCount,
8753
8971
  running: this.running,
8754
8972
  ...this.running && this.currentRequestId ? { currentRequestId: this.currentRequestId } : {},
8755
8973
  ...this.state.models && { models: this.state.models },
package/dist/index.js CHANGED
@@ -1169,6 +1169,12 @@ var init_confirmDestructiveAction = __esm({
1169
1169
 
1170
1170
  // src/subagents/common/runCli.ts
1171
1171
  import { spawn } from "child_process";
1172
+ function formatCliResult(r) {
1173
+ const logBlock = r.logs.length > 0 ? r.logs.join("\n") + "\n\n" : "";
1174
+ const body = r.ok ? r.output : `Error: ${r.output}`;
1175
+ const truncNote = r.truncated ? "\n\n[output truncated]" : "";
1176
+ return logBlock + body + truncNote;
1177
+ }
1172
1178
  function runCli(command, args2, options) {
1173
1179
  return new Promise((resolve2) => {
1174
1180
  const timeout = options?.timeout ?? 6e4;
@@ -1177,35 +1183,71 @@ function runCli(command, args2, options) {
1177
1183
  if (options?.jsonLogs && !args2.includes("--json-logs")) {
1178
1184
  finalArgs = args2.length > 0 ? [args2[0], "--json-logs", ...args2.slice(1)] : ["--json-logs"];
1179
1185
  }
1180
- const child = spawn(command, finalArgs, {
1181
- stdio: [options?.stdin ? "pipe" : "ignore", "pipe", "pipe"]
1182
- });
1183
- if (options?.stdin) {
1184
- child.stdin.write(options.stdin);
1185
- child.stdin.end();
1186
- }
1187
1186
  const logs = [];
1188
1187
  let stdout = "";
1189
1188
  let stderr = "";
1190
1189
  let stdoutSize = 0;
1191
1190
  let stderrSize = 0;
1192
1191
  let killed = false;
1192
+ let timedOut = false;
1193
+ let truncated = false;
1194
+ let settled = false;
1195
+ let timer;
1196
+ let killTimer;
1197
+ const finish = (result) => {
1198
+ if (settled) {
1199
+ return;
1200
+ }
1201
+ settled = true;
1202
+ if (timer) {
1203
+ clearTimeout(timer);
1204
+ }
1205
+ if (killTimer) {
1206
+ clearTimeout(killTimer);
1207
+ }
1208
+ resolve2(result);
1209
+ };
1210
+ const child = spawn(command, finalArgs, {
1211
+ stdio: [options?.stdin ? "pipe" : "ignore", "pipe", "pipe"]
1212
+ });
1213
+ child.on("error", (err) => {
1214
+ finish({
1215
+ ok: false,
1216
+ output: err.message,
1217
+ logs,
1218
+ timedOut: false,
1219
+ truncated: false
1220
+ });
1221
+ });
1222
+ if (options?.stdin) {
1223
+ child.stdin.on("error", () => {
1224
+ });
1225
+ try {
1226
+ child.stdin.write(options.stdin);
1227
+ child.stdin.end();
1228
+ } catch {
1229
+ }
1230
+ }
1231
+ const killForOverflow = () => {
1232
+ if (killed) {
1233
+ return;
1234
+ }
1235
+ killed = true;
1236
+ truncated = true;
1237
+ child.kill();
1238
+ };
1193
1239
  child.stdout.on("data", (chunk) => {
1194
1240
  stdoutSize += chunk.length;
1195
1241
  if (stdoutSize <= maxBuffer) {
1196
1242
  stdout += chunk.toString();
1197
- } else if (!killed) {
1198
- killed = true;
1199
- child.kill();
1243
+ } else {
1244
+ killForOverflow();
1200
1245
  }
1201
1246
  });
1202
1247
  child.stderr.on("data", (chunk) => {
1203
1248
  stderrSize += chunk.length;
1204
1249
  if (stderrSize > maxBuffer) {
1205
- if (!killed) {
1206
- killed = true;
1207
- child.kill();
1208
- }
1250
+ killForOverflow();
1209
1251
  return;
1210
1252
  }
1211
1253
  const text = chunk.toString();
@@ -1230,30 +1272,48 @@ function runCli(command, args2, options) {
1230
1272
  }
1231
1273
  }
1232
1274
  });
1233
- const timer = setTimeout(() => {
1275
+ timer = setTimeout(() => {
1276
+ if (killed) {
1277
+ return;
1278
+ }
1234
1279
  killed = true;
1280
+ timedOut = true;
1235
1281
  child.kill();
1282
+ killTimer = setTimeout(() => child.kill("SIGKILL"), SIGKILL_GRACE_MS);
1236
1283
  }, timeout);
1237
1284
  child.on("close", (code) => {
1238
- clearTimeout(timer);
1239
- const logBlock = !options?.onLog && logs.length > 0 ? logs.join("\n") + "\n\n" : "";
1240
1285
  const out = stdout.trim();
1241
- if (out) {
1242
- resolve2(logBlock + out);
1286
+ if (timedOut) {
1287
+ finish({
1288
+ ok: false,
1289
+ output: stderr.trim() || "Process timed out",
1290
+ logs,
1291
+ timedOut: true,
1292
+ truncated
1293
+ });
1243
1294
  return;
1244
1295
  }
1245
- if (code !== 0 || killed) {
1246
- const errMsg = stderr.trim() || (killed ? "Process timed out" : `Exit code ${code}`);
1247
- resolve2(logBlock + `Error: ${errMsg}`);
1296
+ if (out) {
1297
+ finish({ ok: true, output: out, logs, timedOut: false, truncated });
1248
1298
  return;
1249
1299
  }
1250
- resolve2(logBlock + "(no response)");
1300
+ finish({
1301
+ ok: false,
1302
+ output: stderr.trim() || (code !== 0 ? `Exit code ${code}` : "(no response)"),
1303
+ logs,
1304
+ timedOut: false,
1305
+ truncated
1306
+ });
1251
1307
  });
1252
1308
  });
1253
1309
  }
1310
+ var SCRAPE_MAX_BUFFER, SEARCH_MAX_BUFFER, SIGKILL_GRACE_MS;
1254
1311
  var init_runCli = __esm({
1255
1312
  "src/subagents/common/runCli.ts"() {
1256
1313
  "use strict";
1314
+ SCRAPE_MAX_BUFFER = 4 * 1024 * 1024;
1315
+ SEARCH_MAX_BUFFER = 512 * 1024;
1316
+ SIGKILL_GRACE_MS = 2e3;
1257
1317
  }
1258
1318
  });
1259
1319
 
@@ -1281,11 +1341,12 @@ var init_sdkConsultant = __esm({
1281
1341
  },
1282
1342
  async execute(input, context) {
1283
1343
  const query = input.query;
1284
- return runCli("mindstudio", ["ask", query], {
1344
+ const result = await runCli("mindstudio", ["ask", query], {
1285
1345
  timeout: 2e5,
1286
1346
  maxBuffer: 512 * 1024,
1287
1347
  onLog: context?.onLog
1288
1348
  });
1349
+ return formatCliResult(result);
1289
1350
  }
1290
1351
  };
1291
1352
  }
@@ -1331,17 +1392,21 @@ function stripFlags(args2) {
1331
1392
  }
1332
1393
  return out;
1333
1394
  }
1334
- async function runMindstudioCli(args2, options) {
1395
+ async function runMindstudioCliResult(args2, options) {
1335
1396
  const cleanArgs = stripFlags(args2);
1336
1397
  const cliAction = args2[0];
1337
1398
  const agentName = options?.caller ?? "mindstudio-cli";
1338
1399
  const start = Date.now();
1339
- const raw = await runCli("mindstudio", cleanArgs, options);
1400
+ const res = await runCli("mindstudio", cleanArgs, options);
1401
+ if (!res.ok) {
1402
+ return { ok: false, value: formatCliResult(res) };
1403
+ }
1404
+ const truncNote = res.truncated ? "\n\n[output truncated]" : "";
1340
1405
  let envelope;
1341
1406
  try {
1342
- envelope = JSON.parse(raw);
1407
+ envelope = JSON.parse(res.output);
1343
1408
  } catch {
1344
- return raw;
1409
+ return { ok: true, value: res.output + truncNote };
1345
1410
  }
1346
1411
  if (envelope && typeof envelope === "object" && Array.isArray(envelope.results)) {
1347
1412
  const durationMs = Date.now() - start;
@@ -1359,7 +1424,7 @@ async function runMindstudioCli(args2, options) {
1359
1424
  });
1360
1425
  }
1361
1426
  }
1362
- return JSON.stringify(envelope.results);
1427
+ return { ok: true, value: JSON.stringify(envelope.results) + truncNote };
1363
1428
  }
1364
1429
  if (typeof envelope?.$billingCost === "number") {
1365
1430
  recordUsage({
@@ -1380,11 +1445,18 @@ async function runMindstudioCli(args2, options) {
1380
1445
  if (options?.outputKey) {
1381
1446
  const v = envelope?.[options.outputKey];
1382
1447
  if (v === void 0 || v === null) {
1383
- return JSON.stringify(stripDollarKeys(envelope));
1448
+ return { ok: false, value: JSON.stringify(stripDollarKeys(envelope)) };
1384
1449
  }
1385
- return typeof v === "string" ? v : JSON.stringify(v);
1450
+ const value = typeof v === "string" ? v : JSON.stringify(v);
1451
+ return { ok: true, value: value + truncNote };
1386
1452
  }
1387
- return JSON.stringify(stripDollarKeys(envelope));
1453
+ return {
1454
+ ok: true,
1455
+ value: JSON.stringify(stripDollarKeys(envelope)) + truncNote
1456
+ };
1457
+ }
1458
+ async function runMindstudioCli(args2, options) {
1459
+ return (await runMindstudioCliResult(args2, options)).value;
1388
1460
  }
1389
1461
  function stripDollarKeys(envelope) {
1390
1462
  if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
@@ -1412,6 +1484,7 @@ var init_searchGoogle = __esm({
1412
1484
  "src/tools/common/searchGoogle.ts"() {
1413
1485
  "use strict";
1414
1486
  init_runMindstudioCli();
1487
+ init_runCli();
1415
1488
  searchGoogleTool = {
1416
1489
  clearable: false,
1417
1490
  definition: {
@@ -1432,7 +1505,12 @@ var init_searchGoogle = __esm({
1432
1505
  const query = input.query;
1433
1506
  return runMindstudioCli(
1434
1507
  ["search-google", "--query", query, "--export-type", "json"],
1435
- { outputKey: "results", maxBuffer: 512 * 1024, onLog: context?.onLog }
1508
+ {
1509
+ outputKey: "results",
1510
+ maxBuffer: SEARCH_MAX_BUFFER,
1511
+ onLog: context?.onLog,
1512
+ caller: "parent"
1513
+ }
1436
1514
  );
1437
1515
  }
1438
1516
  };
@@ -2011,6 +2089,9 @@ var init_surfaces = __esm({
2011
2089
  "claude-fable-5",
2012
2090
  "claude-5-sonnet",
2013
2091
  "gpt-5.5",
2092
+ "gpt-5.6-sol",
2093
+ "gpt-5.6-terra",
2094
+ "gpt-5.6-luna",
2014
2095
  "gemini-3-pro",
2015
2096
  "gemini-3.1-pro",
2016
2097
  "gemini-3-flash",
@@ -2314,31 +2395,34 @@ function buildPayload(state) {
2314
2395
  function archiveMessages(messages, label, models) {
2315
2396
  fs10.mkdirSync(ARCHIVE_DIR, { recursive: true });
2316
2397
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2317
- let dest = path4.join(ARCHIVE_DIR, `${label}-${ts}.json`);
2398
+ const count = messages.length;
2399
+ let dest = path4.join(ARCHIVE_DIR, `${label}-${ts}.c${count}.json`);
2318
2400
  let n = 1;
2319
2401
  while (fs10.existsSync(dest)) {
2320
- dest = path4.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.json`);
2402
+ dest = path4.join(ARCHIVE_DIR, `${label}-${ts}-${n++}.c${count}.json`);
2321
2403
  }
2322
2404
  const payload = { messages };
2323
2405
  if (models && Object.keys(models).length > 0) {
2324
2406
  payload.models = models;
2325
2407
  }
2326
2408
  fs10.writeFileSync(dest, JSON.stringify(payload), "utf-8");
2327
- log3.info("Session archived", { label, dest, messageCount: messages.length });
2409
+ archiveCountCache.set(path4.basename(dest), count);
2410
+ log3.info("Session archived", { label, dest, messageCount: count });
2328
2411
  pruneArchives();
2329
2412
  return dest;
2330
2413
  }
2331
2414
  function pruneArchives() {
2332
2415
  try {
2333
- const entries = fs10.readdirSync(ARCHIVE_DIR).filter((name) => /^(cleared|rotated)-.*\.json$/.test(name));
2416
+ const entries = fs10.readdirSync(ARCHIVE_DIR).filter((name) => ARCHIVE_NAME_RE.test(name));
2334
2417
  if (entries.length <= 1) {
2335
2418
  return;
2336
2419
  }
2337
- const sortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
2338
2420
  const archives = entries.map((name) => ({
2339
2421
  name,
2340
2422
  size: fs10.statSync(path4.join(ARCHIVE_DIR, name)).size
2341
- })).sort((a, b) => sortKey(b.name).localeCompare(sortKey(a.name)));
2423
+ })).sort(
2424
+ (a, b) => archiveSortKey(b.name).localeCompare(archiveSortKey(a.name))
2425
+ );
2342
2426
  let kept = 0;
2343
2427
  let cut = archives.length;
2344
2428
  for (let i = 0; i < archives.length; i++) {
@@ -2369,6 +2453,129 @@ function pruneArchives() {
2369
2453
  } catch {
2370
2454
  }
2371
2455
  }
2456
+ function parseArchive(name) {
2457
+ const cached3 = archiveMsgCache.get(name);
2458
+ if (cached3) {
2459
+ archiveMsgCache.delete(name);
2460
+ archiveMsgCache.set(name, cached3);
2461
+ return cached3;
2462
+ }
2463
+ try {
2464
+ const raw = fs10.readFileSync(path4.join(ARCHIVE_DIR, name), "utf-8");
2465
+ const data = JSON.parse(raw);
2466
+ const messages = Array.isArray(data?.messages) ? data.messages : [];
2467
+ archiveCountCache.set(name, messages.length);
2468
+ archiveMsgCache.set(name, messages);
2469
+ while (archiveMsgCache.size > ARCHIVE_MSG_CACHE_MAX) {
2470
+ const oldest = archiveMsgCache.keys().next().value;
2471
+ if (oldest === void 0) {
2472
+ break;
2473
+ }
2474
+ archiveMsgCache.delete(oldest);
2475
+ }
2476
+ return messages;
2477
+ } catch (err) {
2478
+ log3.warn("Session archive unreadable", { name, error: err?.message });
2479
+ return null;
2480
+ }
2481
+ }
2482
+ function archiveCount(name) {
2483
+ const cached3 = archiveCountCache.get(name);
2484
+ if (cached3 !== void 0) {
2485
+ return cached3;
2486
+ }
2487
+ const m = ARCHIVE_COUNT_RE.exec(name);
2488
+ if (m) {
2489
+ const n = Number(m[1]);
2490
+ archiveCountCache.set(name, n);
2491
+ return n;
2492
+ }
2493
+ const msgs = parseArchive(name);
2494
+ return msgs ? msgs.length : null;
2495
+ }
2496
+ function readArchiveMessages(name) {
2497
+ return parseArchive(name) ?? [];
2498
+ }
2499
+ function listConversationArchives() {
2500
+ let names;
2501
+ try {
2502
+ names = fs10.readdirSync(ARCHIVE_DIR).filter((n) => ARCHIVE_NAME_RE.test(n));
2503
+ } catch {
2504
+ return { slots: [], archivedCount: 0 };
2505
+ }
2506
+ let maxClearedKey = null;
2507
+ for (const name of names) {
2508
+ if (name.startsWith("cleared-")) {
2509
+ const key = archiveSortKey(name);
2510
+ if (maxClearedKey === null || key > maxClearedKey) {
2511
+ maxClearedKey = key;
2512
+ }
2513
+ }
2514
+ }
2515
+ const rotated = names.filter((n) => n.startsWith("rotated-")).filter((n) => maxClearedKey === null || archiveSortKey(n) > maxClearedKey).sort((a, b) => archiveSortKey(a).localeCompare(archiveSortKey(b)));
2516
+ const slots = [];
2517
+ let offset = 0;
2518
+ for (const name of rotated) {
2519
+ const count = archiveCount(name);
2520
+ if (count === null || count <= 0) {
2521
+ continue;
2522
+ }
2523
+ slots.push({ name, count, offset });
2524
+ offset += count;
2525
+ }
2526
+ return { slots, archivedCount: offset };
2527
+ }
2528
+ function getHistoryPage(state, opts) {
2529
+ const { slots, archivedCount } = listConversationArchives();
2530
+ const liveLen = state.messages.length;
2531
+ const total = archivedCount + liveLen;
2532
+ const rawLimit = opts?.limit;
2533
+ const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(Math.max(1, rawLimit | 0), HISTORY_MAX_LIMIT) : HISTORY_DEFAULT_LIMIT;
2534
+ const rawBefore = opts?.before;
2535
+ const before = typeof rawBefore === "number" && Number.isFinite(rawBefore) ? Math.max(0, Math.min(rawBefore | 0, total)) : total;
2536
+ const peekGlobal = (i) => {
2537
+ if (i >= archivedCount) {
2538
+ return state.messages[i - archivedCount];
2539
+ }
2540
+ for (const slot of slots) {
2541
+ if (i < slot.offset + slot.count) {
2542
+ return readArchiveMessages(slot.name)[i - slot.offset];
2543
+ }
2544
+ }
2545
+ return void 0;
2546
+ };
2547
+ let startIndex = Math.max(0, before - limit);
2548
+ while (startIndex > 0) {
2549
+ const msg = peekGlobal(startIndex);
2550
+ if (msg && msg.role === "user" && msg.toolCallId) {
2551
+ startIndex--;
2552
+ } else {
2553
+ break;
2554
+ }
2555
+ }
2556
+ const endIndex = before;
2557
+ const messages = [];
2558
+ for (const slot of slots) {
2559
+ const slotEnd = slot.offset + slot.count;
2560
+ if (slotEnd <= startIndex || slot.offset >= endIndex) {
2561
+ continue;
2562
+ }
2563
+ const from = Math.max(startIndex, slot.offset) - slot.offset;
2564
+ const to = Math.min(endIndex, slotEnd) - slot.offset;
2565
+ const msgs = readArchiveMessages(slot.name);
2566
+ for (let i = from; i < to; i++) {
2567
+ messages.push(msgs[i]);
2568
+ }
2569
+ }
2570
+ if (endIndex > archivedCount) {
2571
+ const from = Math.max(startIndex, archivedCount) - archivedCount;
2572
+ const to = endIndex - archivedCount;
2573
+ for (let i = from; i < to; i++) {
2574
+ messages.push(state.messages[i]);
2575
+ }
2576
+ }
2577
+ return { messages, startIndex, endIndex, totalMessageCount: total };
2578
+ }
2372
2579
  function rotate(state) {
2373
2580
  const messages = state.messages;
2374
2581
  if (messages.length === 0) {
@@ -2428,7 +2635,7 @@ function clearSession(state) {
2428
2635
  });
2429
2636
  }
2430
2637
  }
2431
- var log3, SESSION_FILE, ARCHIVE_DIR, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES, ARCHIVE_RETENTION_BYTES;
2638
+ var log3, SESSION_FILE, ARCHIVE_DIR, ROTATE_THRESHOLD_BYTES, RETAIN_TAIL_BYTES, ARCHIVE_RETENTION_BYTES, ARCHIVE_NAME_RE, archiveSortKey, ARCHIVE_COUNT_RE, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT, archiveCountCache, archiveMsgCache, ARCHIVE_MSG_CACHE_MAX;
2432
2639
  var init_session = __esm({
2433
2640
  "src/session.ts"() {
2434
2641
  "use strict";
@@ -2442,6 +2649,14 @@ var init_session = __esm({
2442
2649
  ROTATE_THRESHOLD_BYTES = 32 * 1024 * 1024;
2443
2650
  RETAIN_TAIL_BYTES = 16 * 1024 * 1024;
2444
2651
  ARCHIVE_RETENTION_BYTES = 64 * 1024 * 1024;
2652
+ ARCHIVE_NAME_RE = /^(cleared|rotated)-.*\.json$/;
2653
+ archiveSortKey = (name) => name.replace(/^(cleared|rotated)-/, "");
2654
+ ARCHIVE_COUNT_RE = /\.c(\d+)\.json$/;
2655
+ HISTORY_DEFAULT_LIMIT = 500;
2656
+ HISTORY_MAX_LIMIT = 2e3;
2657
+ archiveCountCache = /* @__PURE__ */ new Map();
2658
+ archiveMsgCache = /* @__PURE__ */ new Map();
2659
+ ARCHIVE_MSG_CACHE_MAX = 3;
2445
2660
  }
2446
2661
  });
2447
2662
 
@@ -4756,7 +4971,12 @@ __export(searchGoogle_exports, {
4756
4971
  async function execute(input, onLog) {
4757
4972
  return runMindstudioCli(
4758
4973
  ["search-google", "--query", input.query, "--export-type", "json"],
4759
- { outputKey: "results", onLog, caller: "designExpert" }
4974
+ {
4975
+ outputKey: "results",
4976
+ onLog,
4977
+ caller: "designExpert",
4978
+ maxBuffer: SEARCH_MAX_BUFFER
4979
+ }
4760
4980
  );
4761
4981
  }
4762
4982
  var definition;
@@ -4764,6 +4984,7 @@ var init_searchGoogle2 = __esm({
4764
4984
  "src/subagents/designExpert/tools/searchGoogle.ts"() {
4765
4985
  "use strict";
4766
4986
  init_runMindstudioCli();
4987
+ init_runCli();
4767
4988
  definition = {
4768
4989
  clearable: false,
4769
4990
  name: "searchGoogle",
@@ -4790,9 +5011,6 @@ __export(scrapeWebUrl_exports, {
4790
5011
  });
4791
5012
  async function execute2(input, onLog) {
4792
5013
  const pageOptions = { onlyMainContent: true };
4793
- if (input.screenshot) {
4794
- pageOptions.screenshot = true;
4795
- }
4796
5014
  return runMindstudioCli(
4797
5015
  [
4798
5016
  "scrape-url",
@@ -4801,7 +5019,7 @@ async function execute2(input, onLog) {
4801
5019
  "--page-options",
4802
5020
  JSON.stringify(pageOptions)
4803
5021
  ],
4804
- { onLog, caller: "designExpert" }
5022
+ { onLog, caller: "designExpert", maxBuffer: SCRAPE_MAX_BUFFER }
4805
5023
  );
4806
5024
  }
4807
5025
  var definition2;
@@ -4809,6 +5027,7 @@ var init_scrapeWebUrl = __esm({
4809
5027
  "src/subagents/designExpert/tools/scrapeWebUrl.ts"() {
4810
5028
  "use strict";
4811
5029
  init_runMindstudioCli();
5030
+ init_runCli();
4812
5031
  definition2 = {
4813
5032
  clearable: false,
4814
5033
  name: "scrapeWebUrl",
@@ -4839,7 +5058,7 @@ async function execute3(input, onLog, context) {
4839
5058
  const isImageUrl = /\.(png|jpe?g|webp|gif|svg|avif)(\?|$)/i.test(url);
4840
5059
  let imageUrl = url;
4841
5060
  if (!isImageUrl) {
4842
- const ssUrl = await runMindstudioCli(
5061
+ const ss = await runMindstudioCliResult(
4843
5062
  [
4844
5063
  "screenshot-url",
4845
5064
  "--url",
@@ -4858,10 +5077,10 @@ async function execute3(input, onLog, context) {
4858
5077
  caller: "designExpert"
4859
5078
  }
4860
5079
  );
4861
- if (ssUrl.startsWith("Error")) {
4862
- return `Could not screenshot ${url}: ${ssUrl}`;
5080
+ if (!ss.ok) {
5081
+ return `Could not screenshot ${url}: ${ss.value}`;
4863
5082
  }
4864
- imageUrl = ssUrl;
5083
+ imageUrl = ss.value;
4865
5084
  }
4866
5085
  const analysis = await analyzeImage({
4867
5086
  prompt: analysisPrompt,
@@ -5087,7 +5306,7 @@ ${context}
5087
5306
  <brief>
5088
5307
  ${brief}
5089
5308
  </brief>`;
5090
- const enhanced = await runMindstudioCli(
5309
+ const enhanced = await runMindstudioCliResult(
5091
5310
  [
5092
5311
  "generate-text",
5093
5312
  "--message",
@@ -5097,7 +5316,11 @@ ${brief}
5097
5316
  ],
5098
5317
  { outputKey: "content", timeout: 6e4, onLog, caller: "designExpert" }
5099
5318
  );
5100
- return enhanced.trim();
5319
+ if (!enhanced.ok) {
5320
+ onLog?.(`[enhancePrompt] enhancement failed, using original brief`);
5321
+ return brief;
5322
+ }
5323
+ return enhanced.value.trim();
5101
5324
  }
5102
5325
  var SYSTEM_PROMPT;
5103
5326
  var init_enhancePrompt = __esm({
@@ -5159,7 +5382,7 @@ async function generateImageAssets(opts) {
5159
5382
  config
5160
5383
  }
5161
5384
  });
5162
- const url = await runMindstudioCli(["generate-image"], {
5385
+ const res = await runMindstudioCliResult(["generate-image"], {
5163
5386
  outputKey: "imageUrl",
5164
5387
  jsonLogs: true,
5165
5388
  timeout: 2e5,
@@ -5167,7 +5390,7 @@ async function generateImageAssets(opts) {
5167
5390
  stdin: step,
5168
5391
  caller: "designExpert"
5169
5392
  });
5170
- imageUrls = [url];
5393
+ imageUrls = [res.ok ? res.value : `Error: ${res.value}`];
5171
5394
  } else {
5172
5395
  const steps = enhancedPrompts.map((prompt) => ({
5173
5396
  stepType: "generateImage",
@@ -5179,20 +5402,23 @@ async function generateImageAssets(opts) {
5179
5402
  }
5180
5403
  }
5181
5404
  }));
5182
- const batchResult = await runMindstudioCli(["batch"], {
5405
+ const batchRes = await runMindstudioCliResult(["batch"], {
5183
5406
  jsonLogs: true,
5184
5407
  timeout: 2e5,
5185
5408
  onLog,
5186
5409
  stdin: JSON.stringify(steps),
5187
5410
  caller: "designExpert"
5188
5411
  });
5412
+ if (!batchRes.ok) {
5413
+ return batchRes.value;
5414
+ }
5189
5415
  try {
5190
- const parsed = JSON.parse(batchResult);
5416
+ const parsed = JSON.parse(batchRes.value);
5191
5417
  imageUrls = parsed.map(
5192
5418
  (r) => r.output?.imageUrl ?? `Error: ${r.error}`
5193
5419
  );
5194
5420
  } catch {
5195
- return batchResult;
5421
+ return batchRes.value;
5196
5422
  }
5197
5423
  }
5198
5424
  if (transparentBackground) {
@@ -5201,7 +5427,7 @@ async function generateImageAssets(opts) {
5201
5427
  if (url.startsWith("Error")) {
5202
5428
  return url;
5203
5429
  }
5204
- const result = await runMindstudioCli(
5430
+ const result = await runMindstudioCliResult(
5205
5431
  ["remove-background-from-image", "--image-url", url],
5206
5432
  {
5207
5433
  outputKey: "imageUrl",
@@ -5210,7 +5436,7 @@ async function generateImageAssets(opts) {
5210
5436
  caller: "designExpert"
5211
5437
  }
5212
5438
  );
5213
- return result.startsWith("Error") ? url : result;
5439
+ return result.ok ? result.value : url;
5214
5440
  })
5215
5441
  );
5216
5442
  }
@@ -6700,6 +6926,7 @@ var init_scrapeWebUrl2 = __esm({
6700
6926
  "src/tools/common/scrapeWebUrl.ts"() {
6701
6927
  "use strict";
6702
6928
  init_runMindstudioCli();
6929
+ init_runCli();
6703
6930
  scrapeWebUrlTool = {
6704
6931
  clearable: false,
6705
6932
  definition: {
@@ -6735,7 +6962,11 @@ var init_scrapeWebUrl2 = __esm({
6735
6962
  "--page-options",
6736
6963
  JSON.stringify(pageOptions)
6737
6964
  ],
6738
- { onLog: context?.onLog }
6965
+ {
6966
+ onLog: context?.onLog,
6967
+ maxBuffer: SCRAPE_MAX_BUFFER,
6968
+ caller: "parent"
6969
+ }
6739
6970
  );
6740
6971
  }
6741
6972
  };
@@ -8839,7 +9070,7 @@ var headless_exports = {};
8839
9070
  __export(headless_exports, {
8840
9071
  HeadlessSession: () => HeadlessSession
8841
9072
  });
8842
- var log15, EXTERNAL_TOOL_TIMEOUT_MS, USER_FACING_TOOLS, FORCED_COMPACTION_THRESHOLD_TOKENS, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT, HeadlessSession;
9073
+ var log15, EXTERNAL_TOOL_TIMEOUT_MS, USER_FACING_TOOLS, FORCED_COMPACTION_THRESHOLD_TOKENS, HeadlessSession;
8843
9074
  var init_headless = __esm({
8844
9075
  "src/headless/index.ts"() {
8845
9076
  "use strict";
@@ -8868,8 +9099,6 @@ var init_headless = __esm({
8868
9099
  "presentPublishPlan"
8869
9100
  ]);
8870
9101
  FORCED_COMPACTION_THRESHOLD_TOKENS = 85e4;
8871
- HISTORY_DEFAULT_LIMIT = 500;
8872
- HISTORY_MAX_LIMIT = 2e3;
8873
9102
  HeadlessSession = class {
8874
9103
  // Configuration
8875
9104
  opts;
@@ -9615,30 +9844,24 @@ var init_headless = __esm({
9615
9844
  }
9616
9845
  if (action === "get_history") {
9617
9846
  this.applyPendingBlockUpdates();
9618
- const total = this.state.messages.length;
9619
- const rawLimit = parsed.limit;
9620
- const limit = typeof rawLimit === "number" && Number.isFinite(rawLimit) ? Math.min(Math.max(1, rawLimit | 0), HISTORY_MAX_LIMIT) : HISTORY_DEFAULT_LIMIT;
9621
- const rawBefore = parsed.before;
9622
- const before = typeof rawBefore === "number" && Number.isFinite(rawBefore) ? Math.max(0, Math.min(rawBefore | 0, total)) : total;
9623
- let startIndex = Math.max(0, before - limit);
9624
- while (startIndex > 0 && this.state.messages[startIndex].role === "user" && this.state.messages[startIndex].toolCallId) {
9625
- startIndex--;
9626
- }
9627
- const endIndex = before;
9847
+ const page = getHistoryPage(this.state, {
9848
+ ...typeof parsed.before === "number" ? { before: parsed.before } : {},
9849
+ ...typeof parsed.limit === "number" ? { limit: parsed.limit } : {}
9850
+ });
9628
9851
  log15.info("History response", {
9629
9852
  requestId,
9630
- startIndex,
9631
- endIndex,
9632
- count: endIndex - startIndex,
9633
- totalMessageCount: total,
9634
- beforeParam: rawBefore,
9635
- limitParam: rawLimit
9853
+ startIndex: page.startIndex,
9854
+ endIndex: page.endIndex,
9855
+ count: page.endIndex - page.startIndex,
9856
+ totalMessageCount: page.totalMessageCount,
9857
+ beforeParam: parsed.before,
9858
+ limitParam: parsed.limit
9636
9859
  });
9637
9860
  this.dispatchSimple(requestId, "history", () => ({
9638
- messages: this.state.messages.slice(startIndex, endIndex),
9639
- startIndex,
9640
- endIndex,
9641
- totalMessageCount: total,
9861
+ messages: page.messages,
9862
+ startIndex: page.startIndex,
9863
+ endIndex: page.endIndex,
9864
+ totalMessageCount: page.totalMessageCount,
9642
9865
  running: this.running,
9643
9866
  ...this.running && this.currentRequestId ? { currentRequestId: this.currentRequestId } : {},
9644
9867
  ...this.state.models && { models: this.state.models },
@@ -6,7 +6,7 @@
6
6
  # in specimens/fonts/{slug}.png, runs analyze-image, and writes the
7
7
  # description back to the font's "description" field.
8
8
  #
9
- # Run: bash src/subagents/designExpert/data/compile-font-descriptions.sh
9
+ # Run: bash src/subagents/designExpert/data/sources/compile-font-descriptions.sh
10
10
  # Supports resuming — skips fonts that already have a description.
11
11
 
12
12
  set -euo pipefail
@@ -5,7 +5,7 @@
5
5
  # Reads each image URL from inspiration.raw.json, runs analyze-image via the
6
6
  # mindstudio CLI, and writes the compiled output with URL + analysis.
7
7
  #
8
- # Run manually: bash src/subagents/designExpert/data/compile-inspiration.sh
8
+ # Run manually: bash src/subagents/designExpert/data/sources/compile-inspiration.sh
9
9
  # Processes images sequentially (one API call at a time).
10
10
  # Supports resuming — skips URLs already present in the output file.
11
11
 
@@ -67,7 +67,7 @@ process_one() {
67
67
  --output-key analysis \
68
68
  --no-meta 2>&1) || true
69
69
 
70
- if echo "$ANALYSIS" | grep -q '"error"'; then
70
+ if [ -z "$ANALYSIS" ] || echo "$ANALYSIS" | grep -q '"error"'; then
71
71
  echo " FAILED — $url"
72
72
  return
73
73
  fi
@@ -68,7 +68,7 @@ process_one() {
68
68
  --output-key analysis \
69
69
  --no-meta 2>&1) || true
70
70
 
71
- if echo "$ANALYSIS" | grep -q '"error"'; then
71
+ if [ -z "$ANALYSIS" ] || echo "$ANALYSIS" | grep -q '"error"'; then
72
72
  echo " FAILED — $url"
73
73
  return
74
74
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/remy",
3
- "version": "0.1.245",
3
+ "version": "0.1.247",
4
4
  "description": "Remy coding agent",
5
5
  "repository": {
6
6
  "type": "git",