baro-ai 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // ../baro-orchestrator/scripts/cli.ts
4
- import { existsSync as existsSync2 } from "fs";
5
- import { resolve } from "path";
4
+ import { existsSync as existsSync4 } from "fs";
5
+ import { resolve as resolve3 } from "path";
6
6
 
7
7
  // ../baro-orchestrator/src/orchestrate.ts
8
- import { mkdirSync as mkdirSync2 } from "fs";
9
- import { dirname as dirname2 } from "path";
8
+ import { mkdirSync as mkdirSync3 } from "fs";
9
+ import { dirname as dirname3 } from "path";
10
10
 
11
11
  // ../../node_modules/openai/internal/tslib.mjs
12
12
  function __classPrivateFieldSet(receiver, state, value, kind, f) {
@@ -247,7 +247,7 @@ var safeJSON = (text) => {
247
247
  };
248
248
 
249
249
  // ../../node_modules/openai/internal/utils/sleep.mjs
250
- var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
250
+ var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
251
251
 
252
252
  // ../../node_modules/openai/version.mjs
253
253
  var VERSION = "6.35.0";
@@ -1326,8 +1326,8 @@ function addRequestID(value, response) {
1326
1326
  var _APIPromise_client;
1327
1327
  var APIPromise = class _APIPromise extends Promise {
1328
1328
  constructor(client, responsePromise, parseResponse2 = defaultParseResponse) {
1329
- super((resolve2) => {
1330
- resolve2(null);
1329
+ super((resolve4) => {
1330
+ resolve4(null);
1331
1331
  });
1332
1332
  this.responsePromise = responsePromise;
1333
1333
  this.parseResponse = parseResponse2;
@@ -1756,12 +1756,12 @@ function encodeURIPath(str2) {
1756
1756
  return str2.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
1757
1757
  }
1758
1758
  var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
1759
- var createPathTagFunction = (pathEncoder = encodeURIPath) => function path2(statics, ...params) {
1759
+ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path4(statics, ...params) {
1760
1760
  if (statics.length === 1)
1761
1761
  return statics[0];
1762
1762
  let postPath = false;
1763
1763
  const invalidSegments = [];
1764
- const path3 = statics.reduce((previousValue, currentValue, index) => {
1764
+ const path5 = statics.reduce((previousValue, currentValue, index) => {
1765
1765
  if (/[?#]/.test(currentValue)) {
1766
1766
  postPath = true;
1767
1767
  }
@@ -1778,7 +1778,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path2(stat
1778
1778
  }
1779
1779
  return previousValue + currentValue + (index === params.length ? "" : encoded);
1780
1780
  }, "");
1781
- const pathOnly = path3.split(/[?#]/, 1)[0];
1781
+ const pathOnly = path5.split(/[?#]/, 1)[0];
1782
1782
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
1783
1783
  let match;
1784
1784
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -1799,10 +1799,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path2(stat
1799
1799
  }, "");
1800
1800
  throw new OpenAIError(`Path parameters result in path with invalid segments:
1801
1801
  ${invalidSegments.map((e) => e.error).join("\n")}
1802
- ${path3}
1802
+ ${path5}
1803
1803
  ${underline}`);
1804
1804
  }
1805
- return path3;
1805
+ return path5;
1806
1806
  };
1807
1807
  var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
1808
1808
 
@@ -1975,12 +1975,12 @@ var EventStream = class {
1975
1975
  _EventStream_errored.set(this, false);
1976
1976
  _EventStream_aborted.set(this, false);
1977
1977
  _EventStream_catchingPromiseCreated.set(this, false);
1978
- __classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((resolve2, reject) => {
1979
- __classPrivateFieldSet(this, _EventStream_resolveConnectedPromise, resolve2, "f");
1978
+ __classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((resolve4, reject) => {
1979
+ __classPrivateFieldSet(this, _EventStream_resolveConnectedPromise, resolve4, "f");
1980
1980
  __classPrivateFieldSet(this, _EventStream_rejectConnectedPromise, reject, "f");
1981
1981
  }), "f");
1982
- __classPrivateFieldSet(this, _EventStream_endPromise, new Promise((resolve2, reject) => {
1983
- __classPrivateFieldSet(this, _EventStream_resolveEndPromise, resolve2, "f");
1982
+ __classPrivateFieldSet(this, _EventStream_endPromise, new Promise((resolve4, reject) => {
1983
+ __classPrivateFieldSet(this, _EventStream_resolveEndPromise, resolve4, "f");
1984
1984
  __classPrivateFieldSet(this, _EventStream_rejectEndPromise, reject, "f");
1985
1985
  }), "f");
1986
1986
  __classPrivateFieldGet(this, _EventStream_connectedPromise, "f").catch(() => {
@@ -2064,11 +2064,11 @@ var EventStream = class {
2064
2064
  * const message = await stream.emitted('message') // rejects if the stream errors
2065
2065
  */
2066
2066
  emitted(event) {
2067
- return new Promise((resolve2, reject) => {
2067
+ return new Promise((resolve4, reject) => {
2068
2068
  __classPrivateFieldSet(this, _EventStream_catchingPromiseCreated, true, "f");
2069
2069
  if (event !== "error")
2070
2070
  this.once("error", reject);
2071
- this.once(event, resolve2);
2071
+ this.once(event, resolve4);
2072
2072
  });
2073
2073
  }
2074
2074
  async done() {
@@ -3007,7 +3007,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
3007
3007
  if (done) {
3008
3008
  return { value: void 0, done: true };
3009
3009
  }
3010
- return new Promise((resolve2, reject) => readQueue.push({ resolve: resolve2, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
3010
+ return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
3011
3011
  }
3012
3012
  const chunk = pushQueue.shift();
3013
3013
  return { value: chunk, done: false };
@@ -3839,7 +3839,7 @@ var AssistantStream = class extends EventStream {
3839
3839
  if (done) {
3840
3840
  return { value: void 0, done: true };
3841
3841
  }
3842
- return new Promise((resolve2, reject) => readQueue.push({ resolve: resolve2, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
3842
+ return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
3843
3843
  }
3844
3844
  const chunk = pushQueue.shift();
3845
3845
  return { value: chunk, done: false };
@@ -5788,7 +5788,7 @@ var ResponseStream = class _ResponseStream extends EventStream {
5788
5788
  if (done) {
5789
5789
  return { value: void 0, done: true };
5790
5790
  }
5791
- return new Promise((resolve2, reject) => readQueue.push({ resolve: resolve2, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
5791
+ return new Promise((resolve4, reject) => readQueue.push({ resolve: resolve4, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
5792
5792
  }
5793
5793
  const event = pushQueue.shift();
5794
5794
  return { value: event, done: false };
@@ -6775,9 +6775,9 @@ var OpenAI = class {
6775
6775
  this.apiKey = token;
6776
6776
  return true;
6777
6777
  }
6778
- buildURL(path2, query, defaultBaseURL) {
6778
+ buildURL(path4, query, defaultBaseURL) {
6779
6779
  const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
6780
- const url = isAbsoluteURL(path2) ? new URL(path2) : new URL(baseURL + (baseURL.endsWith("/") && path2.startsWith("/") ? path2.slice(1) : path2));
6780
+ const url = isAbsoluteURL(path4) ? new URL(path4) : new URL(baseURL + (baseURL.endsWith("/") && path4.startsWith("/") ? path4.slice(1) : path4));
6781
6781
  const defaultQuery = this.defaultQuery();
6782
6782
  const pathQuery = Object.fromEntries(url.searchParams);
6783
6783
  if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
@@ -6802,24 +6802,24 @@ var OpenAI = class {
6802
6802
  */
6803
6803
  async prepareRequest(request, { url, options }) {
6804
6804
  }
6805
- get(path2, opts) {
6806
- return this.methodRequest("get", path2, opts);
6805
+ get(path4, opts) {
6806
+ return this.methodRequest("get", path4, opts);
6807
6807
  }
6808
- post(path2, opts) {
6809
- return this.methodRequest("post", path2, opts);
6808
+ post(path4, opts) {
6809
+ return this.methodRequest("post", path4, opts);
6810
6810
  }
6811
- patch(path2, opts) {
6812
- return this.methodRequest("patch", path2, opts);
6811
+ patch(path4, opts) {
6812
+ return this.methodRequest("patch", path4, opts);
6813
6813
  }
6814
- put(path2, opts) {
6815
- return this.methodRequest("put", path2, opts);
6814
+ put(path4, opts) {
6815
+ return this.methodRequest("put", path4, opts);
6816
6816
  }
6817
- delete(path2, opts) {
6818
- return this.methodRequest("delete", path2, opts);
6817
+ delete(path4, opts) {
6818
+ return this.methodRequest("delete", path4, opts);
6819
6819
  }
6820
- methodRequest(method, path2, opts) {
6820
+ methodRequest(method, path4, opts) {
6821
6821
  return this.request(Promise.resolve(opts).then((opts2) => {
6822
- return { method, path: path2, ...opts2 };
6822
+ return { method, path: path4, ...opts2 };
6823
6823
  }));
6824
6824
  }
6825
6825
  request(options, remainingRetries = null) {
@@ -6937,8 +6937,8 @@ var OpenAI = class {
6937
6937
  }));
6938
6938
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
6939
6939
  }
6940
- getAPIList(path2, Page2, opts) {
6941
- return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path2, ...opts2 })) : { method: "get", path: path2, ...opts });
6940
+ getAPIList(path4, Page2, opts) {
6941
+ return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path4, ...opts2 })) : { method: "get", path: path4, ...opts });
6942
6942
  }
6943
6943
  requestAPIList(Page2, options) {
6944
6944
  const request = this.makeRequest(options, null, void 0);
@@ -7029,8 +7029,8 @@ var OpenAI = class {
7029
7029
  }
7030
7030
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
7031
7031
  const options = { ...inputOptions };
7032
- const { method, path: path2, query, defaultBaseURL } = options;
7033
- const url = this.buildURL(path2, query, defaultBaseURL);
7032
+ const { method, path: path4, query, defaultBaseURL } = options;
7033
+ const url = this.buildURL(path4, query, defaultBaseURL);
7034
7034
  if ("timeout" in options)
7035
7035
  validatePositiveInteger("timeout", options.timeout);
7036
7036
  options.timeout = options.timeout ?? this.timeout;
@@ -7665,7 +7665,7 @@ var AgenticEnvironment = class {
7665
7665
  async start() {
7666
7666
  this.isActive = true;
7667
7667
  while (this.isActive) {
7668
- await new Promise((resolve2) => setTimeout(resolve2, 100));
7668
+ await new Promise((resolve4) => setTimeout(resolve4, 100));
7669
7669
  }
7670
7670
  }
7671
7671
  stop() {
@@ -7783,8 +7783,8 @@ var GitGate = class {
7783
7783
  chain = Promise.resolve();
7784
7784
  async acquire() {
7785
7785
  let release;
7786
- const next = new Promise((resolve2) => {
7787
- release = resolve2;
7786
+ const next = new Promise((resolve4) => {
7787
+ release = resolve4;
7788
7788
  });
7789
7789
  const wait = this.chain;
7790
7790
  this.chain = this.chain.then(() => next);
@@ -8491,13 +8491,13 @@ import { join } from "path";
8491
8491
  // ../baro-orchestrator/src/prd.ts
8492
8492
  import { readFileSync, writeFileSync } from "fs";
8493
8493
  var STORY_DEFAULTS = { retries: 2 };
8494
- function loadPrd(path2) {
8495
- const raw = readFileSync(path2, "utf8");
8494
+ function loadPrd(path4) {
8495
+ const raw = readFileSync(path4, "utf8");
8496
8496
  const json = JSON.parse(raw);
8497
- return normalizePrd(json, path2);
8497
+ return normalizePrd(json, path4);
8498
8498
  }
8499
- function savePrd(path2, prd) {
8500
- writeFileSync(path2, JSON.stringify(prd, null, 2) + "\n");
8499
+ function savePrd(path4, prd) {
8500
+ writeFileSync(path4, JSON.stringify(prd, null, 2) + "\n");
8501
8501
  }
8502
8502
  function normalizePrd(input, source) {
8503
8503
  if (!input || typeof input !== "object") {
@@ -9306,8 +9306,8 @@ var Conductor = class extends BaroParticipant {
9306
9306
  if (this.opts.intraLevelDelaySecs == null) {
9307
9307
  this.opts.intraLevelDelaySecs = 10;
9308
9308
  }
9309
- this.done = new Promise((resolve2) => {
9310
- this.resolveDone = resolve2;
9309
+ this.done = new Promise((resolve4) => {
9310
+ this.resolveDone = resolve4;
9311
9311
  });
9312
9312
  }
9313
9313
  setEnvironment(env) {
@@ -9674,9 +9674,9 @@ function applyReplan(prd, replan) {
9674
9674
  }
9675
9675
  return { ...prd, userStories: stories };
9676
9676
  }
9677
- function readFileSyncSafe(path2) {
9677
+ function readFileSyncSafe(path4) {
9678
9678
  try {
9679
- return readFileSync2(path2, "utf8");
9679
+ return readFileSync2(path4, "utf8");
9680
9680
  } catch {
9681
9681
  return null;
9682
9682
  }
@@ -10626,19 +10626,19 @@ function describeCall(tool, args) {
10626
10626
  const tags = [tool.toLowerCase()];
10627
10627
  let summary = `${tool} call`;
10628
10628
  if (tool === "Read") {
10629
- const path2 = stringArg(args, "file_path") ?? stringArg(args, "path");
10630
- if (path2) {
10631
- summary = `Read ${path2}`;
10632
- tags.push(path2);
10633
- const base = path2.split("/").pop();
10629
+ const path4 = stringArg(args, "file_path") ?? stringArg(args, "path");
10630
+ if (path4) {
10631
+ summary = `Read ${path4}`;
10632
+ tags.push(path4);
10633
+ const base = path4.split("/").pop();
10634
10634
  if (base) tags.push(base);
10635
10635
  }
10636
10636
  } else if (tool === "Grep") {
10637
10637
  const pattern = stringArg(args, "pattern");
10638
- const path2 = stringArg(args, "path");
10639
- summary = `Grep '${pattern ?? "?"}'${path2 ? ` in ${path2}` : ""}`;
10638
+ const path4 = stringArg(args, "path");
10639
+ summary = `Grep '${pattern ?? "?"}'${path4 ? ` in ${path4}` : ""}`;
10640
10640
  if (pattern) tags.push(pattern);
10641
- if (path2) tags.push(path2);
10641
+ if (path4) tags.push(path4);
10642
10642
  } else if (tool === "Glob") {
10643
10643
  const pattern = stringArg(args, "pattern");
10644
10644
  summary = `Glob '${pattern ?? "?"}'`;
@@ -10752,27 +10752,27 @@ var Sentry = class extends BaroParticipant {
10752
10752
  if (!WRITE_TOOLS.has(item.name)) return;
10753
10753
  const agentId = source.agentId;
10754
10754
  if (typeof agentId !== "string") return;
10755
- const path2 = extractPath(item);
10756
- if (!path2) return;
10755
+ const path4 = extractPath(item);
10756
+ if (!path4) return;
10757
10757
  const touch = {
10758
10758
  agentId,
10759
- path: path2,
10759
+ path: path4,
10760
10760
  tool: item.name,
10761
10761
  at: Date.now()
10762
10762
  };
10763
10763
  this.touches.push(touch);
10764
- const set = this.touchedBy.get(path2) ?? /* @__PURE__ */ new Set();
10764
+ const set = this.touchedBy.get(path4) ?? /* @__PURE__ */ new Set();
10765
10765
  set.add(agentId);
10766
- this.touchedBy.set(path2, set);
10766
+ this.touchedBy.set(path4, set);
10767
10767
  const otherAgents = [...set].filter((a) => a !== agentId);
10768
10768
  if (otherAgents.length === 0) return;
10769
10769
  this.opts.onOverlap?.({
10770
- path: path2,
10770
+ path: path4,
10771
10771
  agents: [agentId, ...otherAgents]
10772
10772
  });
10773
- if (!this.opts.emitNotice || this.noticedPaths.has(path2)) return;
10774
- this.noticedPaths.add(path2);
10775
- const reason = `agents [${[agentId, ...otherAgents].join(", ")}] both touched ${path2}`;
10773
+ if (!this.opts.emitNotice || this.noticedPaths.has(path4)) return;
10774
+ this.noticedPaths.add(path4);
10775
+ const reason = `agents [${[agentId, ...otherAgents].join(", ")}] both touched ${path4}`;
10776
10776
  for (const env of this.getEnvironments()) {
10777
10777
  ;
10778
10778
  env.deliverBusEvent(
@@ -10782,7 +10782,7 @@ var Sentry = class extends BaroParticipant {
10782
10782
  otherAgents[0],
10783
10783
  "notice",
10784
10784
  reason,
10785
- { path: path2, agents: [agentId, ...otherAgents] }
10785
+ { path: path4, agents: [agentId, ...otherAgents] }
10786
10786
  )
10787
10787
  );
10788
10788
  }
@@ -10802,6 +10802,767 @@ function extractPath(item) {
10802
10802
  return null;
10803
10803
  }
10804
10804
 
10805
+ // ../baro-orchestrator/src/planning/story-tools.ts
10806
+ import * as fs2 from "fs";
10807
+ import * as path3 from "path";
10808
+
10809
+ // ../baro-orchestrator/src/planning/codebase-tools.ts
10810
+ import { execSync } from "child_process";
10811
+ import * as fs from "fs";
10812
+ import * as path2 from "path";
10813
+ var IGNORE = /* @__PURE__ */ new Set([
10814
+ "node_modules",
10815
+ ".git",
10816
+ "dist",
10817
+ "build",
10818
+ ".next",
10819
+ ".nuxt",
10820
+ "coverage",
10821
+ ".cache",
10822
+ "__pycache__",
10823
+ "target",
10824
+ ".output",
10825
+ ".vercel"
10826
+ ]);
10827
+ var MAX_FILE_BYTES = 15e3;
10828
+ var MAX_GREP_LINES = 80;
10829
+ var MAX_BASH_OUTPUT_BYTES = 8e3;
10830
+ function createCodebaseTools(cwd) {
10831
+ return [
10832
+ readFileTool(cwd),
10833
+ listFilesTool(cwd),
10834
+ fileTreeTool(cwd),
10835
+ grepTool(cwd),
10836
+ globTool(cwd),
10837
+ bashTool(cwd)
10838
+ ];
10839
+ }
10840
+ function readFileTool(cwd) {
10841
+ return {
10842
+ type: "function",
10843
+ name: "read_file",
10844
+ description: "Read the full contents of a file by path relative to the project root. Returns up to 15000 characters (truncates past that). Use to inspect package.json, source files, config files, READMEs, etc.",
10845
+ strict: true,
10846
+ parameters: {
10847
+ type: "object",
10848
+ properties: {
10849
+ path: {
10850
+ type: "string",
10851
+ description: "Path relative to the project root (e.g. 'src/index.ts')."
10852
+ }
10853
+ },
10854
+ required: ["path"],
10855
+ additionalProperties: false
10856
+ },
10857
+ async invoke(args) {
10858
+ const target = safePath(cwd, args.path);
10859
+ if (!target) return `Error: path '${args.path}' escapes the project root.`;
10860
+ if (!fs.existsSync(target)) return `File not found: ${args.path}`;
10861
+ const stat = fs.statSync(target);
10862
+ if (stat.isDirectory()) {
10863
+ return `${args.path} is a directory \u2014 use list_files or file_tree.`;
10864
+ }
10865
+ if (stat.size > 5e5) {
10866
+ return `File too large (${(stat.size / 1024).toFixed(0)} KB) \u2014 skip or grep instead.`;
10867
+ }
10868
+ let content = fs.readFileSync(target, "utf-8");
10869
+ if (content.length > MAX_FILE_BYTES) {
10870
+ content = content.slice(0, MAX_FILE_BYTES) + "\n... (truncated)";
10871
+ }
10872
+ return content;
10873
+ }
10874
+ };
10875
+ }
10876
+ function listFilesTool(cwd) {
10877
+ return {
10878
+ type: "function",
10879
+ name: "list_files",
10880
+ description: "List files and directories at a path. Use path='' for the project root. Skips node_modules, .git, dist, build, target, and other dependency dirs.",
10881
+ strict: true,
10882
+ parameters: {
10883
+ type: "object",
10884
+ properties: {
10885
+ path: {
10886
+ type: "string",
10887
+ description: "Directory path relative to the project root. Empty for root."
10888
+ },
10889
+ recursive: {
10890
+ type: "boolean",
10891
+ description: "Walk subdirectories up to 4 deep, capped at 200 entries."
10892
+ }
10893
+ },
10894
+ required: ["path", "recursive"],
10895
+ additionalProperties: false
10896
+ },
10897
+ async invoke(args) {
10898
+ const target = safePath(cwd, args.path || ".");
10899
+ if (!target) return `Error: path '${args.path}' escapes the project root.`;
10900
+ if (!fs.existsSync(target)) return `Directory not found: ${args.path}`;
10901
+ const stat = fs.statSync(target);
10902
+ if (!stat.isDirectory()) return `${args.path} is not a directory \u2014 use read_file.`;
10903
+ const results = [];
10904
+ function walk(dir, prefix, depth) {
10905
+ if (results.length >= 200 || depth > 4) return;
10906
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
10907
+ if (IGNORE.has(entry.name) || entry.name.startsWith(".")) continue;
10908
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
10909
+ if (entry.isDirectory()) {
10910
+ results.push(rel + "/");
10911
+ if (args.recursive) walk(path2.join(dir, entry.name), rel, depth + 1);
10912
+ } else {
10913
+ results.push(rel);
10914
+ }
10915
+ }
10916
+ }
10917
+ walk(target, "", 0);
10918
+ return results.join("\n") || "(empty directory)";
10919
+ }
10920
+ };
10921
+ }
10922
+ function fileTreeTool(cwd) {
10923
+ return {
10924
+ type: "function",
10925
+ name: "file_tree",
10926
+ description: "Show a condensed ASCII tree of the project structure up to 3 levels deep. Cheapest way to get a first overview before deciding which files to read.",
10927
+ strict: true,
10928
+ parameters: {
10929
+ type: "object",
10930
+ properties: {},
10931
+ required: [],
10932
+ additionalProperties: false
10933
+ },
10934
+ async invoke() {
10935
+ const lines = [path2.basename(cwd) + "/"];
10936
+ function walk(dir, prefix, depth) {
10937
+ if (lines.length >= 150 || depth > 3) return;
10938
+ let entries;
10939
+ try {
10940
+ entries = fs.readdirSync(dir, { withFileTypes: true });
10941
+ } catch {
10942
+ return;
10943
+ }
10944
+ entries.sort((a, b) => {
10945
+ if (a.isDirectory() && !b.isDirectory()) return -1;
10946
+ if (!a.isDirectory() && b.isDirectory()) return 1;
10947
+ return a.name.localeCompare(b.name);
10948
+ });
10949
+ for (let i = 0; i < entries.length; i++) {
10950
+ const entry = entries[i];
10951
+ if (IGNORE.has(entry.name) || entry.name.startsWith(".")) continue;
10952
+ const isLast = i === entries.length - 1;
10953
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
10954
+ const childPrefix = isLast ? " " : "\u2502 ";
10955
+ if (entry.isDirectory()) {
10956
+ lines.push(`${prefix}${connector}${entry.name}/`);
10957
+ walk(path2.join(dir, entry.name), prefix + childPrefix, depth + 1);
10958
+ } else {
10959
+ lines.push(`${prefix}${connector}${entry.name}`);
10960
+ }
10961
+ }
10962
+ }
10963
+ walk(cwd, "", 0);
10964
+ return lines.join("\n");
10965
+ }
10966
+ };
10967
+ }
10968
+ function grepTool(cwd) {
10969
+ return {
10970
+ type: "function",
10971
+ name: "grep",
10972
+ description: "Search for a text pattern across project files. Returns matching lines with their file paths. Case-insensitive. Skips dependency directories. Caps results at 80 lines.",
10973
+ strict: true,
10974
+ parameters: {
10975
+ type: "object",
10976
+ properties: {
10977
+ pattern: {
10978
+ type: "string",
10979
+ description: "Text or regex (POSIX) to search for."
10980
+ },
10981
+ path: {
10982
+ type: "string",
10983
+ description: "Directory to search in. Default: entire project."
10984
+ },
10985
+ file_pattern: {
10986
+ type: "string",
10987
+ description: "File glob filter (e.g. '*.ts', '*.tsx'). Default: all files."
10988
+ }
10989
+ },
10990
+ required: ["pattern", "path", "file_pattern"],
10991
+ additionalProperties: false
10992
+ },
10993
+ async invoke(args) {
10994
+ const searchDir = safePath(cwd, args.path || ".");
10995
+ if (!searchDir) return `Error: path '${args.path}' escapes the project root.`;
10996
+ if (!fs.existsSync(searchDir)) return `Directory not found: ${args.path}`;
10997
+ try {
10998
+ const excludes = Array.from(IGNORE).map((d) => `--exclude-dir=${d}`).join(" ");
10999
+ const include = args.file_pattern ? `--include='${args.file_pattern}'` : "";
11000
+ const cmd = `grep -rn -i ${excludes} ${include} --max-count=50 -- ${JSON.stringify(
11001
+ args.pattern
11002
+ )} ${JSON.stringify(searchDir)} 2>/dev/null || true`;
11003
+ const output = execSync(cmd, { encoding: "utf-8", maxBuffer: 2 * 1024 * 1024 });
11004
+ const lines = output.split("\n").filter(Boolean).map((line) => line.startsWith(cwd) ? line.slice(cwd.length + 1) : line);
11005
+ return lines.slice(0, MAX_GREP_LINES).join("\n") || "No matches found.";
11006
+ } catch {
11007
+ return "No matches found.";
11008
+ }
11009
+ }
11010
+ };
11011
+ }
11012
+ function globTool(cwd) {
11013
+ return {
11014
+ type: "function",
11015
+ name: "glob",
11016
+ description: "List files matching a glob pattern (e.g. 'src/**/*.ts'). Useful when you want every file of a type without scanning the whole tree.",
11017
+ strict: true,
11018
+ parameters: {
11019
+ type: "object",
11020
+ properties: {
11021
+ pattern: {
11022
+ type: "string",
11023
+ description: "Glob pattern relative to the project root."
11024
+ }
11025
+ },
11026
+ required: ["pattern"],
11027
+ additionalProperties: false
11028
+ },
11029
+ async invoke(args) {
11030
+ try {
11031
+ const cmd = `cd ${JSON.stringify(cwd)} && find . -path './node_modules' -prune -o -path './.git' -prune -o -path './target' -prune -o -path './dist' -prune -o -path './build' -prune -o -type f -print | grep -E ${JSON.stringify(
11032
+ globToRegex(args.pattern)
11033
+ )} 2>/dev/null | head -200 || true`;
11034
+ const output = execSync(cmd, { encoding: "utf-8", maxBuffer: 1024 * 1024 });
11035
+ const lines = output.split("\n").filter(Boolean).map((l) => l.replace(/^\.\//, ""));
11036
+ return lines.join("\n") || "(no matches)";
11037
+ } catch {
11038
+ return "(glob failed)";
11039
+ }
11040
+ }
11041
+ };
11042
+ }
11043
+ function bashTool(cwd) {
11044
+ return {
11045
+ type: "function",
11046
+ name: "bash",
11047
+ description: "Run a non-destructive read-only shell command in the project root. Use for inspections like 'cat package.json | head', 'git log --oneline | head', 'wc -l src/**/*.ts'. Caps output at 8 KB. Do NOT use for writes, edits, or installs.",
11048
+ strict: true,
11049
+ parameters: {
11050
+ type: "object",
11051
+ properties: {
11052
+ command: {
11053
+ type: "string",
11054
+ description: "The shell command to execute. Read-only operations only."
11055
+ }
11056
+ },
11057
+ required: ["command"],
11058
+ additionalProperties: false
11059
+ },
11060
+ async invoke(args) {
11061
+ try {
11062
+ const output = execSync(args.command, {
11063
+ cwd,
11064
+ encoding: "utf-8",
11065
+ maxBuffer: 4 * 1024 * 1024,
11066
+ timeout: 3e4,
11067
+ stdio: ["ignore", "pipe", "pipe"]
11068
+ });
11069
+ if (output.length > MAX_BASH_OUTPUT_BYTES) {
11070
+ return output.slice(0, MAX_BASH_OUTPUT_BYTES) + `
11071
+ ... (truncated, ${output.length - MAX_BASH_OUTPUT_BYTES} bytes elided)`;
11072
+ }
11073
+ return output || "(empty output)";
11074
+ } catch (e) {
11075
+ const err = e;
11076
+ const stderr = err.stderr instanceof Buffer ? err.stderr.toString() : err.stderr ?? "";
11077
+ return `bash exited with status ${err.status ?? "?"}: ${stderr || err.message || "unknown error"}`;
11078
+ }
11079
+ }
11080
+ };
11081
+ }
11082
+ function safePath(cwd, filePath) {
11083
+ const resolved = path2.resolve(cwd, filePath);
11084
+ if (!resolved.startsWith(path2.resolve(cwd))) return null;
11085
+ return resolved;
11086
+ }
11087
+ function globToRegex(glob) {
11088
+ let regex = "^";
11089
+ let i = 0;
11090
+ while (i < glob.length) {
11091
+ const ch = glob[i];
11092
+ if (ch === "*") {
11093
+ if (glob[i + 1] === "*") {
11094
+ regex += ".*";
11095
+ i += 2;
11096
+ if (glob[i] === "/") i++;
11097
+ continue;
11098
+ }
11099
+ regex += "[^/]*";
11100
+ i++;
11101
+ continue;
11102
+ }
11103
+ if (ch === "?") {
11104
+ regex += "[^/]";
11105
+ i++;
11106
+ continue;
11107
+ }
11108
+ if (ch === ".") {
11109
+ regex += "\\.";
11110
+ i++;
11111
+ continue;
11112
+ }
11113
+ if ("()[]{}+|^$\\".includes(ch)) {
11114
+ regex += "\\" + ch;
11115
+ i++;
11116
+ continue;
11117
+ }
11118
+ regex += ch;
11119
+ i++;
11120
+ }
11121
+ regex += "$";
11122
+ return regex;
11123
+ }
11124
+
11125
+ // ../baro-orchestrator/src/planning/story-tools.ts
11126
+ var MAX_WRITE_BYTES = 5e5;
11127
+ function createStoryTools(cwd) {
11128
+ return [...createCodebaseTools(cwd), writeFileTool(cwd), editFileTool(cwd)];
11129
+ }
11130
+ function writeFileTool(cwd) {
11131
+ return {
11132
+ type: "function",
11133
+ name: "write_file",
11134
+ description: "Create a file or overwrite its full contents. Parent directories are created if needed. Use this for new files or when replacing the entire body of an existing file is simpler than a series of edits. Caps file size at 500 KB.",
11135
+ strict: true,
11136
+ parameters: {
11137
+ type: "object",
11138
+ properties: {
11139
+ path: {
11140
+ type: "string",
11141
+ description: "Path relative to the project root."
11142
+ },
11143
+ content: {
11144
+ type: "string",
11145
+ description: "Full file contents to write. UTF-8."
11146
+ }
11147
+ },
11148
+ required: ["path", "content"],
11149
+ additionalProperties: false
11150
+ },
11151
+ async invoke(args) {
11152
+ const target = safePath2(cwd, args.path);
11153
+ if (!target) return `Error: path '${args.path}' escapes the project root.`;
11154
+ if (args.content.length > MAX_WRITE_BYTES) {
11155
+ return `Error: write_file refused \u2014 content is ${args.content.length} bytes, max is ${MAX_WRITE_BYTES}. Split the file or use edit_file for partial updates.`;
11156
+ }
11157
+ try {
11158
+ const dir = path3.dirname(target);
11159
+ fs2.mkdirSync(dir, { recursive: true });
11160
+ fs2.writeFileSync(target, args.content, "utf-8");
11161
+ return `Wrote ${args.path} (${args.content.length} bytes).`;
11162
+ } catch (e) {
11163
+ return `Error writing ${args.path}: ${e?.message ?? String(e)}`;
11164
+ }
11165
+ }
11166
+ };
11167
+ }
11168
+ function editFileTool(cwd) {
11169
+ return {
11170
+ type: "function",
11171
+ name: "edit_file",
11172
+ description: "Find-and-replace a single occurrence of a string in an existing file. `old` must appear in the file exactly once (case-sensitive, whitespace and newlines included); pass enough surrounding context to make it unique. Returns an error if `old` is not found or appears multiple times \u2014 at that point read_file again and pick a larger snippet.",
11173
+ strict: true,
11174
+ parameters: {
11175
+ type: "object",
11176
+ properties: {
11177
+ path: {
11178
+ type: "string",
11179
+ description: "Path relative to the project root."
11180
+ },
11181
+ old: {
11182
+ type: "string",
11183
+ description: "Exact text to find. Must be unique in the file. Include surrounding lines if needed to make it unique."
11184
+ },
11185
+ new: {
11186
+ type: "string",
11187
+ description: "Replacement text."
11188
+ }
11189
+ },
11190
+ required: ["path", "old", "new"],
11191
+ additionalProperties: false
11192
+ },
11193
+ async invoke(args) {
11194
+ const target = safePath2(cwd, args.path);
11195
+ if (!target) return `Error: path '${args.path}' escapes the project root.`;
11196
+ if (!fs2.existsSync(target)) {
11197
+ return `Error: ${args.path} does not exist. Use write_file to create it.`;
11198
+ }
11199
+ if (fs2.statSync(target).isDirectory()) {
11200
+ return `Error: ${args.path} is a directory.`;
11201
+ }
11202
+ let original;
11203
+ try {
11204
+ original = fs2.readFileSync(target, "utf-8");
11205
+ } catch (e) {
11206
+ return `Error reading ${args.path}: ${e?.message ?? String(e)}`;
11207
+ }
11208
+ if (!args.old) return "Error: `old` is empty \u2014 refusing to edit.";
11209
+ const firstIdx = original.indexOf(args.old);
11210
+ if (firstIdx === -1) {
11211
+ return `Error: \`old\` not found in ${args.path}. Re-read the file and pass an exact-matching snippet (including whitespace).`;
11212
+ }
11213
+ const secondIdx = original.indexOf(args.old, firstIdx + 1);
11214
+ if (secondIdx !== -1) {
11215
+ return `Error: \`old\` appears multiple times in ${args.path} (first at offset ${firstIdx}, again at ${secondIdx}). Include more surrounding context so the match is unique.`;
11216
+ }
11217
+ const updated = original.slice(0, firstIdx) + args.new + original.slice(firstIdx + args.old.length);
11218
+ try {
11219
+ fs2.writeFileSync(target, updated, "utf-8");
11220
+ const delta = updated.length - original.length;
11221
+ const sign = delta >= 0 ? "+" : "";
11222
+ return `Edited ${args.path} (${sign}${delta} bytes).`;
11223
+ } catch (e) {
11224
+ return `Error writing ${args.path}: ${e?.message ?? String(e)}`;
11225
+ }
11226
+ }
11227
+ };
11228
+ }
11229
+ function safePath2(cwd, filePath) {
11230
+ const resolved = path3.resolve(cwd, filePath);
11231
+ if (!resolved.startsWith(path3.resolve(cwd))) return null;
11232
+ return resolved;
11233
+ }
11234
+
11235
+ // ../baro-orchestrator/src/participants/openai-story-agent.ts
11236
+ var STORY_SYSTEM_PROMPT = `You are an autonomous coding agent. The user will hand you exactly one
11237
+ focused story: a goal plus acceptance criteria and (optionally) test
11238
+ commands. Your job is to read the relevant code, make the changes that
11239
+ satisfy every acceptance criterion, run the tests, and commit.
11240
+
11241
+ Tools available (call them as function calls; arguments are JSON):
11242
+ read_file, list_files, file_tree, grep, glob \u2014 explore the repo
11243
+ write_file \u2014 create or overwrite
11244
+ edit_file \u2014 find-and-replace
11245
+ bash \u2014 run any shell cmd
11246
+ (build, test, git\u2026)
11247
+
11248
+ Rules:
11249
+ - Stay tightly inside the story scope. Do NOT refactor unrelated code.
11250
+ If you notice an unrelated issue, note it in your final commit body
11251
+ under "Noted (out of scope)" \u2014 don't fix it inline.
11252
+ - Before you write code, READ the files you'll touch. Confirm exact
11253
+ function names, exact paths.
11254
+ - When you edit, use edit_file with enough surrounding context to make
11255
+ the match unique. write_file is for new files or full rewrites only.
11256
+ - After your edits, run the test commands the story specified. Use the
11257
+ bash tool. Fix any failures and re-run.
11258
+ - When you're done, run \`git add -A && git commit -m "..."\` via bash
11259
+ with a concise message. Then respond with a brief summary message
11260
+ (no more tool calls) \u2014 that signals the turn is over.
11261
+
11262
+ You may be sent corrective feedback after your turn. If you receive a
11263
+ follow-up user message, treat it as additional acceptance criteria
11264
+ and revise.`;
11265
+ var OpenAIStoryAgent = class extends BaroParticipant {
11266
+ spec;
11267
+ opts;
11268
+ model;
11269
+ runner = new OpenAIInferenceRunner();
11270
+ tools;
11271
+ envRef = null;
11272
+ currentPhase = "idle";
11273
+ startedAt = null;
11274
+ resolveDone;
11275
+ done;
11276
+ /** Resolved by `onExternalBusEvent` when a targeted message arrives. */
11277
+ notifyMessage = null;
11278
+ /** Most recent pending message text (set alongside notifyMessage). */
11279
+ pendingMessage = null;
11280
+ constructor(spec, opts = {}) {
11281
+ super();
11282
+ this.spec = {
11283
+ retries: 2,
11284
+ timeoutSecs: 600,
11285
+ retryDelayMs: 1500,
11286
+ quietTimeoutMs: 2e3,
11287
+ maxTurns: 4,
11288
+ hardTimeoutSecs: 0,
11289
+ ...spec
11290
+ };
11291
+ this.opts = {
11292
+ model: opts.model ?? "gpt-5.5",
11293
+ maxRoundsPerTurn: opts.maxRoundsPerTurn ?? 30,
11294
+ perRoundTimeoutSecs: opts.perRoundTimeoutSecs ?? 180
11295
+ };
11296
+ this.model = pickModel2(this.opts.model);
11297
+ this.tools = createStoryTools(spec.cwd);
11298
+ setModelTools(this.model, this.tools);
11299
+ this.done = new Promise((res) => {
11300
+ this.resolveDone = res;
11301
+ });
11302
+ }
11303
+ get id() {
11304
+ return this.spec.id;
11305
+ }
11306
+ get agentId() {
11307
+ return this.spec.id;
11308
+ }
11309
+ getPhase() {
11310
+ return this.currentPhase;
11311
+ }
11312
+ run(env) {
11313
+ if (this.startedAt != null) return this.done;
11314
+ this.envRef = env;
11315
+ this.startedAt = Date.now();
11316
+ this.transition("starting", "story queued");
11317
+ void this.executeAllAttempts();
11318
+ return this.done;
11319
+ }
11320
+ abort() {
11321
+ this.transition("aborted", "external abort");
11322
+ }
11323
+ async onExternalBusEvent(_source, event) {
11324
+ if (event instanceof AgentTargetedMessageItem && event.recipientId === this.spec.id) {
11325
+ this.pendingMessage = event.text;
11326
+ this.notifyMessage?.();
11327
+ }
11328
+ }
11329
+ // ─── Attempt orchestration ──────────────────────────────────────
11330
+ async executeAllAttempts() {
11331
+ const maxAttempts = this.spec.retries + 1;
11332
+ let lastError = null;
11333
+ let attempts = 0;
11334
+ const hardTimer = this.spec.hardTimeoutSecs > 0 ? setTimeout(() => {
11335
+ lastError = `hard timeout (${this.spec.hardTimeoutSecs}s) hit`;
11336
+ this.transition("aborted", lastError);
11337
+ }, this.spec.hardTimeoutSecs * 1e3) : null;
11338
+ for (let i = 0; i < maxAttempts; i++) {
11339
+ attempts = i + 1;
11340
+ if (this.currentPhase === "aborted") break;
11341
+ if (i > 0) {
11342
+ this.transition("starting", `retry ${attempts}/${maxAttempts}`);
11343
+ await sleep2(this.spec.retryDelayMs);
11344
+ }
11345
+ try {
11346
+ await this.runOneAttempt();
11347
+ if (this.currentPhase === "done") {
11348
+ lastError = null;
11349
+ break;
11350
+ }
11351
+ lastError = "attempt did not reach a terminal state";
11352
+ } catch (e) {
11353
+ lastError = e?.message ?? String(e);
11354
+ this.transition("failed", lastError);
11355
+ }
11356
+ }
11357
+ if (hardTimer) clearTimeout(hardTimer);
11358
+ const durationSecs = this.startedAt ? Math.round((Date.now() - this.startedAt) / 1e3) : 0;
11359
+ const success = this.currentPhase === "done";
11360
+ this.envRef?.deliverBusEvent(
11361
+ this,
11362
+ new StoryResultItem(this.spec.id, success, attempts, durationSecs, lastError)
11363
+ );
11364
+ this.resolveDone({
11365
+ storyId: this.spec.id,
11366
+ success,
11367
+ attempts,
11368
+ durationSecs,
11369
+ finalSummary: null,
11370
+ error: lastError
11371
+ });
11372
+ }
11373
+ async runOneAttempt() {
11374
+ const userMessageText = this.spec.prompt;
11375
+ this.envRef?.deliverBusEvent(
11376
+ this,
11377
+ new AgentUserMessageItem(this.spec.id, userMessageText)
11378
+ );
11379
+ let context = ModelContext.create(this.spec.id).addContextItem(SystemMessageItem.create(STORY_SYSTEM_PROMPT)).addContextItem(UserMessageItem.create(userMessageText));
11380
+ this.transition("running", "first turn");
11381
+ for (let turn = 1; turn <= this.spec.maxTurns; turn++) {
11382
+ const turnResult = await this.runOneTurn(context);
11383
+ context = turnResult.context;
11384
+ this.envRef?.deliverBusEvent(
11385
+ this,
11386
+ new ClaudeResultItem(
11387
+ this.spec.id,
11388
+ turnResult.success ? "success" : "error",
11389
+ null,
11390
+ // session id — not applicable for OpenAI
11391
+ !turnResult.success,
11392
+ turnResult.assistantText,
11393
+ null,
11394
+ // usage info — not surfaced this phase
11395
+ null,
11396
+ null,
11397
+ null,
11398
+ {}
11399
+ )
11400
+ );
11401
+ if (!turnResult.success) {
11402
+ this.transition("failed", turnResult.error ?? "turn failed");
11403
+ return;
11404
+ }
11405
+ const gotMessage = await this.waitForMessageOrQuiet();
11406
+ if (!gotMessage) {
11407
+ this.transition("done", `${turn} turn(s)`);
11408
+ return;
11409
+ }
11410
+ context = context.addContextItem(
11411
+ UserMessageItem.create(this.pendingMessage ?? "")
11412
+ );
11413
+ this.envRef?.deliverBusEvent(
11414
+ this,
11415
+ new AgentUserMessageItem(this.spec.id, this.pendingMessage ?? "")
11416
+ );
11417
+ this.pendingMessage = null;
11418
+ }
11419
+ this.transition("failed", `maxTurns (${this.spec.maxTurns}) exhausted`);
11420
+ }
11421
+ /**
11422
+ * Drive a single TURN — repeated inference rounds with tool
11423
+ * execution in between — until the model returns a final
11424
+ * assistant message without tool calls. That terminal message
11425
+ * IS the end of the turn.
11426
+ */
11427
+ async runOneTurn(initialContext) {
11428
+ let context = initialContext;
11429
+ let assistantText = null;
11430
+ const perRoundMs = this.opts.perRoundTimeoutSecs * 1e3;
11431
+ for (let round = 1; round <= this.opts.maxRoundsPerTurn; round++) {
11432
+ const ac = new AbortController();
11433
+ const timer = setTimeout(() => ac.abort(), perRoundMs);
11434
+ const calls = [];
11435
+ let sawMessage = false;
11436
+ let lastMessageText = null;
11437
+ try {
11438
+ for await (const item of this.runner.run(context, this.model, ac.signal)) {
11439
+ if (item.type === "function_call") {
11440
+ await this.envRef?.deliverFunctionCall(this, item);
11441
+ context = context.addContextItem(item);
11442
+ calls.push(item);
11443
+ } else if (item.type === "message" && item.role === "assistant") {
11444
+ await this.envRef?.deliverModelMessage(this, item);
11445
+ context = context.addContextItem(item);
11446
+ const json = item.toJSON();
11447
+ const text = json.content?.[0]?.text ?? "";
11448
+ lastMessageText = text;
11449
+ sawMessage = true;
11450
+ } else if (item.type === "reasoning") {
11451
+ context = context.addContextItem(item);
11452
+ }
11453
+ }
11454
+ } catch (e) {
11455
+ clearTimeout(timer);
11456
+ return {
11457
+ context,
11458
+ success: false,
11459
+ assistantText,
11460
+ error: `inference round ${round} failed: ${e?.message ?? String(e)}`
11461
+ };
11462
+ } finally {
11463
+ clearTimeout(timer);
11464
+ }
11465
+ for (const call of calls) {
11466
+ const tool = this.tools.find((t) => t.name === call.name);
11467
+ const output = tool ? await runToolSafely(tool, call.args) : `Error: tool '${call.name}' not registered`;
11468
+ const outItem = FunctionCallOutputItem.create(call.callId, output);
11469
+ await this.envRef?.deliverFunctionCallOutput(this, outItem);
11470
+ context = context.addContextItem(outItem);
11471
+ }
11472
+ if (sawMessage && calls.length === 0) {
11473
+ return {
11474
+ context,
11475
+ success: true,
11476
+ assistantText: lastMessageText
11477
+ };
11478
+ }
11479
+ if (!sawMessage && calls.length === 0) {
11480
+ return {
11481
+ context,
11482
+ success: false,
11483
+ assistantText,
11484
+ error: `round ${round} returned no items`
11485
+ };
11486
+ }
11487
+ assistantText = lastMessageText ?? assistantText;
11488
+ }
11489
+ return {
11490
+ context,
11491
+ success: false,
11492
+ assistantText,
11493
+ error: `exceeded maxRoundsPerTurn=${this.opts.maxRoundsPerTurn}`
11494
+ };
11495
+ }
11496
+ /**
11497
+ * Resolves `true` if an `AgentTargetedMessageItem` arrives within
11498
+ * `quietTimeoutMs`, `false` if the timer fires first. Mirrors
11499
+ * Claude side's quiet timer that closes stdin after silence.
11500
+ */
11501
+ async waitForMessageOrQuiet() {
11502
+ return new Promise((resolve4) => {
11503
+ const timer = setTimeout(() => {
11504
+ this.notifyMessage = null;
11505
+ resolve4(false);
11506
+ }, this.spec.quietTimeoutMs);
11507
+ this.notifyMessage = () => {
11508
+ clearTimeout(timer);
11509
+ this.notifyMessage = null;
11510
+ resolve4(true);
11511
+ };
11512
+ });
11513
+ }
11514
+ transition(next, detail) {
11515
+ if (next === this.currentPhase) return;
11516
+ this.currentPhase = next;
11517
+ this.envRef?.deliverBusEvent(
11518
+ this,
11519
+ new AgentStateItem(this.spec.id, next, detail)
11520
+ );
11521
+ }
11522
+ };
11523
+ function pickModel2(name) {
11524
+ switch (name) {
11525
+ case "gpt-5.5":
11526
+ return new Gpt55();
11527
+ case "gpt-5.4":
11528
+ return new Gpt54();
11529
+ case "gpt-5.4-mini":
11530
+ return new Gpt54Mini();
11531
+ case "gpt-5.4-nano":
11532
+ return new Gpt54Nano();
11533
+ default:
11534
+ throw new Error(
11535
+ `OpenAIStoryAgent: unknown model "${name}" \u2014 Mozaik 3.9 ships gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano`
11536
+ );
11537
+ }
11538
+ }
11539
+ function setModelTools(model, tools) {
11540
+ const m = model;
11541
+ if (typeof m.setTools !== "function") {
11542
+ throw new Error(
11543
+ `OpenAIStoryAgent: model ${model.specification.name} does not implement ToolCallingCapability`
11544
+ );
11545
+ }
11546
+ m.setTools(tools);
11547
+ }
11548
+ async function runToolSafely(tool, argsJson) {
11549
+ let parsed;
11550
+ try {
11551
+ parsed = JSON.parse(argsJson);
11552
+ } catch (e) {
11553
+ return `Error: tool args were not valid JSON: ${e?.message ?? String(e)}`;
11554
+ }
11555
+ try {
11556
+ const result = await tool.invoke(parsed);
11557
+ return typeof result === "string" ? result : JSON.stringify(result);
11558
+ } catch (e) {
11559
+ return `Error running ${tool.name}: ${e?.message ?? String(e)}`;
11560
+ }
11561
+ }
11562
+ function sleep2(ms) {
11563
+ return new Promise((res) => setTimeout(res, ms));
11564
+ }
11565
+
10805
11566
  // ../baro-orchestrator/src/participants/story-factory.ts
10806
11567
  var StoryFactory = class extends BaroParticipant {
10807
11568
  constructor(opts) {
@@ -10829,7 +11590,18 @@ var StoryFactory = class extends BaroParticipant {
10829
11590
  async spawn(req) {
10830
11591
  if (!this.envRef) return;
10831
11592
  if (this.active.has(req.storyId)) return;
10832
- const agent = new StoryAgent({
11593
+ const llm = this.opts.llm ?? "claude";
11594
+ const agent = llm === "openai" ? new OpenAIStoryAgent(
11595
+ {
11596
+ id: req.storyId,
11597
+ prompt: req.prompt,
11598
+ cwd: this.opts.cwd,
11599
+ model: req.model,
11600
+ retries: req.retries,
11601
+ timeoutSecs: req.timeoutSecs
11602
+ },
11603
+ { model: this.opts.openaiModel ?? "gpt-5.5" }
11604
+ ) : new StoryAgent({
10833
11605
  id: req.storyId,
10834
11606
  prompt: req.prompt,
10835
11607
  cwd: this.opts.cwd,
@@ -11059,7 +11831,7 @@ function surgeonDeterministicReplan(failure) {
11059
11831
  }
11060
11832
 
11061
11833
  // ../baro-orchestrator/src/participants/surgeon-openai.ts
11062
- function pickModel2(name) {
11834
+ function pickModel3(name) {
11063
11835
  switch (name) {
11064
11836
  case "gpt-5.5":
11065
11837
  return new Gpt55();
@@ -11088,7 +11860,7 @@ var SurgeonOpenAI = class extends BaroParticipant {
11088
11860
  model: opts.model ?? "gpt-5.4",
11089
11861
  snapshot: opts.snapshot
11090
11862
  };
11091
- this.model = pickModel2(this.opts.model);
11863
+ this.model = pickModel3(this.opts.model);
11092
11864
  }
11093
11865
  async idle() {
11094
11866
  await Promise.allSettled([...this.pending]);
@@ -11178,7 +11950,7 @@ async function orchestrate(config) {
11178
11950
  );
11179
11951
  }
11180
11952
  if (config.auditLogPath) {
11181
- mkdirSync2(dirname2(config.auditLogPath), { recursive: true });
11953
+ mkdirSync3(dirname3(config.auditLogPath), { recursive: true });
11182
11954
  new Auditor({ path: config.auditLogPath }).join(env);
11183
11955
  }
11184
11956
  if (config.extraParticipants) {
@@ -11307,7 +12079,11 @@ async function orchestrate(config) {
11307
12079
  });
11308
12080
  conductor.setEnvironment(env);
11309
12081
  conductor.join(env);
11310
- const storyFactory = new StoryFactory({ cwd: config.cwd });
12082
+ const storyFactory = new StoryFactory({
12083
+ cwd: config.cwd,
12084
+ llm,
12085
+ openaiModel: "gpt-5.5"
12086
+ });
11311
12087
  storyFactory.setEnvironment(env);
11312
12088
  storyFactory.join(env);
11313
12089
  if (emitTui) {
@@ -11662,9 +12438,9 @@ async function main() {
11662
12438
  printHelp();
11663
12439
  return;
11664
12440
  }
11665
- const cwd = resolve(args.cwd);
11666
- const prdPath = resolve(cwd, args.prd);
11667
- if (!existsSync2(prdPath)) {
12441
+ const cwd = resolve3(args.cwd);
12442
+ const prdPath = resolve3(cwd, args.prd);
12443
+ if (!existsSync4(prdPath)) {
11668
12444
  process.stderr.write(`[cli] PRD not found: ${prdPath}
11669
12445
  `);
11670
12446
  process.exit(2);