@kidlat-dakila/flightplan 0.1.7 → 0.1.8

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.
Files changed (60) hide show
  1. package/backend/main.js +826 -1336
  2. package/backend/plugin/skills/live/SKILL.md +79 -0
  3. package/backend/plugin/skills/review/SKILL.md +8 -5
  4. package/frontend/assets/_branchId-Caxyy__g.js +1 -0
  5. package/frontend/assets/{_branchId-438YLxpk.js → _branchId-IJ4VEzEl.js} +1 -1
  6. package/frontend/assets/{_id-yAHjl11v.js → _id-3L9ONe4W.js} +1 -1
  7. package/frontend/assets/_id-CvORsoNj.js +1 -0
  8. package/frontend/assets/{agents-CUULznCR.js → agents-DnjhKMUc.js} +1 -1
  9. package/frontend/assets/agents-bla75JmI.js +1 -0
  10. package/frontend/assets/{create-Byti8N1q.js → create-B82q7ZMu.js} +1 -1
  11. package/frontend/assets/create-C9yw-thL.js +1 -0
  12. package/frontend/assets/{create-t6MxPYs8.js → create-CEpA1s8S.js} +1 -1
  13. package/frontend/assets/{create-DHjp2cB6.js → create-D0h6PRxn.js} +1 -1
  14. package/frontend/assets/{download-kIOYm7yl.js → download-CwWMZwNG.js} +1 -1
  15. package/frontend/assets/forges-BH3uXIQL.js +1 -0
  16. package/frontend/assets/{forges-5NgJFexV.js → forges-uSh1-w20.js} +1 -1
  17. package/frontend/assets/{index-DVMDfdCW.js → index--nj01XKS.js} +1 -1
  18. package/frontend/assets/index-0VAWT0gs.js +1 -0
  19. package/frontend/assets/{index-Dq71dq4Z.js → index-B0zuYONu.js} +1 -1
  20. package/frontend/assets/{index-WOMODjD2.js → index-BQaZ5gZR.js} +1 -1
  21. package/frontend/assets/{index-BUtsWXGk.js → index-BZ6iL63Z.js} +1 -1
  22. package/frontend/assets/{index-CRQLJ5uq.js → index-Bar8NyIm.js} +1 -1
  23. package/frontend/assets/{index-EFsgiJx1.js → index-BlTfCkr5.js} +1 -1
  24. package/frontend/assets/{index-Dz-E4Hhc.js → index-BvuPClj3.js} +1 -1
  25. package/frontend/assets/index-C9xsdcXo.js +1 -0
  26. package/frontend/assets/{index-Cjx98rmL.js → index-CJDNq1TA.js} +1 -1
  27. package/frontend/assets/{index-N0J0txvy.js → index-CJslcuJ0.js} +1 -1
  28. package/frontend/assets/index-CTKqkqZN.css +1 -0
  29. package/frontend/assets/{index-CAt5Gv5u.js → index-CXyB0ld0.js} +1 -1
  30. package/frontend/assets/{index-BDhIIxnY.js → index-CcORd1N6.js} +1 -1
  31. package/frontend/assets/{index-EItMs0kz.js → index-CrAQ5_e4.js} +1 -1
  32. package/frontend/assets/{index-CmW92SdM.js → index-DG0FDX8u.js} +1 -1
  33. package/frontend/assets/index-DKnoo-kT.js +455 -0
  34. package/frontend/assets/{index-BrL94etp.js → index-DKzDkR6v.js} +1 -1
  35. package/frontend/assets/{index-9MPLrKh9.js → index-DfKNJbUM.js} +40 -40
  36. package/frontend/assets/{index-CBObEG9P.js → index-DlUHisPA.js} +1 -1
  37. package/frontend/assets/{index-DYy-kFEi.js → index-Dv5cF6qF.js} +1 -1
  38. package/frontend/assets/{index-Qxg2C8CP.js → index-JPWJ3IiN.js} +1 -1
  39. package/frontend/assets/{index-RYSZy-Ra.js → index-V1avMli5.js} +1 -1
  40. package/frontend/assets/{index-Dk3p64fP.js → index-iRAoFOv3.js} +1 -1
  41. package/frontend/assets/repositories-DqGU7W63.js +1 -0
  42. package/frontend/assets/{repositories-VWl6aq7K.js → repositories-DuObN8PJ.js} +1 -1
  43. package/frontend/assets/review-BbbMUFsk.js +1 -0
  44. package/frontend/assets/{review-CXzRf1jk.js → review-C3k0h5Vl.js} +1 -1
  45. package/frontend/assets/{route--z3gnt4z.js → route-D8s8AylJ.js} +1 -1
  46. package/frontend/index.html +2 -2
  47. package/package.json +1 -2
  48. package/published-review/app.css +1 -1
  49. package/published-review/app.js +94 -96
  50. package/frontend/assets/_branchId-PhBseeXo.js +0 -1
  51. package/frontend/assets/_id-z15a_Wqg.js +0 -1
  52. package/frontend/assets/agents-CF_BNYuU.js +0 -1
  53. package/frontend/assets/create-DiPNM_Jx.js +0 -1
  54. package/frontend/assets/forges-Dz31lZmK.js +0 -1
  55. package/frontend/assets/index-BDgPH-og.js +0 -1
  56. package/frontend/assets/index-C8_4U9zA.js +0 -457
  57. package/frontend/assets/index-CSy69oc4.js +0 -1
  58. package/frontend/assets/index-Cmg8rxUj.css +0 -1
  59. package/frontend/assets/repositories-BrbB16PW.js +0 -1
  60. package/frontend/assets/review-BMO-QDpl.js +0 -1
package/backend/main.js CHANGED
@@ -3921,50 +3921,50 @@ var require_fast_uri = __commonJS({
3921
3921
  schemelessOptions.skipEscape = true;
3922
3922
  return serialize(resolved, schemelessOptions);
3923
3923
  }
3924
- function resolveComponent(base, relative2, options, skipNormalization) {
3924
+ function resolveComponent(base, relative3, options, skipNormalization) {
3925
3925
  const target = {};
3926
3926
  if (!skipNormalization) {
3927
3927
  base = parse8(serialize(base, options), options);
3928
- relative2 = parse8(serialize(relative2, options), options);
3928
+ relative3 = parse8(serialize(relative3, options), options);
3929
3929
  }
3930
3930
  options = options || {};
3931
- if (!options.tolerant && relative2.scheme) {
3932
- target.scheme = relative2.scheme;
3933
- target.userinfo = relative2.userinfo;
3934
- target.host = relative2.host;
3935
- target.port = relative2.port;
3936
- target.path = removeDotSegments(relative2.path || "");
3937
- target.query = relative2.query;
3931
+ if (!options.tolerant && relative3.scheme) {
3932
+ target.scheme = relative3.scheme;
3933
+ target.userinfo = relative3.userinfo;
3934
+ target.host = relative3.host;
3935
+ target.port = relative3.port;
3936
+ target.path = removeDotSegments(relative3.path || "");
3937
+ target.query = relative3.query;
3938
3938
  } else {
3939
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
3940
- target.userinfo = relative2.userinfo;
3941
- target.host = relative2.host;
3942
- target.port = relative2.port;
3943
- target.path = removeDotSegments(relative2.path || "");
3944
- target.query = relative2.query;
3939
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
3940
+ target.userinfo = relative3.userinfo;
3941
+ target.host = relative3.host;
3942
+ target.port = relative3.port;
3943
+ target.path = removeDotSegments(relative3.path || "");
3944
+ target.query = relative3.query;
3945
3945
  } else {
3946
- if (!relative2.path) {
3946
+ if (!relative3.path) {
3947
3947
  target.path = base.path;
3948
- if (relative2.query !== void 0) {
3949
- target.query = relative2.query;
3948
+ if (relative3.query !== void 0) {
3949
+ target.query = relative3.query;
3950
3950
  } else {
3951
3951
  target.query = base.query;
3952
3952
  }
3953
3953
  } else {
3954
- if (relative2.path[0] === "/") {
3955
- target.path = removeDotSegments(relative2.path);
3954
+ if (relative3.path[0] === "/") {
3955
+ target.path = removeDotSegments(relative3.path);
3956
3956
  } else {
3957
3957
  const baseHasAuthority = base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0;
3958
3958
  if (baseHasAuthority && !base.path) {
3959
- target.path = "/" + relative2.path;
3959
+ target.path = "/" + relative3.path;
3960
3960
  } else if (!base.path) {
3961
- target.path = relative2.path;
3961
+ target.path = relative3.path;
3962
3962
  } else {
3963
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
3963
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
3964
3964
  }
3965
3965
  target.path = base.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
3966
3966
  }
3967
- target.query = relative2.query;
3967
+ target.query = relative3.query;
3968
3968
  }
3969
3969
  target.userinfo = base.userinfo;
3970
3970
  target.host = base.host;
@@ -3972,7 +3972,7 @@ var require_fast_uri = __commonJS({
3972
3972
  }
3973
3973
  target.scheme = base.scheme;
3974
3974
  }
3975
- target.fragment = relative2.fragment;
3975
+ target.fragment = relative3.fragment;
3976
3976
  return target;
3977
3977
  }
3978
3978
  function equal(uriA, uriB, options) {
@@ -7971,9 +7971,9 @@ var require_CachedInputFileSystem = __commonJS({
7971
7971
  this.fileSystem.readFileSync,
7972
7972
  this.fileSystem
7973
7973
  );
7974
- const readFile7 = this._readFileBackend.provide;
7974
+ const readFile6 = this._readFileBackend.provide;
7975
7975
  this.readFile = /** @type {FileSystem["readFile"]} */
7976
- readFile7;
7976
+ readFile6;
7977
7977
  const readFileSync = this._readFileBackend.provideSync;
7978
7978
  this.readFileSync = /** @type {SyncFileSystem["readFileSync"]} */
7979
7979
  readFileSync;
@@ -8561,8 +8561,8 @@ var require_graceful_fs = __commonJS({
8561
8561
  fs2.createReadStream = createReadStream;
8562
8562
  fs2.createWriteStream = createWriteStream;
8563
8563
  var fs$readFile = fs2.readFile;
8564
- fs2.readFile = readFile7;
8565
- function readFile7(path11, options, cb) {
8564
+ fs2.readFile = readFile6;
8565
+ function readFile6(path11, options, cb) {
8566
8566
  if (typeof options === "function")
8567
8567
  cb = options, options = null;
8568
8568
  return go$readFile(path11, options, cb);
@@ -9285,7 +9285,7 @@ var require_path = __commonJS({
9285
9285
  }
9286
9286
  return posixNormalize(maybePath);
9287
9287
  };
9288
- var join22 = (rootPath, request) => {
9288
+ var join21 = (rootPath, request) => {
9289
9289
  if (!request) return normalize5(rootPath);
9290
9290
  const requestType = getType(request);
9291
9291
  switch (requestType) {
@@ -9330,7 +9330,7 @@ var require_path = __commonJS({
9330
9330
  cacheEntry = inner.get(request);
9331
9331
  if (cacheEntry !== void 0) return cacheEntry;
9332
9332
  }
9333
- cacheEntry = join22(rootPath, request);
9333
+ cacheEntry = join21(rootPath, request);
9334
9334
  inner.set(request, cacheEntry);
9335
9335
  return cacheEntry;
9336
9336
  };
@@ -9446,7 +9446,7 @@ var require_path = __commonJS({
9446
9446
  module2.exports.isRelativeRequest = isRelativeRequest;
9447
9447
  module2.exports.isSubPath = isSubPath;
9448
9448
  module2.exports.isWindowsPath = isWindowsPath;
9449
- module2.exports.join = join22;
9449
+ module2.exports.join = join21;
9450
9450
  module2.exports.normalize = normalize5;
9451
9451
  module2.exports.toPath = toPath;
9452
9452
  }
@@ -44067,7 +44067,7 @@ var require_type_is = __commonJS({
44067
44067
  var require_depd = __commonJS({
44068
44068
  "node_modules/.pnpm/depd@2.0.0/node_modules/depd/index.js"(exports2, module2) {
44069
44069
  "use strict";
44070
- var relative2 = require("path").relative;
44070
+ var relative3 = require("path").relative;
44071
44071
  module2.exports = depd;
44072
44072
  var basePath = process.cwd();
44073
44073
  function containsNamespace(str, namespace) {
@@ -44259,7 +44259,7 @@ var require_depd = __commonJS({
44259
44259
  return formatted;
44260
44260
  }
44261
44261
  function formatLocation(callSite) {
44262
- return relative2(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
44262
+ return relative3(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
44263
44263
  }
44264
44264
  function getStack() {
44265
44265
  var limit = Error.stackTraceLimit;
@@ -53284,7 +53284,7 @@ var require_thread_stream = __commonJS({
53284
53284
  var { version: version3 } = require_package();
53285
53285
  var { EventEmitter } = require("events");
53286
53286
  var { Worker } = require("worker_threads");
53287
- var { join: join22 } = require("path");
53287
+ var { join: join21 } = require("path");
53288
53288
  var { pathToFileURL } = require("url");
53289
53289
  var { wait } = require_wait();
53290
53290
  var {
@@ -53327,7 +53327,7 @@ var require_thread_stream = __commonJS({
53327
53327
  function createWorker(stream, opts) {
53328
53328
  const { filename, workerData } = opts;
53329
53329
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
53330
- const toExecute = bundlerOverrides["thread-stream-worker"] || join22(__dirname, "lib", "worker.js");
53330
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join21(__dirname, "lib", "worker.js");
53331
53331
  const worker = new Worker(toExecute, {
53332
53332
  ...opts.workerOpts,
53333
53333
  trackUnmanagedFds: false,
@@ -53730,7 +53730,7 @@ var require_transport = __commonJS({
53730
53730
  "use strict";
53731
53731
  var { createRequire } = require("module");
53732
53732
  var getCallers = require_caller();
53733
- var { join: join22, isAbsolute: isAbsolute2, sep: sep3 } = require("node:path");
53733
+ var { join: join21, isAbsolute: isAbsolute2, sep: sep3 } = require("node:path");
53734
53734
  var sleep = require_atomic_sleep();
53735
53735
  var onExit = require_on_exit_leak_free();
53736
53736
  var ThreadStream = require_thread_stream();
@@ -53793,7 +53793,7 @@ var require_transport = __commonJS({
53793
53793
  throw new Error("only one of target or targets can be specified");
53794
53794
  }
53795
53795
  if (targets) {
53796
- target = bundlerOverrides["pino-worker"] || join22(__dirname, "worker.js");
53796
+ target = bundlerOverrides["pino-worker"] || join21(__dirname, "worker.js");
53797
53797
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
53798
53798
  return {
53799
53799
  ...dest,
@@ -53811,7 +53811,7 @@ var require_transport = __commonJS({
53811
53811
  });
53812
53812
  });
53813
53813
  } else if (pipeline) {
53814
- target = bundlerOverrides["pino-worker"] || join22(__dirname, "worker.js");
53814
+ target = bundlerOverrides["pino-worker"] || join21(__dirname, "worker.js");
53815
53815
  options.pipelines = [pipeline.map((dest) => {
53816
53816
  return {
53817
53817
  ...dest,
@@ -53833,7 +53833,7 @@ var require_transport = __commonJS({
53833
53833
  return origin;
53834
53834
  }
53835
53835
  if (origin === "pino/file") {
53836
- return join22(__dirname, "..", "file.js");
53836
+ return join21(__dirname, "..", "file.js");
53837
53837
  }
53838
53838
  let fixTarget2;
53839
53839
  for (const filePath of callers) {
@@ -54823,7 +54823,7 @@ var require_safe_stable_stringify = __commonJS({
54823
54823
  return circularValue;
54824
54824
  }
54825
54825
  let res = "";
54826
- let join22 = ",";
54826
+ let join21 = ",";
54827
54827
  const originalIndentation = indentation;
54828
54828
  if (Array.isArray(value)) {
54829
54829
  if (value.length === 0) {
@@ -54837,7 +54837,7 @@ var require_safe_stable_stringify = __commonJS({
54837
54837
  indentation += spacer;
54838
54838
  res += `
54839
54839
  ${indentation}`;
54840
- join22 = `,
54840
+ join21 = `,
54841
54841
  ${indentation}`;
54842
54842
  }
54843
54843
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -54845,13 +54845,13 @@ ${indentation}`;
54845
54845
  for (; i155 < maximumValuesToStringify - 1; i155++) {
54846
54846
  const tmp2 = stringifyFnReplacer(String(i155), value, stack, replacer, spacer, indentation);
54847
54847
  res += tmp2 !== void 0 ? tmp2 : "null";
54848
- res += join22;
54848
+ res += join21;
54849
54849
  }
54850
54850
  const tmp = stringifyFnReplacer(String(i155), value, stack, replacer, spacer, indentation);
54851
54851
  res += tmp !== void 0 ? tmp : "null";
54852
54852
  if (value.length - 1 > maximumBreadth) {
54853
54853
  const removedKeys = value.length - maximumBreadth - 1;
54854
- res += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
54854
+ res += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
54855
54855
  }
54856
54856
  if (spacer !== "") {
54857
54857
  res += `
@@ -54872,7 +54872,7 @@ ${originalIndentation}`;
54872
54872
  let separator = "";
54873
54873
  if (spacer !== "") {
54874
54874
  indentation += spacer;
54875
- join22 = `,
54875
+ join21 = `,
54876
54876
  ${indentation}`;
54877
54877
  whitespace = " ";
54878
54878
  }
@@ -54886,13 +54886,13 @@ ${indentation}`;
54886
54886
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
54887
54887
  if (tmp !== void 0) {
54888
54888
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
54889
- separator = join22;
54889
+ separator = join21;
54890
54890
  }
54891
54891
  }
54892
54892
  if (keyLength > maximumBreadth) {
54893
54893
  const removedKeys = keyLength - maximumBreadth;
54894
54894
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
54895
- separator = join22;
54895
+ separator = join21;
54896
54896
  }
54897
54897
  if (spacer !== "" && separator.length > 1) {
54898
54898
  res = `
@@ -54933,7 +54933,7 @@ ${originalIndentation}`;
54933
54933
  }
54934
54934
  const originalIndentation = indentation;
54935
54935
  let res = "";
54936
- let join22 = ",";
54936
+ let join21 = ",";
54937
54937
  if (Array.isArray(value)) {
54938
54938
  if (value.length === 0) {
54939
54939
  return "[]";
@@ -54946,7 +54946,7 @@ ${originalIndentation}`;
54946
54946
  indentation += spacer;
54947
54947
  res += `
54948
54948
  ${indentation}`;
54949
- join22 = `,
54949
+ join21 = `,
54950
54950
  ${indentation}`;
54951
54951
  }
54952
54952
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -54954,13 +54954,13 @@ ${indentation}`;
54954
54954
  for (; i155 < maximumValuesToStringify - 1; i155++) {
54955
54955
  const tmp2 = stringifyArrayReplacer(String(i155), value[i155], stack, replacer, spacer, indentation);
54956
54956
  res += tmp2 !== void 0 ? tmp2 : "null";
54957
- res += join22;
54957
+ res += join21;
54958
54958
  }
54959
54959
  const tmp = stringifyArrayReplacer(String(i155), value[i155], stack, replacer, spacer, indentation);
54960
54960
  res += tmp !== void 0 ? tmp : "null";
54961
54961
  if (value.length - 1 > maximumBreadth) {
54962
54962
  const removedKeys = value.length - maximumBreadth - 1;
54963
- res += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
54963
+ res += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
54964
54964
  }
54965
54965
  if (spacer !== "") {
54966
54966
  res += `
@@ -54973,7 +54973,7 @@ ${originalIndentation}`;
54973
54973
  let whitespace = "";
54974
54974
  if (spacer !== "") {
54975
54975
  indentation += spacer;
54976
- join22 = `,
54976
+ join21 = `,
54977
54977
  ${indentation}`;
54978
54978
  whitespace = " ";
54979
54979
  }
@@ -54982,7 +54982,7 @@ ${indentation}`;
54982
54982
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
54983
54983
  if (tmp !== void 0) {
54984
54984
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
54985
- separator = join22;
54985
+ separator = join21;
54986
54986
  }
54987
54987
  }
54988
54988
  if (spacer !== "" && separator.length > 1) {
@@ -55040,20 +55040,20 @@ ${originalIndentation}`;
55040
55040
  indentation += spacer;
55041
55041
  let res2 = `
55042
55042
  ${indentation}`;
55043
- const join23 = `,
55043
+ const join22 = `,
55044
55044
  ${indentation}`;
55045
55045
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
55046
55046
  let i155 = 0;
55047
55047
  for (; i155 < maximumValuesToStringify - 1; i155++) {
55048
55048
  const tmp2 = stringifyIndent(String(i155), value[i155], stack, spacer, indentation);
55049
55049
  res2 += tmp2 !== void 0 ? tmp2 : "null";
55050
- res2 += join23;
55050
+ res2 += join22;
55051
55051
  }
55052
55052
  const tmp = stringifyIndent(String(i155), value[i155], stack, spacer, indentation);
55053
55053
  res2 += tmp !== void 0 ? tmp : "null";
55054
55054
  if (value.length - 1 > maximumBreadth) {
55055
55055
  const removedKeys = value.length - maximumBreadth - 1;
55056
- res2 += `${join23}"... ${getItemCount(removedKeys)} not stringified"`;
55056
+ res2 += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
55057
55057
  }
55058
55058
  res2 += `
55059
55059
  ${originalIndentation}`;
@@ -55069,16 +55069,16 @@ ${originalIndentation}`;
55069
55069
  return '"[Object]"';
55070
55070
  }
55071
55071
  indentation += spacer;
55072
- const join22 = `,
55072
+ const join21 = `,
55073
55073
  ${indentation}`;
55074
55074
  let res = "";
55075
55075
  let separator = "";
55076
55076
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
55077
55077
  if (isTypedArrayWithEntries(value)) {
55078
- res += stringifyTypedArray(value, join22, maximumBreadth);
55078
+ res += stringifyTypedArray(value, join21, maximumBreadth);
55079
55079
  keys = keys.slice(value.length);
55080
55080
  maximumPropertiesToStringify -= value.length;
55081
- separator = join22;
55081
+ separator = join21;
55082
55082
  }
55083
55083
  if (deterministic) {
55084
55084
  keys = sort(keys, comparator);
@@ -55089,13 +55089,13 @@ ${indentation}`;
55089
55089
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
55090
55090
  if (tmp !== void 0) {
55091
55091
  res += `${separator}${strEscape(key2)}: ${tmp}`;
55092
- separator = join22;
55092
+ separator = join21;
55093
55093
  }
55094
55094
  }
55095
55095
  if (keyLength > maximumBreadth) {
55096
55096
  const removedKeys = keyLength - maximumBreadth;
55097
55097
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
55098
- separator = join22;
55098
+ separator = join21;
55099
55099
  }
55100
55100
  if (separator !== "") {
55101
55101
  res = `
@@ -56838,15 +56838,15 @@ var require_reply = __commonJS({
56838
56838
  notFound(this);
56839
56839
  return this;
56840
56840
  };
56841
- Reply.prototype.then = function(fulfilled, rejected5) {
56841
+ Reply.prototype.then = function(fulfilled, rejected6) {
56842
56842
  if (this.sent) {
56843
56843
  fulfilled();
56844
56844
  return;
56845
56845
  }
56846
56846
  eos(this.raw, (err) => {
56847
56847
  if (err && err.code !== "ERR_STREAM_PREMATURE_CLOSE") {
56848
- if (rejected5) {
56849
- rejected5(err);
56848
+ if (rejected6) {
56849
+ rejected6(err);
56850
56850
  } else {
56851
56851
  this.log && this.log.warn("unhandled rejection on reply.then");
56852
56852
  }
@@ -62041,51 +62041,51 @@ var require_fast_uri2 = __commonJS({
62041
62041
  target.ipv6Zone = source.ipv6Zone;
62042
62042
  }
62043
62043
  }
62044
- function resolveComponent(base, relative2, options, skipNormalization) {
62044
+ function resolveComponent(base, relative3, options, skipNormalization) {
62045
62045
  const target = {};
62046
62046
  if (!skipNormalization) {
62047
62047
  base = parse8(serialize(base, options), options);
62048
- relative2 = parse8(serialize(relative2, options), options);
62048
+ relative3 = parse8(serialize(relative3, options), options);
62049
62049
  }
62050
62050
  options = options || {};
62051
- if (!options.tolerant && relative2.scheme) {
62052
- target.scheme = relative2.scheme;
62053
- target.userinfo = relative2.userinfo;
62054
- copyHost(target, relative2);
62055
- target.port = relative2.port;
62056
- const relativeSchemeHandler = getSchemeHandler(relative2.scheme);
62057
- target.path = relativeSchemeHandler && relativeSchemeHandler.absolutePath ? relative2.path || "" : removeDotSegments(relative2.path || "");
62058
- target.query = relative2.query;
62051
+ if (!options.tolerant && relative3.scheme) {
62052
+ target.scheme = relative3.scheme;
62053
+ target.userinfo = relative3.userinfo;
62054
+ copyHost(target, relative3);
62055
+ target.port = relative3.port;
62056
+ const relativeSchemeHandler = getSchemeHandler(relative3.scheme);
62057
+ target.path = relativeSchemeHandler && relativeSchemeHandler.absolutePath ? relative3.path || "" : removeDotSegments(relative3.path || "");
62058
+ target.query = relative3.query;
62059
62059
  } else {
62060
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
62061
- target.userinfo = relative2.userinfo;
62062
- copyHost(target, relative2);
62063
- target.port = relative2.port;
62064
- target.path = removeDotSegments(relative2.path || "");
62065
- target.query = relative2.query;
62060
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
62061
+ target.userinfo = relative3.userinfo;
62062
+ copyHost(target, relative3);
62063
+ target.port = relative3.port;
62064
+ target.path = removeDotSegments(relative3.path || "");
62065
+ target.query = relative3.query;
62066
62066
  } else {
62067
- if (!relative2.path) {
62067
+ if (!relative3.path) {
62068
62068
  target.path = base.path;
62069
- if (relative2.query !== void 0) {
62070
- target.query = relative2.query;
62069
+ if (relative3.query !== void 0) {
62070
+ target.query = relative3.query;
62071
62071
  } else {
62072
62072
  target.query = base.query;
62073
62073
  }
62074
62074
  } else {
62075
- if (relative2.path[0] === "/") {
62076
- target.path = removeDotSegments(relative2.path);
62075
+ if (relative3.path[0] === "/") {
62076
+ target.path = removeDotSegments(relative3.path);
62077
62077
  } else {
62078
62078
  const baseHasAuthority = base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0;
62079
62079
  if (baseHasAuthority && !base.path) {
62080
- target.path = "/" + relative2.path;
62080
+ target.path = "/" + relative3.path;
62081
62081
  } else if (!base.path) {
62082
- target.path = relative2.path;
62082
+ target.path = relative3.path;
62083
62083
  } else {
62084
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
62084
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
62085
62085
  }
62086
62086
  target.path = base.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
62087
62087
  }
62088
- target.query = relative2.query;
62088
+ target.query = relative3.query;
62089
62089
  }
62090
62090
  target.userinfo = base.userinfo;
62091
62091
  copyHost(target, base);
@@ -62093,7 +62093,7 @@ var require_fast_uri2 = __commonJS({
62093
62093
  }
62094
62094
  target.scheme = base.scheme;
62095
62095
  }
62096
- target.fragment = relative2.fragment;
62096
+ target.fragment = relative3.fragment;
62097
62097
  return target;
62098
62098
  }
62099
62099
  function equal(uriA, uriB, options) {
@@ -65559,14 +65559,14 @@ ${contextFunctionCode}`,
65559
65559
  let code3 = "";
65560
65560
  for (let index = 0, oneOfsLength = oneOfs.length; index < oneOfsLength; index++) {
65561
65561
  const optionLocation = oneOfsLocation.getPropertyLocation(index);
65562
- const optionSchema = optionLocation.schema;
65563
- let mergedSchemaId = context.mergedSchemasIds.get(optionSchema);
65562
+ const optionSchema2 = optionLocation.schema;
65563
+ let mergedSchemaId = context.mergedSchemasIds.get(optionSchema2);
65564
65564
  let mergedLocation = null;
65565
65565
  if (mergedSchemaId) {
65566
65566
  mergedLocation = getMergedLocation(context, mergedSchemaId);
65567
65567
  } else {
65568
65568
  mergedSchemaId = `__fjs_merged_${schemaIdCounter++}`;
65569
- context.mergedSchemasIds.set(optionSchema, mergedSchemaId);
65569
+ context.mergedSchemasIds.set(optionSchema2, mergedSchemaId);
65570
65570
  mergedLocation = mergeLocations(context, mergedSchemaId, [
65571
65571
  locationWithoutOneOf,
65572
65572
  optionLocation
@@ -80247,7 +80247,7 @@ var require_send = __commonJS({
80247
80247
  var { parseTokenList } = require_parseTokenList();
80248
80248
  var { createHttpError } = require_createHttpError();
80249
80249
  var extname = path11.extname;
80250
- var join22 = path11.join;
80250
+ var join21 = path11.join;
80251
80251
  var normalize5 = path11.normalize;
80252
80252
  var resolve3 = path11.resolve;
80253
80253
  var sep3 = path11.sep;
@@ -80334,7 +80334,7 @@ var require_send = __commonJS({
80334
80334
  return { statusCode: 403 };
80335
80335
  }
80336
80336
  parts = path12.split(sep3);
80337
- path12 = normalize5(join22(root, path12));
80337
+ path12 = normalize5(join21(root, path12));
80338
80338
  } else {
80339
80339
  if (UP_PATH_REGEXP.test(path12)) {
80340
80340
  debug('malicious path "%s"', path12);
@@ -80617,7 +80617,7 @@ var require_send = __commonJS({
80617
80617
  let err;
80618
80618
  for (let i155 = 0; i155 < options.index.length; i155++) {
80619
80619
  const index = options.index[i155];
80620
- const p15 = join22(path12, index);
80620
+ const p15 = join21(path12, index);
80621
80621
  const { error: error61, stat: stat2 } = await tryStat(p15);
80622
80622
  if (error61) {
80623
80623
  err = error61;
@@ -82081,9 +82081,9 @@ var require_static = __commonJS({
82081
82081
  });
82082
82082
 
82083
82083
  // apps/backend/src/main.ts
82084
- var import_promises12 = require("node:fs/promises");
82084
+ var import_promises11 = require("node:fs/promises");
82085
82085
  var import_node_os = require("node:os");
82086
- var import_node_path24 = require("node:path");
82086
+ var import_node_path23 = require("node:path");
82087
82087
 
82088
82088
  // node_modules/.pnpm/zod@4.5.4/node_modules/zod/v4/classic/external.js
82089
82089
  var external_exports = {};
@@ -112635,23 +112635,15 @@ var appendEvents = (conversation, events) => {
112635
112635
  return { conversation: { ...conversation, journal: [...conversation.journal, ...entries] }, entries };
112636
112636
  };
112637
112637
  var isInbound = (event) => event.kind === "reviewer-message" || event.kind === "answers" && event.origin === "agent";
112638
- var isOutbound = (event) => event.kind === "agent-message" || event.kind === "question-batch" && event.origin === "agent";
112639
112638
  var lastDeliveredSequenceOf = (journal) => journal.reduce(
112640
112639
  (highest, { event }) => event.kind === "delivery" ? Math.max(highest, event.throughSequence) : highest,
112641
112640
  NO_SEQUENCE
112642
112641
  );
112643
112642
  var inboundAfter = (journal, sequence) => journal.filter((entry) => entry.sequence > sequence && isInbound(entry.event));
112644
- var pendingInboundOf = (journal) => inboundAfter(journal, lastDeliveredSequenceOf(journal));
112645
- var hasOutboundAfter = (journal, sequence) => journal.some((entry) => entry.sequence > sequence && isOutbound(entry.event));
112646
112643
  var findQuestionBatch = (journal, batchId) => journal.map(({ event }) => event).find((event) => event.kind === "question-batch" && event.id === batchId) ?? null;
112647
112644
  var findAnswerSet = (journal, batchId) => journal.map(({ event }) => event).find((event) => event.kind === "answers" && event.batchId === batchId) ?? null;
112648
112645
  var findWithdrawal = (journal, batchId) => journal.map(({ event }) => event).find((event) => event.kind === "questions-withdrawn" && event.batchId === batchId) ?? null;
112649
112646
  var isBatchSettled = (journal, batchId) => findAnswerSet(journal, batchId) !== null || findWithdrawal(journal, batchId) !== null;
112650
- var openQuestionBatchesOf = (journal) => journal.map(({ event }) => event).filter((event) => event.kind === "question-batch").filter((batch) => !isBatchSettled(journal, batch.id));
112651
- var latestFailureOf = (journal) => journal.reduce((latest, { event }) => {
112652
- if (event.kind === "agent-failure") return event;
112653
- return event.kind === "delivery" ? null : latest;
112654
- }, null);
112655
112647
  var latestModelChoiceOf = (journal, agent) => journal.map(({ event }) => event).filter((event) => event.kind === "model-choice" && event.agent === agent).at(-1) ?? null;
112656
112648
  var messageBodiesOf = (journal) => journal.flatMap(({ event }) => event.kind === "reviewer-message" || event.kind === "agent-message" ? [event.body] : []);
112657
112649
  var reviewKeyId = (key) => `${key.repositoryName}
@@ -112663,6 +112655,7 @@ var NO_ANSWER = "(no answer)";
112663
112655
  var LISTEN_PATH_SUFFIX = "live/agent";
112664
112656
  var listenPathOf = (key) => `/api/${encodeURIComponent(key.repositoryName)}/${encodeURIComponent(key.targetId)}/${LISTEN_PATH_SUFFIX}`;
112665
112657
  var listenCommandOf = (serverUrl, key) => `curl -sN "${serverUrl}${listenPathOf(key)}"`;
112658
+ var listenPermissionRuleOf = (serverUrl) => `Bash(curl -sN "${serverUrl}/api/*)`;
112666
112659
  var formatReviewerMessage = (message) => message.reference === null ? `The reviewer wrote:
112667
112660
  ${message.body}` : `The reviewer wrote about ${message.reference}:
112668
112661
  ${message.body}`;
@@ -112685,43 +112678,54 @@ var formatInboundEntry = (entry, journal) => {
112685
112678
  return null;
112686
112679
  };
112687
112680
  var formatInbound = (entries, journal) => entries.map((entry) => formatInboundEntry(entry, journal)).filter((text2) => text2 !== null).join(PARAGRAPH_BREAK);
112688
- var formatListenPacket = (text2, nextListenCommand) => [text2, `When you are ready for the reviewer's next reply, run this in the background:
112689
- ${nextListenCommand}`].join(
112690
- PARAGRAPH_BREAK
112691
- );
112692
- var describeTarget = (key) => `repositoryName "${key.repositoryName}" and targetId "${key.targetId}"`;
112693
- var spawnInstructionsOf = (key) => [
112694
- `Flight Plan started this session for the reviewer of the review target with ${describeTarget(key)}.`,
112695
- "Nobody reads this terminal. The reviewer reads you only in Flight Plan's web app.",
112696
- `Reach the reviewer only through Flight Plan's MCP tools, always with ${describeTarget(key)}:`,
112697
- "- send-message sends the reviewer a message. Send every answer, summary or result this way.",
112698
- "- ask-questions asks the reviewer one question or a batch. After asking, end your turn without waiting: Flight Plan closes this session and resumes it with the answers.",
112699
- "When the reviewer asks you to change the review artifact, change it through read, nodes and update-nodes, then tell them what you changed with send-message.",
112700
- "Flight Plan's tools can take a few seconds to connect after this session starts or resumes, and may look unavailable or removed at first. If they do, end your turn: Flight Plan tells you when to use them.",
112701
- "Never run the curl listen command that the tool descriptions mention. It is for sessions the reviewer started themselves."
112702
- ].join("\n");
112681
+ var LIVE_SKILL_COMMAND = "/flightplan:live";
112682
+ var liveCommandOf = (key) => `${LIVE_SKILL_COMMAND} ${key.repositoryName}/${key.targetId}`;
112683
+ var listenAgainOf = (nextListenCommand) => `When you have done everything above, listen again with this command, in the foreground:
112684
+ ${nextListenCommand}`;
112685
+ var formatListenPacket = (text2, nextListenCommand) => [text2, listenAgainOf(nextListenCommand)].join(PARAGRAPH_BREAK);
112686
+ var formatIdlePacket = (nextListenCommand) => [
112687
+ "Nothing new from the reviewer yet.",
112688
+ `Listen again with this command, in the foreground:
112689
+ ${nextListenCommand}`
112690
+ ].join(PARAGRAPH_BREAK);
112691
+ var listenInstructionOf = (command) => `The reviewer's reply comes on your listening stream. If you are not listening yet, run this in the foreground. It exits when the reviewer replies, and its output holds the reply:
112692
+ ${command}`;
112703
112693
  var MCP_INSTRUCTIONS = [
112704
112694
  "Flight Plan connects you with the reviewer of a pull request, who reads you in Flight Plan's web app.",
112705
112695
  "A review target is named by repositoryName (the repository's name in Flight Plan) and targetId (pr-<number> for a pull request, branch-<name> for a branch).",
112706
- "A review artifact is filled in through get-instructions, read, nodes, update-nodes and complete. Flight Plan builds its structure when the reviewer starts the review, and these tools write the prose onto it. get-instructions says how to write each field, for the configuration the reviewer chose.",
112696
+ "A review artifact is filled in through get-instructions, read, nodes, update-nodes and complete. Flight Plan builds its structure when the reviewer opens the review, and these tools write the prose onto it. get-instructions says how to write each field, for the configuration the reviewer chose.",
112707
112697
  "send-message sends the reviewer a message. ask-questions asks one question or a batch; each question either offers options (one or several picks, and the reviewer can always write their own answer) or asks for free text.",
112708
112698
  "record-publication records the address of the Claude artifact a Flight Plan was published to. Call it only after Flight Plan asked you to publish one and your Artifact tool did.",
112709
112699
  "record-import stores a Flight Plan you fetched from a Claude artifact. Call it only after Flight Plan asked you to fetch one and you copied the file where it said.",
112710
- "If Flight Plan started your session, its system prompt says so: end your turn after asking, and Flight Plan resumes you with the answers.",
112711
- "If the reviewer started your session, the ask-questions result gives a curl command. Run it in the background. It exits when the reviewer replies, and its output holds the reply and the command to run for the next one."
112700
+ "Flight Plan shows the reviewer whether an agent is online for their review. Every call to these tools counts, and ping counts when you have nothing else to call. While you work on a review without calling Flight Plan, call ping at least every 5 seconds.",
112701
+ "Requests from the reviewer arrive on the review's listening stream: a curl command the tool results give you. It waits until there is something to do, prints it with the command to listen again, and exits."
112712
112702
  ].join("\n");
112713
- var REPLY_REMINDER = [
112714
- "You have not written to the reviewer this turn, and the reviewer reads only what you send through Flight Plan.",
112715
- "If send-message or ask-questions looked unavailable, they were still connecting and are available now.",
112716
- "Send your reply with send-message, or ask with ask-questions."
112717
- ].join(" ");
112718
112703
 
112719
- // apps/backend/src/procedures/get-conversation/utils/agent-next-step.ts
112720
- var SPAWNED_AFTER_ASKING = "End your turn now. Flight Plan resumes this session with the reviewer's answers.";
112721
- var listenInstructionOf = (command) => `Run this in the background. It exits when the reviewer replies, and its output holds the reply:
112722
- ${command}`;
112723
- var isSpawnedFor = (key, context) => context.conversationEnvironment.agents.find(key) !== null;
112724
- var listenCommandFor = (key, context) => listenCommandOf(context.conversationEnvironment.serverUrl, key);
112704
+ // apps/backend/src/procedures/get-conversation/utils/agent-presence.ts
112705
+ var ONLINE_WINDOW_MS = 15e3;
112706
+ var NO_PRESENCE = { openStreamCount: 0, lastSeenAt: null, claimedThroughSequence: 0 };
112707
+ var isAgentOnline = (presence, now) => presence.openStreamCount > 0 || presence.lastSeenAt !== null && now.getTime() - presence.lastSeenAt.getTime() <= ONLINE_WINDOW_MS;
112708
+ var InMemoryAgentPresenceRegistry = class {
112709
+ presences = /* @__PURE__ */ new Map();
112710
+ presenceOf = (key) => this.presences.get(reviewKeyId(key)) ?? NO_PRESENCE;
112711
+ openStream = (key) => {
112712
+ this.change(key, (presence) => ({ ...presence, openStreamCount: presence.openStreamCount + 1 }));
112713
+ };
112714
+ closeStream = (key, closedAt) => {
112715
+ this.change(key, (presence) => ({ ...presence, openStreamCount: Math.max(presence.openStreamCount - 1, 0), lastSeenAt: closedAt }));
112716
+ };
112717
+ markSeen = (key, seenAt) => {
112718
+ this.change(key, (presence) => ({ ...presence, lastSeenAt: seenAt }));
112719
+ };
112720
+ claimThrough = (key, sequence) => {
112721
+ if (this.presenceOf(key).claimedThroughSequence >= sequence) return false;
112722
+ this.change(key, (presence) => ({ ...presence, claimedThroughSequence: sequence }));
112723
+ return true;
112724
+ };
112725
+ change = (key, update) => {
112726
+ this.presences = new Map([...this.presences, [reviewKeyId(key), update(this.presenceOf(key))]]);
112727
+ };
112728
+ };
112725
112729
 
112726
112730
  // apps/backend/src/procedures/get-conversation/utils/review-target.ts
112727
112731
  var TARGET_ID_PATTERN = /^(?:pr-[1-9]\d*|branch-.+)$/;
@@ -112748,29 +112752,11 @@ var appendAndPublish = async (key, events, context) => {
112748
112752
  return entries;
112749
112753
  };
112750
112754
  var findReviewTarget = async (key, context) => lookUpReviewTarget(key, await context.repositoryStore.listRegistered());
112751
- var markAgentActivity = (key, context) => {
112752
- const now = context.now();
112753
- context.conversationEnvironment.agents.update(key, (agent) => ({ ...agent, lastActivityAt: now }));
112754
- };
112755
- var recordAgentFailure = async (key, reason, context) => {
112756
- await appendAndPublish(
112757
- key,
112758
- [{ kind: "agent-failure", id: context.conversationEnvironment.newId(), reason, failedAt: context.now() }],
112759
- context
112760
- );
112761
- };
112762
- var confirmAgentStart = async (sessionId, context) => {
112763
- const { agents, store, hub } = context.conversationEnvironment;
112764
- const agent = agents.findBySessionId(sessionId);
112765
- if (agent === null) return null;
112766
- const now = context.now();
112767
- const confirmed = agents.update(agent.key, (current) => ({ ...current, phase: "running", lastActivityAt: now }));
112768
- if (agent.phase === "starting") {
112769
- const conversation = await store.read(agent.key);
112770
- if (conversation.sessionId !== sessionId) await store.saveSessionId(agent.key, sessionId);
112771
- hub.publish(agent.key);
112772
- }
112773
- return confirmed;
112755
+ var isAgentOnlineFor = (key, context) => isAgentOnline(context.conversationEnvironment.presence.presenceOf(key), context.now());
112756
+ var markAgentSeen = (key, context) => {
112757
+ const wasOnline = isAgentOnlineFor(key, context);
112758
+ context.conversationEnvironment.presence.markSeen(key, context.now());
112759
+ if (!wasOnline) context.conversationEnvironment.hub.publish(key);
112774
112760
  };
112775
112761
 
112776
112762
  // apps/backend/src/procedures/ask-reviewer/utils/plan-question-batch.ts
@@ -112797,14 +112783,13 @@ var askReviewer = async (input2, context) => {
112797
112783
  const batchId = context.conversationEnvironment.newId();
112798
112784
  const plan4 = planQuestionBatch(batchId, input2.questions);
112799
112785
  if (plan4.kind === "rejected") return plan4;
112800
- markAgentActivity(key, context);
112786
+ markAgentSeen(key, context);
112801
112787
  await appendAndPublish(
112802
112788
  key,
112803
112789
  [{ kind: "question-batch", id: batchId, origin: "agent", questions: plan4.questions, askedAt: context.now() }],
112804
112790
  context
112805
112791
  );
112806
- if (isSpawnedFor(key, context)) return { kind: "asked", batchId, note: askedNote(batchId, SPAWNED_AFTER_ASKING) };
112807
- const command = listenCommandFor(key, context);
112792
+ const command = listenCommandOf(context.conversationEnvironment.serverUrl, key);
112808
112793
  return { kind: "asked", batchId, note: askedNote(batchId, listenInstructionOf(command)) };
112809
112794
  };
112810
112795
 
@@ -136363,19 +136348,19 @@ var ancestorsOf3 = (at) => {
136363
136348
  return above === "." ? [] : [above, ...ancestorsOf3(above)];
136364
136349
  };
136365
136350
  var isResolverFileSystem = (fileSystem) => ["stat", "lstat", "readFile", "readJson", "readdir", "readlink"].every((method) => method in fileSystem);
136366
- var inPackageFolder = (relative2) => relative2.startsWith(`${PACKAGE_FOLDER}/`) ? relative2.slice(PACKAGE_FOLDER.length + 1) : null;
136367
- function installedPathOf(relative2, packages) {
136368
- const inside = inPackageFolder(relative2);
136351
+ var inPackageFolder = (relative3) => relative3.startsWith(`${PACKAGE_FOLDER}/`) ? relative3.slice(PACKAGE_FOLDER.length + 1) : null;
136352
+ function installedPathOf(relative3, packages) {
136353
+ const inside = inPackageFolder(relative3);
136369
136354
  const name = inside === null ? void 0 : [...packages.keys()].find((one) => inside === one || inside.startsWith(`${one}/`));
136370
136355
  const directory = name === void 0 ? void 0 : packages.get(name);
136371
136356
  if (inside === null || name === void 0 || directory === void 0) return null;
136372
136357
  const found2 = path7.posix.join(directory, inside.slice(name.length));
136373
136358
  return found2 === CURRENT_FOLDER ? "" : found2;
136374
136359
  }
136375
- function installedNamesIn(relative2, packages) {
136360
+ function installedNamesIn(relative3, packages) {
136376
136361
  const names = [...packages.keys()];
136377
- if (relative2 === PACKAGE_FOLDER) return [...new Set(names.map((name) => name.split("/")[0]))];
136378
- const scope = inPackageFolder(relative2);
136362
+ if (relative3 === PACKAGE_FOLDER) return [...new Set(names.map((name) => name.split("/")[0]))];
136363
+ const scope = inPackageFolder(relative3);
136379
136364
  if (scope === null || !scope.startsWith(SCOPE_PREFIX) || scope.includes("/")) return null;
136380
136365
  const scoped = names.filter((name) => name.startsWith(`${scope}/`)).map((name) => name.slice(scope.length + 1));
136381
136366
  return scoped.length === 0 ? null : scoped;
@@ -136384,29 +136369,29 @@ function repoFileSystem(files, read2, packages) {
136384
136369
  const directories = /* @__PURE__ */ new Set(["", ...[...files].flatMap(ancestorsOf3)]);
136385
136370
  const inRepo = (at) => path7.posix.relative(ROOT, at);
136386
136371
  const realPathOf = async (at) => {
136387
- const relative2 = inRepo(at);
136388
- return installedPathOf(relative2, await packages) ?? relative2;
136372
+ const relative3 = inRepo(at);
136373
+ return installedPathOf(relative3, await packages) ?? relative3;
136389
136374
  };
136390
136375
  const installedNamesAt = async (at) => installedNamesIn(inRepo(at), await packages);
136391
136376
  const stats = (isFile) => ({ isFile: () => isFile, isDirectory: () => !isFile, isSymbolicLink: () => false });
136392
136377
  const buffers = new Memo();
136393
- const textOf7 = async (relative2) => {
136394
- if (!TSCONFIG.test(relative2)) return read2(relative2);
136395
- const [value, found2] = await Promise.all([read2(relative2), packages]);
136396
- return value === null ? null : rootedPaths(value, relative2, (entry) => extendedConfig(entry, relative2, files, found2));
136378
+ const textOf7 = async (relative3) => {
136379
+ if (!TSCONFIG.test(relative3)) return read2(relative3);
136380
+ const [value, found2] = await Promise.all([read2(relative3), packages]);
136381
+ return value === null ? null : rootedPaths(value, relative3, (entry) => extendedConfig(entry, relative3, files, found2));
136397
136382
  };
136398
- const bufferOf = async (relative2) => buffers.remember(relative2, async () => textOf7(relative2).then((value) => value === null ? null : Buffer.from(value)));
136383
+ const bufferOf = async (relative3) => buffers.remember(relative3, async () => textOf7(relative3).then((value) => value === null ? null : Buffer.from(value)));
136399
136384
  const manifests = new Memo();
136400
- const manifestOf = async (relative2) => manifests.remember(
136401
- relative2,
136402
- async () => bufferOf(relative2).then((value) => value === null ? null : JSON.parse(value.toString("utf8")))
136385
+ const manifestOf = async (relative3) => manifests.remember(
136386
+ relative3,
136387
+ async () => bufferOf(relative3).then((value) => value === null ? null : JSON.parse(value.toString("utf8")))
136403
136388
  );
136404
136389
  const stat2 = (at, ...args) => {
136405
136390
  const done = callbackOf(args);
136406
- Promise.all([realPathOf(at), installedNamesAt(at)]).then(([relative2, installed]) => {
136391
+ Promise.all([realPathOf(at), installedNamesAt(at)]).then(([relative3, installed]) => {
136407
136392
  if (installed !== null) done(null, stats(false));
136408
- else if (files.has(relative2)) done(null, stats(true));
136409
- else if (directories.has(relative2)) done(null, stats(false));
136393
+ else if (files.has(relative3)) done(null, stats(true));
136394
+ else if (directories.has(relative3)) done(null, stats(false));
136410
136395
  else done(missing(at));
136411
136396
  }, failWith(done));
136412
136397
  };
@@ -136415,29 +136400,29 @@ function repoFileSystem(files, read2, packages) {
136415
136400
  lstat: stat2,
136416
136401
  readFile: (at, ...args) => {
136417
136402
  const done = callbackOf(args);
136418
- realPathOf(at).then(async (relative2) => files.has(relative2) ? await bufferOf(relative2) : null).then((value) => {
136403
+ realPathOf(at).then(async (relative3) => files.has(relative3) ? await bufferOf(relative3) : null).then((value) => {
136419
136404
  deliver(done, at, value);
136420
136405
  }, failWith(done));
136421
136406
  },
136422
136407
  readJson: (at, ...args) => {
136423
136408
  const done = callbackOf(args);
136424
- realPathOf(at).then(async (relative2) => files.has(relative2) ? await manifestOf(relative2) : null).then((value) => {
136409
+ realPathOf(at).then(async (relative3) => files.has(relative3) ? await manifestOf(relative3) : null).then((value) => {
136425
136410
  deliver(done, at, value);
136426
136411
  }, failWith(done));
136427
136412
  },
136428
136413
  readdir: (at, ...args) => {
136429
136414
  const done = callbackOf(args);
136430
- Promise.all([realPathOf(at), installedNamesAt(at)]).then(([relative2, installed]) => {
136415
+ Promise.all([realPathOf(at), installedNamesAt(at)]).then(([relative3, installed]) => {
136431
136416
  if (installed !== null) {
136432
136417
  done(null, installed);
136433
136418
  return;
136434
136419
  }
136435
- if (!directories.has(relative2)) {
136420
+ if (!directories.has(relative3)) {
136436
136421
  done(missing(at));
136437
136422
  return;
136438
136423
  }
136439
- const prefix = relative2 === "" ? "" : `${relative2}/`;
136440
- const names = [...files, ...directories].filter((one) => one !== relative2 && one.startsWith(prefix)).map((one) => one.slice(prefix.length)).filter((rest) => !rest.includes("/"));
136424
+ const prefix = relative3 === "" ? "" : `${relative3}/`;
136425
+ const names = [...files, ...directories].filter((one) => one !== relative3 && one.startsWith(prefix)).map((one) => one.slice(prefix.length)).filter((rest) => !rest.includes("/"));
136441
136426
  done(null, [...new Set(names)]);
136442
136427
  }, failWith(done));
136443
136428
  },
@@ -136552,8 +136537,8 @@ function moduleResolver(files, read2) {
136552
136537
  return nearest === null ? null : await findCoveringConfig(nearest, file2, configReader, files, /* @__PURE__ */ new Set()) ?? nearest;
136553
136538
  });
136554
136539
  const repoPathOf = async (found2) => {
136555
- const relative2 = path7.posix.relative(ROOT, found2);
136556
- return installedPathOf(relative2, await packages) ?? relative2;
136540
+ const relative3 = path7.posix.relative(ROOT, found2);
136541
+ return installedPathOf(relative3, await packages) ?? relative3;
136557
136542
  };
136558
136543
  const resolverFor = async (config3) => resolvers.remember(
136559
136544
  config3 ?? "",
@@ -137335,8 +137320,8 @@ var evaluationFoldersOf = (library, manifests) => {
137335
137320
  var keyPathOf2 = (file2, key) => file2.under === void 0 ? key : `${file2.under}${KEY_SEPARATOR4}${key}`;
137336
137321
  var findConfigs = (setting, folder, readerByPath) => [...readerByPath].flatMap(([filePath, reader]) => {
137337
137322
  if (!isWithin(folder, filePath)) return [];
137338
- const relative2 = relativeTo(folder, filePath);
137339
- const file2 = setting.files.find((candidate) => fitsGlob(candidate.glob, relative2) && (candidate.under === void 0 || reader.holdsSection(candidate.under)));
137323
+ const relative3 = relativeTo(folder, filePath);
137324
+ const file2 = setting.files.find((candidate) => fitsGlob(candidate.glob, relative3) && (candidate.under === void 0 || reader.holdsSection(candidate.under)));
137340
137325
  return file2 === void 0 ? [] : [{ filePath, file: file2, reader }];
137341
137326
  });
137342
137327
  var baseOf = ({ filePath, file: file2 }, folder) => file2.base === "package" ? folder : levelsAbove(folderOf(filePath), file2.levelsUp ?? 0);
@@ -147401,7 +147386,7 @@ var missingArtifactRejection = (key) => ({
147401
147386
  var openArtifact = async (key, context) => {
147402
147387
  const target = await findReviewTarget(key, context);
147403
147388
  if (target.kind === "rejected") return target;
147404
- markAgentActivity(key, context);
147389
+ markAgentSeen(key, context);
147405
147390
  const [artifact, request, savedConfigurations] = await Promise.all([
147406
147391
  context.reviewStore.findArtifact(key),
147407
147392
  context.reviewStore.findRequest(key),
@@ -147996,6 +147981,37 @@ var registerNodesTool = (mcpServer, context) => {
147996
147981
  );
147997
147982
  };
147998
147983
 
147984
+ // apps/backend/src/procedures/record-ping/index.ts
147985
+ var recordedNoteOf = (listenCommand) => `Flight Plan shows the reviewer that you are online. The review's listen command:
147986
+ ${listenCommand}`;
147987
+ var recordPing = async (input2, context) => {
147988
+ const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
147989
+ const target = await findReviewTarget(key, context);
147990
+ if (target.kind === "rejected") return target;
147991
+ markAgentSeen(key, context);
147992
+ return { kind: "recorded", note: recordedNoteOf(listenCommandOf(context.conversationEnvironment.serverUrl, key)) };
147993
+ };
147994
+
147995
+ // apps/backend/src/procedures/record-ping/utils/schemas.ts
147996
+ var recordPingInputShape = {
147997
+ repositoryName: external_exports.string().min(1).describe("The repository's name in Flight Plan."),
147998
+ targetId: external_exports.string().min(1).describe("pr-<number> for a pull request, branch-<name> for a branch.")
147999
+ };
148000
+ var recordPingInputSchema = external_exports.strictObject(recordPingInputShape);
148001
+
148002
+ // apps/backend/src/server/mcp/tools/ping/index.ts
148003
+ var DESCRIPTION5 = "Tell Flight Plan you are still online for a review. Call it at least every 5 seconds while you work without calling Flight Plan's other tools, which count as well.";
148004
+ var registerPingTool = (mcpServer, context) => {
148005
+ mcpServer.registerTool(
148006
+ "ping",
148007
+ { description: DESCRIPTION5, inputSchema: recordPingInputShape },
148008
+ async (input2) => {
148009
+ const recording = await recordPing(input2, context);
148010
+ return recording.kind === "recorded" ? { content: [{ type: "text", text: recording.note }] } : { content: [{ type: "text", text: recording.message }], isError: true };
148011
+ }
148012
+ );
148013
+ };
148014
+
147999
148015
  // apps/backend/src/procedures/read-artifact/index.ts
148000
148016
  var DEFAULT_DIFF_FILE_LIMIT = 25;
148001
148017
  var readArtifact = async (input2, context) => {
@@ -148019,7 +148035,7 @@ var readArtifact = async (input2, context) => {
148019
148035
  };
148020
148036
 
148021
148037
  // apps/backend/src/server/mcp/tools/read/index.ts
148022
- var DESCRIPTION5 = [
148038
+ var DESCRIPTION6 = [
148023
148039
  "Returns one part of a review artifact. Every segment and every changed file comes back with its `segmentId`, which is what a write names.",
148024
148040
  "`overview` is the skeleton: ids, titles, the files a code link can name, and what is still unwritten, with no prose. Read it to get oriented.",
148025
148041
  "`diff` is the whole change, per file, with the waypoint ids that cover each one. Read the change here, not from git.",
@@ -148028,7 +148044,7 @@ var DESCRIPTION5 = [
148028
148044
  var registerReadTool = (mcpServer, context) => {
148029
148045
  mcpServer.registerTool(
148030
148046
  "read",
148031
- { description: DESCRIPTION5, inputSchema: readArtifactInputShape },
148047
+ { description: DESCRIPTION6, inputSchema: readArtifactInputShape },
148032
148048
  async (input2) => {
148033
148049
  const reading = await readArtifact(input2, context);
148034
148050
  return reading.kind === "read" ? jsonResult(reading.content) : errorResult(reading.message);
@@ -149048,6 +149064,7 @@ var completeImport = async (input2, context) => {
149048
149064
  const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
149049
149065
  const request = await context.reviewStore.readImportRequest(key);
149050
149066
  if (request?.importedAt !== null) return rejected(NOT_REQUESTED_MESSAGE);
149067
+ markAgentSeen(key, context);
149051
149068
  const path11 = context.importFiles.pathOf(key);
149052
149069
  const text2 = await context.importFiles.read(key);
149053
149070
  if (text2 === null) return rejected(`There is no file at ${path11} yet. Copy the Flight Plan there, then call record-import again.`);
@@ -149066,7 +149083,7 @@ var completeImportInputShape = { ...reviewTargetShape };
149066
149083
  var completeImportInputSchema = external_exports.strictObject(completeImportInputShape);
149067
149084
 
149068
149085
  // apps/backend/src/server/mcp/tools/record-import/index.ts
149069
- var DESCRIPTION6 = [
149086
+ var DESCRIPTION7 = [
149070
149087
  "Stores the Flight Plan you fetched from a Claude artifact as the Flight Plan of a review target.",
149071
149088
  "Call it once, after copying the fetched file to the path Flight Plan named when it asked for the import.",
149072
149089
  "It takes no path and no content: Flight Plan reads the file from that path."
@@ -149074,7 +149091,7 @@ var DESCRIPTION6 = [
149074
149091
  var registerRecordImportTool = (mcpServer, context) => {
149075
149092
  mcpServer.registerTool(
149076
149093
  "record-import",
149077
- { description: DESCRIPTION6, inputSchema: completeImportInputShape },
149094
+ { description: DESCRIPTION7, inputSchema: completeImportInputShape },
149078
149095
  async (input2) => {
149079
149096
  const completion = await completeImport(input2, context);
149080
149097
  return completion.kind === "imported" ? textResult(completion.note) : errorResult(completion.message);
@@ -149316,9 +149333,9 @@ var listLinkablePullRequests = async (repository, branchName, context) => {
149316
149333
  // apps/backend/src/procedures/publish-artifact/utils/publishing-progress.ts
149317
149334
  var LISTENER_PUBLISHING_WAIT_MS = 6e5;
149318
149335
  var wasRecordedSince = (request, publications) => publications.some((publication) => publication.kind === "claude" && publication.publishedAt.getTime() >= request.requestedAt.getTime());
149319
- var isPublishingUnderway = (request, publications, isAgentRunning, now) => {
149336
+ var isPublishingUnderway = (request, publications, isAgentOnline2, now) => {
149320
149337
  if (request === null || wasRecordedSince(request, publications)) return false;
149321
- return request.channel === "agent" ? isAgentRunning : now.getTime() - request.requestedAt.getTime() < LISTENER_PUBLISHING_WAIT_MS;
149338
+ return request.channel === "agent" ? isAgentOnline2 : now.getTime() - request.requestedAt.getTime() < LISTENER_PUBLISHING_WAIT_MS;
149322
149339
  };
149323
149340
 
149324
149341
  // packages/git-forge/src/git-adapter/base.ts
@@ -152391,14 +152408,14 @@ function toRelativePath(path11) {
152391
152408
  return `${url2.pathname}${url2.search}`.replace(/^\/+/, "");
152392
152409
  }
152393
152410
  function buildRequestPath(path11, query) {
152394
- const relative2 = toRelativePath(path11);
152411
+ const relative3 = toRelativePath(path11);
152395
152412
  const params = new URLSearchParams(
152396
152413
  Object.entries(query ?? {}).flatMap(
152397
152414
  ([key, value]) => typeof value === "string" || typeof value === "number" ? [[key, String(value)]] : []
152398
152415
  )
152399
152416
  );
152400
152417
  const suffix = params.toString();
152401
- return suffix === "" ? relative2 : `${relative2}${relative2.includes("?") ? "&" : "?"}${suffix}`;
152418
+ return suffix === "" ? relative3 : `${relative3}${relative3.includes("?") ? "&" : "?"}${suffix}`;
152402
152419
  }
152403
152420
  function buildBodyArgs(body) {
152404
152421
  return Object.entries(body ?? {}).flatMap(([name, value]) => typeof value === "string" ? ["-f", `${name}=${value}`] : []);
@@ -154472,43 +154489,9 @@ var AGENT_CLI_NAMES = {
154472
154489
  cursor: "cursor-agent"
154473
154490
  };
154474
154491
 
154475
- // apps/backend/src/procedures/request-permission/utils/describe-permission.ts
154476
- var ALLOW_LABEL = "Allow";
154477
- var DENY_LABEL = "Deny";
154478
- var MAX_DETAIL_LENGTH = 4e3;
154479
- var JSON_INDENT2 = 2;
154480
- var DETAIL_FIELDS = ["command", "file_path", "url", "pattern", "path"];
154481
- var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
154482
- var leadingField = (toolInput) => {
154483
- if (!isRecord4(toolInput)) return null;
154484
- const field = DETAIL_FIELDS.map((name) => toolInput[name]).find((value) => typeof value === "string");
154485
- return typeof field === "string" ? field : null;
154486
- };
154487
- var describeToolInput = (toolInput) => {
154488
- const detail = leadingField(toolInput) ?? JSON.stringify(toolInput ?? null, null, JSON_INDENT2);
154489
- return detail === "null" || detail === "{}" ? null : detail.slice(0, MAX_DETAIL_LENGTH);
154490
- };
154491
- var permissionQuestionOf = (questionId, toolName, toolInput) => ({
154492
- id: questionId,
154493
- prompt: `Allow the agent to use ${toolName}?`,
154494
- detail: describeToolInput(toolInput),
154495
- options: [
154496
- { label: ALLOW_LABEL, description: "Let the agent run it this time." },
154497
- { label: DENY_LABEL, description: "Refuse, and tell the agent why if you write an answer." }
154498
- ],
154499
- allowsMultiple: false
154500
- });
154501
-
154502
154492
  // apps/backend/src/procedures/get-review/utils/build-progress.ts
154503
- var NO_WRITING_ACTIVITY = { request: null, isAgentRunning: false, journal: [], prompt: null };
154493
+ var NO_WRITING_ACTIVITY = { request: null, isAgentOnline: false, prompt: null };
154504
154494
  var PROMPT_LABELS = { label: "Copy the line", copiedMessage: "Line copied" };
154505
- var CHAT_HINT = "Send it a message in Chat to start it again.";
154506
- var PERMISSION_LABELS = {
154507
- description: "The agent is waiting for your answer before it goes on writing the review.",
154508
- allowLabel: ALLOW_LABEL,
154509
- denyLabel: DENY_LABEL,
154510
- reasonLabel: "Tell the agent why, if you deny it"
154511
- };
154512
154495
  var UNATTENDED = {
154513
154496
  state: "waiting",
154514
154497
  label: "Still being written",
@@ -154524,37 +154507,27 @@ var WRITTEN_BY_REVIEWER_AGENT = {
154524
154507
  label: "Your agent is writing the review",
154525
154508
  message: "You can read the code meanwhile. Each part appears as it is written."
154526
154509
  };
154527
- var lastFailureReasonOf = (journal) => {
154528
- const outcomes = journal.map((entry) => entry.event).filter((event) => event.kind === "agent-failure" || event.kind === "delivery");
154529
- const last = outcomes.at(-1);
154530
- return last?.kind === "agent-failure" ? last.reason : null;
154510
+ var liveAgentStanding = (agentLabel, isAgentOnline2) => isAgentOnline2 ? {
154511
+ state: "writing",
154512
+ label: `${agentLabel} is writing the review`,
154513
+ message: "You can read the code meanwhile. Each part appears as it is written."
154514
+ } : {
154515
+ state: "waiting",
154516
+ label: `Waiting for ${agentLabel}`,
154517
+ message: `${agentLabel} writes the review once it is online. You can read the code meanwhile.`
154531
154518
  };
154532
- var stoppedStanding = (agentLabel, failureReason) => ({
154533
- state: "stopped",
154534
- label: failureReason === null ? `${agentLabel} stopped before the review was finished` : `${agentLabel} could not write the review`,
154535
- message: failureReason === null ? CHAT_HINT : `${failureReason} ${CHAT_HINT}`
154536
- });
154537
- var standingOf2 = ({ request, isAgentRunning, journal }, writtenCount) => {
154519
+ var standingOf2 = ({ request, isAgentOnline: isAgentOnline2 }, writtenCount) => {
154538
154520
  if (request === null) return UNATTENDED;
154539
154521
  if (request.writer.kind !== "agent") return writtenCount === 0 ? WAITING_FOR_REVIEWER_AGENT : WRITTEN_BY_REVIEWER_AGENT;
154540
- const agentLabel = AGENT_LABELS[request.writer.agent];
154541
- return isAgentRunning ? {
154542
- state: "writing",
154543
- label: `${agentLabel} is writing the review`,
154544
- message: "You can read the code meanwhile. Each part appears as it is written."
154545
- } : stoppedStanding(agentLabel, lastFailureReasonOf(journal));
154546
- };
154547
- var pendingPermissionOf = (journal) => {
154548
- const batch = openQuestionBatchesOf(journal).filter((open2) => open2.origin === "permission").at(-1);
154549
- const question = batch?.questions.at(0);
154550
- return batch === void 0 || question === void 0 ? null : { batchId: batch.id, questionId: question.id, title: question.prompt, detail: question.detail, ...PERMISSION_LABELS };
154522
+ return liveAgentStanding(AGENT_LABELS[request.writer.agent], isAgentOnline2);
154551
154523
  };
154552
154524
  var countLabelOf2 = (writtenCount, totalCount) => `${String(writtenCount)} of ${String(totalCount)} parts written`;
154553
- var isAgentWriting = ({ request, isAgentRunning }) => {
154525
+ var isAgentWriting = ({ request, isAgentOnline: isAgentOnline2 }) => {
154554
154526
  if (request?.writer.kind === "external") return true;
154555
- return request?.writer.kind === "agent" && isAgentRunning;
154527
+ return request?.writer.kind === "agent" && isAgentOnline2;
154556
154528
  };
154557
154529
  var isWritingNow = (artifact, activity) => artifact.status === "drafting" && isAgentWriting(activity);
154530
+ var isWaitingForLiveAgent = ({ request, isAgentOnline: isAgentOnline2 }) => request?.writer.kind === "agent" && !isAgentOnline2;
154558
154531
  var isLeftToNoAgent = (activity) => activity.request?.writer.kind === "author" || activity.request?.writer.kind === "none";
154559
154532
  var buildProgress = (artifact, activity) => {
154560
154533
  if (artifact.status !== "drafting" || isLeftToNoAgent(activity)) return null;
@@ -154563,12 +154536,11 @@ var buildProgress = (artifact, activity) => {
154563
154536
  const standing = standingOf2(activity, tally.complete);
154564
154537
  return {
154565
154538
  ...standing,
154566
- isWriting: standing.state !== "stopped",
154539
+ isWriting: !isWaitingForLiveAgent(activity),
154567
154540
  countLabel: countLabelOf2(tally.complete, totalCount),
154568
154541
  writtenCount: tally.complete,
154569
154542
  totalCount,
154570
- prompt: activity.request?.writer.kind === "external" && activity.prompt !== null ? { ...PROMPT_LABELS, text: activity.prompt } : null,
154571
- permission: activity.isAgentRunning ? pendingPermissionOf(activity.journal) : null
154543
+ prompt: activity.request?.writer.kind === "external" && activity.prompt !== null ? { ...PROMPT_LABELS, text: activity.prompt } : null
154572
154544
  };
154573
154545
  };
154574
154546
  var buildCompletionNotice = (artifact) => artifact.status === "complete" ? { title: "Flight Plan Review ready", body: `${artifact.title} is ready to read.` } : null;
@@ -155805,12 +155777,10 @@ var findPullRequestSubject = async (key, context) => {
155805
155777
  };
155806
155778
  var readWritingActivity = async (subject, key, context) => {
155807
155779
  if (subject.artifact?.status !== "drafting") return NO_WRITING_ACTIVITY;
155808
- const { store, agents } = context.conversationEnvironment;
155809
- const [request, conversation] = await Promise.all([context.reviewStore.findRequest(key), store.read(key)]);
155780
+ const request = await context.reviewStore.findRequest(key);
155810
155781
  return {
155811
155782
  request,
155812
- isAgentRunning: agents.find(key) !== null,
155813
- journal: conversation.journal,
155783
+ isAgentOnline: isAgentOnlineFor(key, context),
155814
155784
  prompt: request === null ? null : buildReviewPrompt(subject.repository, subject.target, key, request.writeUp)
155815
155785
  };
155816
155786
  };
@@ -155850,7 +155820,7 @@ var getReview = async (input2, context) => {
155850
155820
  branchLinking,
155851
155821
  pendingComments,
155852
155822
  publications,
155853
- isPublishing: isPublishingUnderway(publishingRequest, publications, context.conversationEnvironment.agents.find(key) !== null, context.now()),
155823
+ isPublishing: isPublishingUnderway(publishingRequest, publications, isAgentOnlineFor(key, context), context.now()),
155854
155824
  now: context.now(),
155855
155825
  setup,
155856
155826
  unavailableMessage: opened.unavailableMessage,
@@ -156021,6 +155991,7 @@ var recordPublication = async (input2, context) => {
156021
155991
  const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
156022
155992
  const [artifact, publications] = await Promise.all([context.reviewStore.findArtifact(key), context.reviewStore.readPublications(key)]);
156023
155993
  if (artifact === null) return { kind: "rejected", message: NO_FLIGHT_PLAN_MESSAGE };
155994
+ markAgentSeen(key, context);
156024
155995
  await context.reviewStore.writePublications(key, withPublication(publications, { kind: "claude", url: input2.url, publishedAt: context.now() }));
156025
155996
  const isSameAddress = publications.some((publication) => publication.kind === "claude" && publication.url === input2.url);
156026
155997
  if (isSameAddress) return { kind: "recorded", note: RECORDED_NOTE };
@@ -156036,7 +156007,7 @@ var recordPublicationInputShape = {
156036
156007
  var recordPublicationInputSchema = external_exports.strictObject(recordPublicationInputShape);
156037
156008
 
156038
156009
  // apps/backend/src/server/mcp/tools/record-publication/index.ts
156039
- var DESCRIPTION7 = [
156010
+ var DESCRIPTION8 = [
156040
156011
  "Records the address of the Claude artifact a Flight Plan was published to.",
156041
156012
  "Call it once, right after the Artifact tool publishes the page Flight Plan asked you to publish.",
156042
156013
  "Until it is called, the reviewer has no link to the artifact."
@@ -156044,7 +156015,7 @@ var DESCRIPTION7 = [
156044
156015
  var registerRecordPublicationTool = (mcpServer, context) => {
156045
156016
  mcpServer.registerTool(
156046
156017
  "record-publication",
156047
- { description: DESCRIPTION7, inputSchema: recordPublicationInputShape },
156018
+ { description: DESCRIPTION8, inputSchema: recordPublicationInputShape },
156048
156019
  async (input2) => {
156049
156020
  const recording = await recordPublication(input2, context);
156050
156021
  return recording.kind === "recorded" ? textResult(recording.note) : errorResult(recording.message);
@@ -156058,15 +156029,14 @@ var sendAgentMessage = async (input2, context) => {
156058
156029
  const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
156059
156030
  const target = await findReviewTarget(key, context);
156060
156031
  if (target.kind === "rejected") return target;
156061
- markAgentActivity(key, context);
156032
+ markAgentSeen(key, context);
156062
156033
  await appendAndPublish(
156063
156034
  key,
156064
156035
  [{ kind: "agent-message", id: context.conversationEnvironment.newId(), body: input2.body, sentAt: context.now() }],
156065
156036
  context
156066
156037
  );
156067
- if (isSpawnedFor(key, context)) return { kind: "sent", note: SENT_NOTE };
156068
- const command = listenCommandFor(key, context);
156069
- return { kind: "sent", note: `${SENT_NOTE} If you want the reviewer's reply: ${listenInstructionOf(command)}` };
156038
+ const command = listenCommandOf(context.conversationEnvironment.serverUrl, key);
156039
+ return { kind: "sent", note: `${SENT_NOTE} ${listenInstructionOf(command)}` };
156070
156040
  };
156071
156041
 
156072
156042
  // apps/backend/src/procedures/send-agent-message/utils/schemas.ts
@@ -156081,11 +156051,11 @@ var sendAgentMessageInputShape = {
156081
156051
  var sendAgentMessageInputSchema = external_exports.strictObject(sendAgentMessageInputShape);
156082
156052
 
156083
156053
  // apps/backend/src/server/mcp/tools/send-message/index.ts
156084
- var DESCRIPTION8 = "Send the reviewer a message in Flight Plan's web app. Use it for every answer, summary or result the reviewer should read.";
156054
+ var DESCRIPTION9 = "Send the reviewer a message in Flight Plan's web app. Use it for every answer, summary or result the reviewer should read.";
156085
156055
  var registerSendMessageTool = (mcpServer, context) => {
156086
156056
  mcpServer.registerTool(
156087
156057
  "send-message",
156088
- { description: DESCRIPTION8, inputSchema: sendAgentMessageInputShape },
156058
+ { description: DESCRIPTION9, inputSchema: sendAgentMessageInputShape },
156089
156059
  async (input2) => {
156090
156060
  const sending = await sendAgentMessage(input2, context);
156091
156061
  return sending.kind === "sent" ? { content: [{ type: "text", text: sending.note }] } : { content: [{ type: "text", text: sending.message }], isError: true };
@@ -156518,7 +156488,7 @@ var updateNodesInputShape = {
156518
156488
  var updateNodesInputSchema = external_exports.strictObject(updateNodesInputShape);
156519
156489
 
156520
156490
  // apps/backend/src/server/mcp/tools/update-nodes/index.ts
156521
- var DESCRIPTION9 = [
156491
+ var DESCRIPTION10 = [
156522
156492
  "Writes prose onto what one segment of the artifact already has, by id, in one batch.",
156523
156493
  "`segmentId` names that segment, and every id in the call is looked up inside it.",
156524
156494
  "The segment's own fields are written by name: `title` and `context`, and `traceDiagramDescription` for its Trace Diagram.",
@@ -156532,7 +156502,7 @@ var DESCRIPTION9 = [
156532
156502
  var registerUpdateNodesTool = (mcpServer, context) => {
156533
156503
  mcpServer.registerTool(
156534
156504
  "update-nodes",
156535
- { description: DESCRIPTION9, inputSchema: updateNodesInputShape },
156505
+ { description: DESCRIPTION10, inputSchema: updateNodesInputShape },
156536
156506
  async (input2) => {
156537
156507
  const writing = await updateNodes(input2, context);
156538
156508
  return writing.kind === "written" ? jsonResult({ note: writing.note, nodes: writing.nodes, unwritten: writing.unwritten }) : errorResult(writing.message);
@@ -156560,6 +156530,7 @@ var createMcpServer = (context) => {
156560
156530
  registerCompleteTool(mcpServer, context);
156561
156531
  registerRecordPublicationTool(mcpServer, context);
156562
156532
  registerRecordImportTool(mcpServer, context);
156533
+ registerPingTool(mcpServer, context);
156563
156534
  return mcpServer;
156564
156535
  };
156565
156536
  var handleMcpRequest = (context) => async (request, reply) => {
@@ -156642,46 +156613,7 @@ var parseCommand = (commandArguments) => {
156642
156613
  };
156643
156614
 
156644
156615
  // apps/backend/src/procedures/context.ts
156645
- var import_node_crypto4 = require("node:crypto");
156646
-
156647
- // apps/backend/src/procedures/get-conversation/utils/agent-registry.ts
156648
- var shouldRedeliverAfter = (closing) => closing === "turn-ended" || closing === "idle";
156649
- var NO_LISTENER = { openCount: 0, lastClosedAt: null };
156650
- var InMemoryAgentRegistry = class {
156651
- agents = /* @__PURE__ */ new Map();
156652
- listeners = /* @__PURE__ */ new Map();
156653
- find = (key) => this.agents.get(reviewKeyId(key)) ?? null;
156654
- findBySessionId = (sessionId) => [...this.agents.values()].find((agent) => agent.sessionId === sessionId) ?? null;
156655
- claim = (agent) => {
156656
- if (this.agents.has(reviewKeyId(agent.key))) return false;
156657
- this.agents = new Map([...this.agents, [reviewKeyId(agent.key), agent]]);
156658
- return true;
156659
- };
156660
- update = (key, change) => {
156661
- const current = this.find(key);
156662
- if (current === null) return null;
156663
- const changed = change(current);
156664
- this.agents = new Map([...this.agents, [reviewKeyId(key), changed]]);
156665
- return changed;
156666
- };
156667
- release = (key, sessionId) => {
156668
- const current = this.find(key);
156669
- if (current?.sessionId !== sessionId) return null;
156670
- this.agents = new Map([...this.agents].filter(([id]) => id !== reviewKeyId(key)));
156671
- return current;
156672
- };
156673
- list = () => [...this.agents.values()];
156674
- openListener = (key) => {
156675
- const presence = this.listenerPresence(key);
156676
- this.listeners = new Map([...this.listeners, [reviewKeyId(key), { ...presence, openCount: presence.openCount + 1 }]]);
156677
- };
156678
- closeListener = (key, closedAt) => {
156679
- const presence = this.listenerPresence(key);
156680
- const openCount = Math.max(presence.openCount - 1, 0);
156681
- this.listeners = new Map([...this.listeners, [reviewKeyId(key), { openCount, lastClosedAt: closedAt }]]);
156682
- };
156683
- listenerPresence = (key) => this.listeners.get(reviewKeyId(key)) ?? NO_LISTENER;
156684
- };
156616
+ var import_node_crypto5 = require("node:crypto");
156685
156617
 
156686
156618
  // apps/backend/src/procedures/get-conversation/utils/conversation-store.ts
156687
156619
  var InMemoryConversationStore = class {
@@ -156702,58 +156634,92 @@ var InMemoryConversationStore = class {
156702
156634
  );
156703
156635
  };
156704
156636
 
156705
- // apps/backend/src/procedures/close-agent/index.ts
156706
- var closeAgent = (key, closing, context) => {
156707
- const { agents, hub } = context.conversationEnvironment;
156708
- const closed = agents.update(key, (agent) => ({ ...agent, closing: agent.closing ?? closing }));
156709
- if (closed === null) return false;
156710
- if (closed.process === null) agents.release(key, closed.sessionId);
156711
- else closed.process.stop();
156712
- hub.publish(key);
156713
- return true;
156637
+ // apps/backend/src/procedures/get-conversation/utils/conversation-hub.ts
156638
+ var NO_TIME_LEFT_MS = 0;
156639
+ var nextChangeOf = async (subscription, options) => {
156640
+ const timeoutMs = options.remainingMs === null ? null : Math.max(options.remainingMs(), NO_TIME_LEFT_MS);
156641
+ if (timeoutMs === NO_TIME_LEFT_MS) return "timed-out";
156642
+ return await subscription.nextChange({ timeoutMs, signal: options.signal });
156714
156643
  };
156644
+ var readsOnChange = (subscription, options, read2) => ({
156645
+ [Symbol.asyncIterator]: () => ({
156646
+ next: async () => {
156647
+ const wait = await nextChangeOf(subscription, options);
156648
+ return wait === "changed" ? { value: await read2(), done: false } : { value: void 0, done: true };
156649
+ }
156650
+ })
156651
+ });
156715
156652
 
156716
- // apps/backend/src/procedures/end-agent-turn/utils/plan-turn-ending.ts
156717
- var unsentReplyOf = (facts, hasWritten) => {
156718
- const text2 = facts.lastAssistantMessage?.trim() ?? "";
156719
- return text2 === "" || hasWritten ? null : text2;
156720
- };
156721
- var planTurnEnding = (facts) => {
156722
- const pending = pendingInboundOf(facts.journal);
156723
- const hasWritten = hasOutboundAfter(facts.journal, lastDeliveredSequenceOf(facts.journal));
156724
- if (pending.length > 0) return { kind: "deliver", pending, unsentReply: unsentReplyOf(facts, hasWritten) };
156725
- if (!hasWritten && !facts.wasReminded) return { kind: "remind" };
156726
- return { kind: "close", unsentReply: unsentReplyOf(facts, hasWritten) };
156727
- };
156728
-
156729
- // apps/backend/src/procedures/end-agent-turn/index.ts
156730
- var replyEvents = (unsentReply, id, now) => unsentReply === null ? [] : [{ kind: "agent-message", id, body: unsentReply, sentAt: now }];
156731
- var endAgentTurn = async (input2, context) => {
156732
- const { agents, store, newId } = context.conversationEnvironment;
156733
- const agent = await confirmAgentStart(input2.sessionId, context);
156734
- if (agent === null) return { kind: "unknown-session" };
156735
- const now = context.now();
156736
- const { journal } = await store.read(agent.key);
156737
- const plan4 = planTurnEnding({ journal, wasReminded: agent.wasReminded, lastAssistantMessage: input2.lastAssistantMessage });
156738
- if (plan4.kind === "remind") {
156739
- agents.update(agent.key, (current) => ({ ...current, wasReminded: true }));
156740
- return { kind: "continue", reason: REPLY_REMINDER };
156741
- }
156742
- if (plan4.kind === "deliver") {
156743
- agents.update(agent.key, (current) => ({ ...current, wasReminded: false }));
156744
- const delivery = {
156745
- kind: "delivery",
156746
- id: newId(),
156747
- throughSequence: lastSequenceOf(plan4.pending),
156748
- channel: "turn-end",
156749
- deliveredAt: now
156750
- };
156751
- await appendAndPublish(agent.key, [...replyEvents(plan4.unsentReply, newId(), now), delivery], context);
156752
- return { kind: "continue", reason: formatInbound(plan4.pending, journal) };
156653
+ // apps/backend/src/procedures/listen-for-reviewer/index.ts
156654
+ var LISTEN_WINDOW_MS = 54e4;
156655
+ var claimPacket = async (key, context) => {
156656
+ const { store, presence, serverUrl } = context.conversationEnvironment;
156657
+ const { journal } = await store.read(key);
156658
+ const claimed = Math.max(lastDeliveredSequenceOf(journal), presence.presenceOf(key).claimedThroughSequence);
156659
+ const pending = inboundAfter(journal, claimed);
156660
+ if (pending.length === 0) return null;
156661
+ const throughSequence = lastSequenceOf(pending);
156662
+ if (!presence.claimThrough(key, throughSequence)) return null;
156663
+ return {
156664
+ kind: "work",
156665
+ throughSequence,
156666
+ text: formatListenPacket(formatInbound(pending, journal), listenCommandOf(serverUrl, key))
156667
+ };
156668
+ };
156669
+ var recordDelivery = async (key, packet, context) => {
156670
+ await appendAndPublish(
156671
+ key,
156672
+ [
156673
+ {
156674
+ kind: "delivery",
156675
+ id: context.conversationEnvironment.newId(),
156676
+ throughSequence: packet.throughSequence,
156677
+ channel: "listener",
156678
+ deliveredAt: context.now()
156679
+ }
156680
+ ],
156681
+ context
156682
+ );
156683
+ };
156684
+ var waitForPacket = async (key, subscription, context, signal) => {
156685
+ const waiting = await claimPacket(key, context);
156686
+ if (waiting !== null) return waiting;
156687
+ const deadlineMs = context.now().getTime() + LISTEN_WINDOW_MS;
156688
+ const remainingMs = () => deadlineMs - context.now().getTime();
156689
+ const packets = readsOnChange(subscription, { signal, remainingMs }, async () => await claimPacket(key, context));
156690
+ for await (const packet of packets) {
156691
+ if (packet !== null) return packet;
156753
156692
  }
156754
- agents.update(agent.key, (current) => ({ ...current, closing: current.closing ?? "turn-ended" }));
156755
- await appendAndPublish(agent.key, replyEvents(plan4.unsentReply, newId(), now), context);
156756
- return { kind: "close", key: agent.key };
156693
+ return null;
156694
+ };
156695
+ var idlePacketOf = (key, context) => ({
156696
+ kind: "idle",
156697
+ text: formatIdlePacket(listenCommandOf(context.conversationEnvironment.serverUrl, key))
156698
+ });
156699
+ var packetsOf = async function* (key, context, signal) {
156700
+ const { presence, hub } = context.conversationEnvironment;
156701
+ const subscription = hub.subscribe(key);
156702
+ presence.openStream(key);
156703
+ hub.publish(key);
156704
+ try {
156705
+ const packet = await waitForPacket(key, subscription, context, signal);
156706
+ if (packet !== null) {
156707
+ yield packet;
156708
+ await recordDelivery(key, packet, context);
156709
+ } else if (signal?.aborted !== true) {
156710
+ yield idlePacketOf(key, context);
156711
+ }
156712
+ } finally {
156713
+ subscription.close();
156714
+ presence.closeStream(key, context.now());
156715
+ hub.publish(key);
156716
+ }
156717
+ };
156718
+ var listenForReviewer = async (input2, context, signal) => {
156719
+ const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
156720
+ const target = await findReviewTarget(key, context);
156721
+ if (target.kind === "rejected") return target;
156722
+ return { kind: "listening", packets: packetsOf(key, context, signal) };
156757
156723
  };
156758
156724
 
156759
156725
  // apps/backend/src/procedures/get-instructions/utils/instruction-texts.ts
@@ -158423,6 +158389,7 @@ var createMockSetupEnvironment = () => ({
158423
158389
  hasAgentCli: async (agent) => await Promise.resolve(agent !== AGENT_WITHOUT_CLI),
158424
158390
  listAgentModels: async (agent) => await Promise.resolve(MOCK_AGENT_MODELS[agent] ?? []),
158425
158391
  installAgent: async () => await Promise.resolve({ kind: "installed" }),
158392
+ isAgentInstallCurrent: async () => await Promise.resolve(true),
158426
158393
  probeForgeClis: async () => await Promise.resolve([
158427
158394
  { cli: "gh", state: { kind: "signed-in", account: "dev-harper" } },
158428
158395
  { cli: "glab", state: { kind: "missing" } }
@@ -158693,25 +158660,6 @@ var writeSamplePublishedPage = async (key, texts) => await Promise.resolve({
158693
158660
  ]
158694
158661
  });
158695
158662
 
158696
- // apps/backend/src/procedures/request-write-up/utils/write-up-queue.ts
158697
- var isSameTarget = (first, second) => JSON.stringify(first) === JSON.stringify(second);
158698
- var withQueuedTarget = (queued, target) => queued.some((listed) => isSameTarget(listed, target)) ? queued : [...queued, target];
158699
- var InMemoryWriteUpQueue = class {
158700
- targetsByKey = /* @__PURE__ */ new Map();
158701
- add = (key, target) => {
158702
- const queued = this.targetsByKey.get(reviewKeyId(key)) ?? [];
158703
- this.targetsByKey = new Map([...this.targetsByKey, [reviewKeyId(key), withQueuedTarget(queued, target)]]);
158704
- };
158705
- take = (key) => {
158706
- const queued = this.targetsByKey.get(reviewKeyId(key)) ?? [];
158707
- this.targetsByKey = new Map([...this.targetsByKey].filter(([id]) => id !== reviewKeyId(key)));
158708
- return queued;
158709
- };
158710
- };
158711
-
158712
- // apps/backend/src/procedures/save-setup-step/utils/claude-plugin.ts
158713
- var import_node_path4 = require("node:path");
158714
-
158715
158663
  // apps/backend/src/procedures/get-setup/utils/agent-folders.ts
158716
158664
  var import_node_path3 = require("node:path");
158717
158665
  var CLAUDE_FOLDER_NAME = ".claude";
@@ -158730,83 +158678,15 @@ var AGENT_ENTRY_NAMES = {
158730
158678
  };
158731
158679
  var isAgentFolder = (agent, entryNames) => AGENT_ENTRY_NAMES[agent].some((entryName) => entryNames.includes(entryName));
158732
158680
 
158733
- // apps/backend/src/procedures/save-setup-step/utils/flightplan-server.ts
158734
- var MCP_SERVER_NAME = "flightplan";
158735
-
158736
- // apps/backend/src/procedures/save-setup-step/utils/claude-plugin.ts
158737
- var MARKETPLACE_FOLDER_NAME = "agent-plugin";
158738
- var MARKETPLACE_NAME = "flightplan";
158739
- var PLUGIN_NAME = "flightplan";
158740
- var MARKETPLACE_MANIFEST_PATH = [".claude-plugin", "marketplace.json"];
158741
- var MCP_MANIFEST_NAME = ".mcp.json";
158742
- var MARKETPLACE_OWNER = "Flight Plan";
158743
- var PLUGIN_DESCRIPTION = "Write and maintain pull request walkthroughs with Flight Plan.";
158744
- var MCP_TRANSPORT = "http";
158745
- var buildMarketplaceManifest = () => ({
158746
- name: MARKETPLACE_NAME,
158747
- owner: { name: MARKETPLACE_OWNER },
158748
- plugins: [{ name: PLUGIN_NAME, source: `./${PLUGIN_NAME}`, description: PLUGIN_DESCRIPTION }]
158749
- });
158750
- var buildMcpManifest = (mcpUrl) => ({
158751
- mcpServers: { [MCP_SERVER_NAME]: { type: MCP_TRANSPORT, url: mcpUrl } }
158752
- });
158753
- var claudeInstallCommands = (marketplacePath) => [
158754
- ["plugin", "marketplace", "add", marketplacePath],
158755
- ["plugin", "install", `${PLUGIN_NAME}@${MARKETPLACE_NAME}`]
158756
- ];
158757
- var usesOwnFolder = (installPath, homePath, environment) => agentFolders(homePath, environment).some((folder) => folder.agent === "claude" && folder.path === installPath);
158758
- var claudeCommandEnvironment = (installPath, homePath, environment) => usesOwnFolder(installPath, homePath, environment) ? environment : { ...environment, CLAUDE_CONFIG_DIR: installPath };
158759
- var SPAWN_PLUGIN_NAME = "flightplan-spawn";
158760
- var PLUGIN_MANIFEST_PATH = [".claude-plugin", "plugin.json"];
158761
- var HOOKS_MANIFEST_PATH = ["hooks", "hooks.json"];
158762
- var AGENT_HOOK_PATHS = {
158763
- promptSubmit: "/api/agent-hooks/user-prompt-submit",
158764
- postToolUse: "/api/agent-hooks/post-tool-use",
158765
- permissionRequest: "/api/agent-hooks/permission-request",
158766
- stop: "/api/agent-hooks/stop"
158767
- };
158768
- var SPAWN_PLUGIN_DESCRIPTION = "Connects a Claude session that Flight Plan started to Flight Plan's web app.";
158769
- var HOOK_TYPE = "http";
158770
- var ALL_TOOLS_MATCHER = "*";
158771
- var QUICK_HOOK_TIMEOUT_SECONDS = 10;
158772
- var STOP_HOOK_TIMEOUT_SECONDS = 30;
158773
- var PERMISSION_HOOK_TIMEOUT_SECONDS = 600;
158774
- var spawnPluginPathOf = (dataPath) => (0, import_node_path4.join)(dataPath, MARKETPLACE_FOLDER_NAME, SPAWN_PLUGIN_NAME);
158775
- var buildSpawnPluginManifest = () => ({
158776
- name: SPAWN_PLUGIN_NAME,
158777
- description: SPAWN_PLUGIN_DESCRIPTION
158778
- });
158779
- var httpHook = (serverUrl, path11, timeout) => ({
158780
- type: HOOK_TYPE,
158781
- url: `${serverUrl}${path11}`,
158782
- timeout
158783
- });
158784
- var buildSpawnHooksManifest = (serverUrl) => ({
158785
- description: SPAWN_PLUGIN_DESCRIPTION,
158786
- hooks: {
158787
- UserPromptSubmit: [{ hooks: [httpHook(serverUrl, AGENT_HOOK_PATHS.promptSubmit, QUICK_HOOK_TIMEOUT_SECONDS)] }],
158788
- PostToolUse: [
158789
- { matcher: ALL_TOOLS_MATCHER, hooks: [httpHook(serverUrl, AGENT_HOOK_PATHS.postToolUse, QUICK_HOOK_TIMEOUT_SECONDS)] }
158790
- ],
158791
- PermissionRequest: [
158792
- {
158793
- matcher: ALL_TOOLS_MATCHER,
158794
- hooks: [httpHook(serverUrl, AGENT_HOOK_PATHS.permissionRequest, PERMISSION_HOOK_TIMEOUT_SECONDS)]
158795
- }
158796
- ],
158797
- Stop: [{ hooks: [httpHook(serverUrl, AGENT_HOOK_PATHS.stop, STOP_HOOK_TIMEOUT_SECONDS)] }]
158798
- }
158799
- });
158800
-
158801
158681
  // apps/backend/src/utils/executable-finder/index.ts
158802
158682
  var import_node_fs3 = require("node:fs");
158803
158683
  var import_promises = require("node:fs/promises");
158804
158684
 
158805
158685
  // apps/backend/src/procedures/save-setup-step/utils/executable-candidates.ts
158806
- var import_node_path5 = require("node:path");
158686
+ var import_node_path4 = require("node:path");
158807
158687
  var USER_PROGRAMS_FOLDER = [".local", "bin"];
158808
- var searchFolders = (searchPath) => (searchPath ?? "").split(import_node_path5.delimiter).filter((folder) => folder !== "");
158809
- var executableCandidates = (name, searchPath, homePath) => [...searchFolders(searchPath), (0, import_node_path5.join)(homePath, ...USER_PROGRAMS_FOLDER)].map((folder) => (0, import_node_path5.join)(folder, name));
158688
+ var searchFolders = (searchPath) => (searchPath ?? "").split(import_node_path4.delimiter).filter((folder) => folder !== "");
158689
+ var executableCandidates = (name, searchPath, homePath) => [...searchFolders(searchPath), (0, import_node_path4.join)(homePath, ...USER_PROGRAMS_FOLDER)].map((folder) => (0, import_node_path4.join)(folder, name));
158810
158690
 
158811
158691
  // apps/backend/src/utils/executable-finder/index.ts
158812
158692
  var isExecutable = async (path11) => {
@@ -158829,7 +158709,7 @@ var createAgentCliProbe = (options) => {
158829
158709
 
158830
158710
  // apps/backend/src/utils/folder-lister/index.ts
158831
158711
  var import_promises2 = require("node:fs/promises");
158832
- var import_node_path6 = require("node:path");
158712
+ var import_node_path5 = require("node:path");
158833
158713
  var GIT_FOLDER_NAME = ".git";
158834
158714
  var exists = async (path11) => {
158835
158715
  try {
@@ -158861,14 +158741,14 @@ var readFolderNames = async (path11) => {
158861
158741
  return null;
158862
158742
  }
158863
158743
  };
158864
- var parentPathOf2 = (path11) => (0, import_node_path6.dirname)(path11) === path11 ? null : (0, import_node_path6.dirname)(path11);
158865
- var isRepositoryFolder = async (path11) => await exists((0, import_node_path6.join)(path11, GIT_FOLDER_NAME));
158744
+ var parentPathOf2 = (path11) => (0, import_node_path5.dirname)(path11) === path11 ? null : (0, import_node_path5.dirname)(path11);
158745
+ var isRepositoryFolder = async (path11) => await exists((0, import_node_path5.join)(path11, GIT_FOLDER_NAME));
158866
158746
  var toFolderEntry2 = async (parentPath, name) => {
158867
- const path11 = (0, import_node_path6.join)(parentPath, name);
158747
+ const path11 = (0, import_node_path5.join)(parentPath, name);
158868
158748
  return { name, path: path11, isRepository: await isRepositoryFolder(path11) };
158869
158749
  };
158870
158750
  var createFolderLister = (homePath) => async (requestedPath) => {
158871
- const path11 = (0, import_node_path6.resolve)(requestedPath ?? homePath);
158751
+ const path11 = (0, import_node_path5.resolve)(requestedPath ?? homePath);
158872
158752
  const names = await readFolderNames(path11);
158873
158753
  if (names === null) return null;
158874
158754
  const folders = await Promise.all(names.map(async (name) => await toFolderEntry2(path11, name)));
@@ -158889,6 +158769,41 @@ var createAgentDetector = (options) => {
158889
158769
  var import_promises4 = require("node:fs/promises");
158890
158770
  var import_node_path8 = require("node:path");
158891
158771
 
158772
+ // apps/backend/src/procedures/refresh-agent-installs/utils/install-fingerprint.ts
158773
+ var import_node_crypto4 = require("node:crypto");
158774
+ var HASH_ALGORITHM = "sha256";
158775
+ var FIELD_SEPARATOR2 = "\0";
158776
+ var byPath2 = (first, second) => first.path.localeCompare(second.path);
158777
+ var installFingerprintOf = (files, mcpUrl) => [...files].sort(byPath2).reduce((hash2, file2) => hash2.update(file2.path).update(FIELD_SEPARATOR2).update(file2.content).update(FIELD_SEPARATOR2), (0, import_node_crypto4.createHash)(HASH_ALGORITHM).update(mcpUrl)).digest("hex");
158778
+ var isInstallCurrent = (install, stamp, fingerprint) => stamp?.installPath === install.installPath && stamp.fingerprint === fingerprint;
158779
+
158780
+ // apps/backend/src/procedures/save-setup-step/utils/flightplan-server.ts
158781
+ var MCP_SERVER_NAME = "flightplan";
158782
+
158783
+ // apps/backend/src/procedures/save-setup-step/utils/claude-plugin.ts
158784
+ var MARKETPLACE_FOLDER_NAME = "agent-plugin";
158785
+ var MARKETPLACE_NAME = "flightplan";
158786
+ var PLUGIN_NAME = "flightplan";
158787
+ var MARKETPLACE_MANIFEST_PATH = [".claude-plugin", "marketplace.json"];
158788
+ var MCP_MANIFEST_NAME = ".mcp.json";
158789
+ var MARKETPLACE_OWNER = "Flight Plan";
158790
+ var PLUGIN_DESCRIPTION = "Write and maintain pull request walkthroughs with Flight Plan.";
158791
+ var MCP_TRANSPORT = "http";
158792
+ var buildMarketplaceManifest = () => ({
158793
+ name: MARKETPLACE_NAME,
158794
+ owner: { name: MARKETPLACE_OWNER },
158795
+ plugins: [{ name: PLUGIN_NAME, source: `./${PLUGIN_NAME}`, description: PLUGIN_DESCRIPTION }]
158796
+ });
158797
+ var buildMcpManifest = (mcpUrl) => ({
158798
+ mcpServers: { [MCP_SERVER_NAME]: { type: MCP_TRANSPORT, url: mcpUrl } }
158799
+ });
158800
+ var claudeInstallCommands = (marketplacePath) => [
158801
+ ["plugin", "marketplace", "add", marketplacePath],
158802
+ ["plugin", "install", `${PLUGIN_NAME}@${MARKETPLACE_NAME}`]
158803
+ ];
158804
+ var usesOwnFolder = (installPath, homePath, environment) => agentFolders(homePath, environment).some((folder) => folder.agent === "claude" && folder.path === installPath);
158805
+ var claudeCommandEnvironment = (installPath, homePath, environment) => usesOwnFolder(installPath, homePath, environment) ? environment : { ...environment, CLAUDE_CONFIG_DIR: installPath };
158806
+
158892
158807
  // apps/backend/src/procedures/save-setup-step/utils/codex-config.ts
158893
158808
  var SERVER_TABLE = new RegExp(String.raw`^\[mcp_servers\.${MCP_SERVER_NAME}\][^\n]*\n?(?:(?!\[)[^\n]*\n?)*`, "mu");
158894
158809
  var serverTable = (mcpUrl) => `[mcp_servers.${MCP_SERVER_NAME}]
@@ -158912,7 +158827,7 @@ ${serverTable(mcpUrl)}`;
158912
158827
 
158913
158828
  // apps/backend/src/procedures/save-setup-step/utils/cursor-config.ts
158914
158829
  var EMPTY_CONFIG = "{}";
158915
- var JSON_INDENT3 = 2;
158830
+ var JSON_INDENT2 = 2;
158916
158831
  var isJsonObject = (parsed) => typeof parsed === "object" && parsed !== null && !Array.isArray(parsed);
158917
158832
  var parseJsonObject = (text2) => {
158918
158833
  try {
@@ -158927,7 +158842,7 @@ var withCursorMcpServer = (config3, mcpUrl) => {
158927
158842
  const parsed = parseJsonObject(config3 === null || config3.trim() === "" ? EMPTY_CONFIG : config3);
158928
158843
  if (parsed === null) return null;
158929
158844
  const merged = { ...parsed, mcpServers: { ...serversOf(parsed), [MCP_SERVER_NAME]: { url: mcpUrl } } };
158930
- return `${JSON.stringify(merged, null, JSON_INDENT3)}
158845
+ return `${JSON.stringify(merged, null, JSON_INDENT2)}
158931
158846
  `;
158932
158847
  };
158933
158848
 
@@ -158935,9 +158850,12 @@ var withCursorMcpServer = (config3, mcpUrl) => {
158935
158850
  var NAME_LINE = /^name:.*$/mu;
158936
158851
  var renameSkill = (skill, name) => skill.replace(NAME_LINE, `name: ${name}`);
158937
158852
 
158853
+ // apps/backend/src/utils/install-stamp-file/index.ts
158854
+ var import_node_path7 = require("node:path");
158855
+
158938
158856
  // apps/backend/src/utils/text-file/index.ts
158939
158857
  var import_promises3 = require("node:fs/promises");
158940
- var import_node_path7 = require("node:path");
158858
+ var import_node_path6 = require("node:path");
158941
158859
  var MISSING_FILE_CODE = "ENOENT";
158942
158860
  var DRAFT_SUFFIX = ".draft";
158943
158861
  var isMissingFile = (error61) => error61 instanceof Error && "code" in error61 && error61.code === MISSING_FILE_CODE;
@@ -158951,7 +158869,7 @@ var readTextOrNull = async (path11) => {
158951
158869
  };
158952
158870
  var writeText = async (path11, text2) => {
158953
158871
  const draftPath = `${path11}${DRAFT_SUFFIX}`;
158954
- await (0, import_promises3.mkdir)((0, import_node_path7.dirname)(path11), { recursive: true });
158872
+ await (0, import_promises3.mkdir)((0, import_node_path6.dirname)(path11), { recursive: true });
158955
158873
  await (0, import_promises3.writeFile)(draftPath, text2);
158956
158874
  await (0, import_promises3.rename)(draftPath, path11);
158957
158875
  };
@@ -158959,6 +158877,30 @@ var removeFile = async (path11) => {
158959
158877
  await (0, import_promises3.rm)(path11, { force: true });
158960
158878
  };
158961
158879
 
158880
+ // apps/backend/src/utils/install-stamp-file/index.ts
158881
+ var FOLDER_NAME = "agent-installs";
158882
+ var JSON_INDENT3 = 2;
158883
+ var installStampSchema = external_exports.object({ installPath: external_exports.string(), fingerprint: external_exports.string() });
158884
+ var parseInstallStamp = (text2) => {
158885
+ if (text2 === null) return null;
158886
+ try {
158887
+ const parsed = installStampSchema.safeParse(JSON.parse(text2));
158888
+ return parsed.success ? parsed.data : null;
158889
+ } catch {
158890
+ return null;
158891
+ }
158892
+ };
158893
+ var createInstallStampFile = (dataPath) => {
158894
+ const pathOf2 = (agent) => (0, import_node_path7.join)(dataPath, FOLDER_NAME, `${agent}.json`);
158895
+ return {
158896
+ read: async (agent) => parseInstallStamp(await readTextOrNull(pathOf2(agent))),
158897
+ write: async (agent, stamp) => {
158898
+ await writeText(pathOf2(agent), `${JSON.stringify(stamp, null, JSON_INDENT3)}
158899
+ `);
158900
+ }
158901
+ };
158902
+ };
158903
+
158962
158904
  // apps/backend/src/utils/agent-installer/index.ts
158963
158905
  var SKILL_SOURCE_PATH = ["skills", "review"];
158964
158906
  var SKILL_NAME = "flightplan-review";
@@ -159001,12 +158943,6 @@ var writeMarketplace = async (marketplacePath, options) => {
159001
158943
  await writeText((0, import_node_path8.join)(pluginPath, MCP_MANIFEST_NAME), formatJson(buildMcpManifest(options.mcpUrl)));
159002
158944
  await writeText((0, import_node_path8.join)(marketplacePath, ...MARKETPLACE_MANIFEST_PATH), formatJson(buildMarketplaceManifest()));
159003
158945
  };
159004
- var writeSpawnPlugin = async (options) => {
159005
- const spawnPluginPath = spawnPluginPathOf(options.dataPath);
159006
- await (0, import_promises4.rm)(spawnPluginPath, { recursive: true, force: true });
159007
- await writeText((0, import_node_path8.join)(spawnPluginPath, ...PLUGIN_MANIFEST_PATH), formatJson(buildSpawnPluginManifest()));
159008
- await writeText((0, import_node_path8.join)(spawnPluginPath, ...HOOKS_MANIFEST_PATH), formatJson(buildSpawnHooksManifest(options.serverUrl)));
159009
- };
159010
158946
  var runUntilFailure = async (run4, commands) => {
159011
158947
  const [command, ...laterCommands] = commands;
159012
158948
  if (commands.length === 0) return INSTALLED;
@@ -159018,7 +158954,6 @@ var installClaudePlugin = async (install, options) => {
159018
158954
  const executable = await createExecutableFinder(options)(AGENT_CLI_NAMES.claude);
159019
158955
  if (executable === null) return failed(CLAUDE_MISSING_REASON);
159020
158956
  await writeMarketplace(marketplacePath, options);
159021
- await writeSpawnPlugin(options);
159022
158957
  const environment = claudeCommandEnvironment(install.installPath, options.homePath, options.environment);
159023
158958
  const run4 = async (commandArguments) => {
159024
158959
  const outcome = await options.runCommand(executable, commandArguments, environment);
@@ -159046,72 +158981,49 @@ var INSTALLERS = {
159046
158981
  codex: installFiles(CODEX_CONFIG),
159047
158982
  cursor: installFiles(CURSOR_CONFIG)
159048
158983
  };
159049
- var createAgentInstaller = (options) => async (install) => {
158984
+ var listFilePaths = async (folderPath) => {
158985
+ const entries = await (0, import_promises4.readdir)(folderPath, { withFileTypes: true });
158986
+ const nested = await Promise.all(
158987
+ entries.map(async (entry) => {
158988
+ const entryPath = (0, import_node_path8.join)(folderPath, entry.name);
158989
+ return entry.isDirectory() ? await listFilePaths(entryPath) : [entryPath];
158990
+ })
158991
+ );
158992
+ return nested.flat();
158993
+ };
158994
+ var readPluginFiles = async (pluginPath) => {
158995
+ const paths = await listFilePaths(pluginPath);
158996
+ return await Promise.all(paths.map(async (path11) => ({ path: (0, import_node_path8.relative)(pluginPath, path11), content: await (0, import_promises4.readFile)(path11, "utf8") })));
158997
+ };
158998
+ var fingerprintOf2 = async (options) => installFingerprintOf(await readPluginFiles(options.pluginPath), options.mcpUrl);
158999
+ var installAndStamp = async (agentInstall, options) => {
159000
+ const installation = await INSTALLERS[agentInstall.agent](agentInstall, options);
159001
+ if (installation.kind === "installed") {
159002
+ await createInstallStampFile(options.dataPath).write(agentInstall.agent, {
159003
+ installPath: agentInstall.installPath,
159004
+ fingerprint: await fingerprintOf2(options)
159005
+ });
159006
+ }
159007
+ return installation;
159008
+ };
159009
+ var createAgentInstaller = (options) => async (agentInstall) => {
159050
159010
  try {
159051
- return await INSTALLERS[install.agent](install, options);
159011
+ return await installAndStamp(agentInstall, options);
159052
159012
  } catch {
159053
159013
  return failed(UNWRITABLE_REASON);
159054
159014
  }
159055
159015
  };
159056
-
159057
- // apps/backend/src/utils/agent-model-lister/index.ts
159058
- var import_node_child_process4 = require("node:child_process");
159059
-
159060
- // apps/backend/src/procedures/deliver-to-agent/utils/claude-launch.ts
159061
- var FLIGHT_PLAN_TOOLS_RULE = `mcp__plugin_${PLUGIN_NAME}_${MCP_SERVER_NAME}__*`;
159062
- var PROJECT_MCP_FILE_NAME = ".mcp.json";
159063
- var isJsonObject2 = (parsed) => typeof parsed === "object" && parsed !== null && !Array.isArray(parsed);
159064
- var parseJsonObject2 = (text2) => {
159016
+ var createInstallChecker = (options) => async (agentInstall) => {
159065
159017
  try {
159066
- const parsed = JSON.parse(text2);
159067
- return isJsonObject2(parsed) ? parsed : null;
159018
+ const [stamp, fingerprint] = await Promise.all([createInstallStampFile(options.dataPath).read(agentInstall.agent), fingerprintOf2(options)]);
159019
+ return isInstallCurrent(agentInstall, stamp, fingerprint);
159068
159020
  } catch {
159069
- return null;
159021
+ return false;
159070
159022
  }
159071
159023
  };
159072
- var projectMcpServerNames = (projectMcpFile) => {
159073
- const config3 = projectMcpFile === null ? null : parseJsonObject2(projectMcpFile);
159074
- return config3 !== null && isJsonObject2(config3.mcpServers) ? Object.keys(config3.mcpServers) : [];
159075
- };
159076
- var disabledProjectMcpArguments = (projectMcpServers) => projectMcpServers.length === 0 ? [] : ["--settings", JSON.stringify({ disabledMcpjsonServers: projectMcpServers })];
159077
- var claudeLaunchArguments = (launch2, spawnPluginPath, projectMcpServers) => [
159078
- ...launch2.isResume ? ["--resume", launch2.sessionId] : ["--session-id", launch2.sessionId],
159079
- "--plugin-dir",
159080
- spawnPluginPath,
159081
- ...launch2.model === DEFAULT_MODEL ? [] : ["--model", launch2.model],
159082
- "--allowedTools",
159083
- FLIGHT_PLAN_TOOLS_RULE,
159084
- ...disabledProjectMcpArguments(projectMcpServers),
159085
- "--append-system-prompt",
159086
- launch2.instructions,
159087
- launch2.prompt
159088
- ];
159089
- var PARENT_SESSION_VARIABLES = /* @__PURE__ */ new Set([
159090
- "CLAUDECODE",
159091
- "CLAUDE_AGENT_SDK_VERSION",
159092
- "CLAUDE_CODE_CHILD_SESSION",
159093
- "CLAUDE_CODE_EMIT_STARTUP_TIMING",
159094
- "CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING",
159095
- "CLAUDE_CODE_ENABLE_TASKS",
159096
- "CLAUDE_CODE_ENTRYPOINT",
159097
- "CLAUDE_CODE_EXECPATH",
159098
- "CLAUDE_CODE_MESSAGING_SOCKET",
159099
- "CLAUDE_CODE_MESSAGING_TOKEN",
159100
- "CLAUDE_CODE_QUESTION_PREVIEW_FORMAT",
159101
- "CLAUDE_CODE_SESSION_ATTENDED",
159102
- "CLAUDE_CODE_SESSION_ID",
159103
- "CLAUDE_EFFORT",
159104
- "CLAUDE_PID",
159105
- "MCP_CONNECTION_NONBLOCKING"
159106
- ]);
159107
- var FORCE_SESSION_PERSISTENCE = { CLAUDE_CODE_FORCE_SESSION_PERSISTENCE: "1" };
159108
- var EFFORT_VARIABLE = "CLAUDE_CODE_EFFORT_LEVEL";
159109
- var withoutParentSession = (environment) => Object.fromEntries(Object.entries(environment).filter(([name]) => !PARENT_SESSION_VARIABLES.has(name)));
159110
- var spawnEnvironmentOf = (environment, effort) => ({
159111
- ...withoutParentSession(environment),
159112
- ...FORCE_SESSION_PERSISTENCE,
159113
- ...effort === DEFAULT_EFFORT ? {} : { [EFFORT_VARIABLE]: effort }
159114
- });
159024
+
159025
+ // apps/backend/src/utils/agent-model-lister/index.ts
159026
+ var import_node_child_process4 = require("node:child_process");
159115
159027
 
159116
159028
  // apps/backend/src/procedures/get-setup/utils/claude-model-query.ts
159117
159029
  var CLAUDE_MODEL_QUERY_ARGUMENTS = [
@@ -159158,6 +159070,27 @@ var toListedModel = (reported) => ({
159158
159070
  });
159159
159071
  var parseClaudeModels = (output2) => output2.split(LINE_BREAK20).flatMap(reportedModelsOf).map(toListedModel);
159160
159072
 
159073
+ // apps/backend/src/utils/agent-model-lister/parent-session.ts
159074
+ var PARENT_SESSION_VARIABLES = /* @__PURE__ */ new Set([
159075
+ "CLAUDECODE",
159076
+ "CLAUDE_AGENT_SDK_VERSION",
159077
+ "CLAUDE_CODE_CHILD_SESSION",
159078
+ "CLAUDE_CODE_EMIT_STARTUP_TIMING",
159079
+ "CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING",
159080
+ "CLAUDE_CODE_ENABLE_TASKS",
159081
+ "CLAUDE_CODE_ENTRYPOINT",
159082
+ "CLAUDE_CODE_EXECPATH",
159083
+ "CLAUDE_CODE_MESSAGING_SOCKET",
159084
+ "CLAUDE_CODE_MESSAGING_TOKEN",
159085
+ "CLAUDE_CODE_QUESTION_PREVIEW_FORMAT",
159086
+ "CLAUDE_CODE_SESSION_ATTENDED",
159087
+ "CLAUDE_CODE_SESSION_ID",
159088
+ "CLAUDE_EFFORT",
159089
+ "CLAUDE_PID",
159090
+ "MCP_CONNECTION_NONBLOCKING"
159091
+ ]);
159092
+ var withoutParentSession = (environment) => Object.fromEntries(Object.entries(environment).filter(([name]) => !PARENT_SESSION_VARIABLES.has(name)));
159093
+
159161
159094
  // apps/backend/src/utils/agent-model-lister/index.ts
159162
159095
  var DEFAULT_TIMEOUT_MS = 15e3;
159163
159096
  var NO_MODELS = [];
@@ -159336,81 +159269,6 @@ var createBranchPuller = ({ runCommand: runCommand3, environment }) => {
159336
159269
  };
159337
159270
  };
159338
159271
 
159339
- // apps/backend/src/utils/claude-spawner/index.ts
159340
- var import_promises5 = require("node:fs/promises");
159341
- var import_node_path9 = require("node:path");
159342
- var import_node_util8 = require("node:util");
159343
- var import_node_pty = require("node-pty");
159344
- var TERMINAL_NAME = "xterm-256color";
159345
- var TERMINAL_COLUMNS = 120;
159346
- var TERMINAL_ROWS = 40;
159347
- var OUTPUT_TAIL_LENGTH = 2e4;
159348
- var CLAUDE_MISSING_REASON2 = "The claude command was not found on this machine.";
159349
- var EXIT_COMMAND = "/exit\r";
159350
- var DEFAULT_EXIT_GRACE_MS = 5e3;
159351
- var failed3 = (reason) => ({ kind: "failed", reason });
159352
- var definedEntries = (environment) => Object.fromEntries(
159353
- Object.entries(environment).filter((entry) => entry[1] !== void 0)
159354
- );
159355
- var OutputTail = class {
159356
- text = "";
159357
- add = (chunk) => {
159358
- this.text = `${this.text}${chunk}`.slice(-OUTPUT_TAIL_LENGTH);
159359
- };
159360
- read = () => (0, import_node_util8.stripVTControlCharacters)(this.text);
159361
- };
159362
- var TerminalProcess = class {
159363
- terminal;
159364
- output;
159365
- exitGraceMs;
159366
- hasExited = false;
159367
- constructor(terminal, output2, exitGraceMs) {
159368
- this.terminal = terminal;
159369
- this.output = output2;
159370
- this.exitGraceMs = exitGraceMs;
159371
- terminal.onExit(() => {
159372
- this.hasExited = true;
159373
- });
159374
- }
159375
- stop = () => {
159376
- if (this.hasExited) return;
159377
- this.terminal.write(EXIT_COMMAND);
159378
- setTimeout(() => {
159379
- if (!this.hasExited) this.terminal.kill();
159380
- }, this.exitGraceMs).unref();
159381
- };
159382
- readOutput = () => this.output.read();
159383
- };
159384
- var readProjectMcpFile = async (workingPath) => await (0, import_promises5.readFile)((0, import_node_path9.join)(workingPath, PROJECT_MCP_FILE_NAME), "utf8").catch(() => null);
159385
- var startReason = (error61) => error61 instanceof Error ? `Claude could not be started: ${error61.message}` : "Claude could not be started.";
159386
- var createClaudeSpawner = (options) => {
159387
- const findExecutable = createExecutableFinder(options);
159388
- return async (launch2, onExit) => {
159389
- const executable = await findExecutable(AGENT_CLI_NAMES.claude);
159390
- if (executable === null) return failed3(CLAUDE_MISSING_REASON2);
159391
- const environment = claudeCommandEnvironment(launch2.installPath, options.homePath, spawnEnvironmentOf(options.environment, launch2.effort));
159392
- const projectMcpServers = projectMcpServerNames(await readProjectMcpFile(launch2.workingPath));
159393
- try {
159394
- const launchArguments = claudeLaunchArguments(launch2, options.spawnPluginPath, projectMcpServers);
159395
- const terminal = (0, import_node_pty.spawn)(executable, [...launchArguments], {
159396
- name: TERMINAL_NAME,
159397
- cols: TERMINAL_COLUMNS,
159398
- rows: TERMINAL_ROWS,
159399
- cwd: launch2.workingPath,
159400
- env: definedEntries(environment)
159401
- });
159402
- const output2 = new OutputTail();
159403
- terminal.onData(output2.add);
159404
- terminal.onExit(({ exitCode }) => {
159405
- onExit({ exitCode });
159406
- });
159407
- return { kind: "started", process: new TerminalProcess(terminal, output2, options.exitGraceMs ?? DEFAULT_EXIT_GRACE_MS) };
159408
- } catch (error61) {
159409
- return failed3(startReason(error61));
159410
- }
159411
- };
159412
- };
159413
-
159414
159272
  // node_modules/.pnpm/@code-hike+lighter@1.0.1/node_modules/@code-hike/lighter/dist/index.esm.mjs
159415
159273
  async function A3(A4) {
159416
159274
  return (async function(A5) {
@@ -162630,8 +162488,8 @@ var createCommitAuthorReader = (options) => async (repositoryPath) => {
162630
162488
  };
162631
162489
 
162632
162490
  // apps/backend/src/utils/configuration-file-store/index.ts
162633
- var import_promises6 = require("node:fs/promises");
162634
- var import_node_path10 = require("node:path");
162491
+ var import_promises5 = require("node:fs/promises");
162492
+ var import_node_path9 = require("node:path");
162635
162493
 
162636
162494
  // apps/backend/src/procedures/list-configurations/utils/stored-configuration.ts
162637
162495
  var CONFIGURATION_FILE_NAME = "configuration.json";
@@ -162701,7 +162559,7 @@ var MISSING_FOLDER_CODE = "ENOENT";
162701
162559
  var isMissingFolder = (error61) => error61 instanceof Error && "code" in error61 && error61.code === MISSING_FOLDER_CODE;
162702
162560
  var readFolderNames2 = async (path11) => {
162703
162561
  try {
162704
- const entries = await (0, import_promises6.readdir)(path11, { withFileTypes: true });
162562
+ const entries = await (0, import_promises5.readdir)(path11, { withFileTypes: true });
162705
162563
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
162706
162564
  } catch (error61) {
162707
162565
  if (isMissingFolder(error61)) return [];
@@ -162709,16 +162567,16 @@ var readFolderNames2 = async (path11) => {
162709
162567
  }
162710
162568
  };
162711
162569
  var readConfigurationFiles = async (folderPath) => {
162712
- const entries = await (0, import_promises6.readdir)(folderPath, { withFileTypes: true });
162570
+ const entries = await (0, import_promises5.readdir)(folderPath, { withFileTypes: true });
162713
162571
  const fileNames = entries.filter((entry) => entry.isFile() && isStoredFileName(entry.name)).map((entry) => entry.name);
162714
162572
  return new Map(
162715
- await Promise.all(fileNames.map(async (fileName) => [fileName, await (0, import_promises6.readFile)((0, import_node_path10.join)(folderPath, fileName), "utf8")]))
162573
+ await Promise.all(fileNames.map(async (fileName) => [fileName, await (0, import_promises5.readFile)((0, import_node_path9.join)(folderPath, fileName), "utf8")]))
162716
162574
  );
162717
162575
  };
162718
162576
  var FileConfigurationStore = class {
162719
162577
  rootPath;
162720
162578
  constructor(dataPath) {
162721
- this.rootPath = (0, import_node_path10.join)(dataPath, CONFIGURATIONS_FOLDER_NAME);
162579
+ this.rootPath = (0, import_node_path9.join)(dataPath, CONFIGURATIONS_FOLDER_NAME);
162722
162580
  }
162723
162581
  read = async () => storedConfigurationsOf(await this.readFolders());
162724
162582
  write = async (configurations2) => {
@@ -162730,21 +162588,21 @@ var FileConfigurationStore = class {
162730
162588
  await removeFile(this.pathOf(file2));
162731
162589
  }));
162732
162590
  await Promise.all(changes.removedFolders.map(async (folder) => {
162733
- await (0, import_promises6.rm)((0, import_node_path10.join)(this.rootPath, folder), { recursive: true, force: true });
162591
+ await (0, import_promises5.rm)((0, import_node_path9.join)(this.rootPath, folder), { recursive: true, force: true });
162734
162592
  }));
162735
162593
  };
162736
162594
  readFolders = async () => {
162737
162595
  const folders = await readFolderNames2(this.rootPath);
162738
162596
  return new Map(
162739
- await Promise.all(folders.map(async (folder) => [folder, await readConfigurationFiles((0, import_node_path10.join)(this.rootPath, folder))]))
162597
+ await Promise.all(folders.map(async (folder) => [folder, await readConfigurationFiles((0, import_node_path9.join)(this.rootPath, folder))]))
162740
162598
  );
162741
162599
  };
162742
- pathOf = (file2) => (0, import_node_path10.join)(this.rootPath, file2.folder, file2.fileName);
162600
+ pathOf = (file2) => (0, import_node_path9.join)(this.rootPath, file2.folder, file2.fileName);
162743
162601
  };
162744
162602
 
162745
162603
  // apps/backend/src/utils/conversation-file-store/index.ts
162746
- var import_promises7 = require("node:fs/promises");
162747
- var import_node_path11 = require("node:path");
162604
+ var import_promises6 = require("node:fs/promises");
162605
+ var import_node_path10 = require("node:path");
162748
162606
 
162749
162607
  // apps/backend/src/procedures/get-conversation/utils/stored-conversation.ts
162750
162608
  var JSON_INDENT6 = 2;
@@ -162841,7 +162699,7 @@ var toFileName = (name) => {
162841
162699
  var fromFileName = (fileName) => decodeURIComponent(fileName);
162842
162700
  var readFolderNames3 = async (path11) => {
162843
162701
  try {
162844
- return await (0, import_promises7.readdir)(path11);
162702
+ return await (0, import_promises6.readdir)(path11);
162845
162703
  } catch {
162846
162704
  return [];
162847
162705
  }
@@ -162850,7 +162708,7 @@ var FileConversationStore = class {
162850
162708
  rootPath;
162851
162709
  writes = /* @__PURE__ */ new Map();
162852
162710
  constructor(dataPath) {
162853
- this.rootPath = (0, import_node_path11.join)(dataPath, CONVERSATIONS_FOLDER_NAME);
162711
+ this.rootPath = (0, import_node_path10.join)(dataPath, CONVERSATIONS_FOLDER_NAME);
162854
162712
  }
162855
162713
  read = async (key) => await this.readPath(this.pathOf(key));
162856
162714
  append = async (key, events) => await this.queue(key, async () => {
@@ -162873,7 +162731,7 @@ var FileConversationStore = class {
162873
162731
  const repositoryFolders = await readFolderNames3(this.rootPath);
162874
162732
  const nested = await Promise.all(
162875
162733
  repositoryFolders.map(async (folder) => {
162876
- const fileNames = await readFolderNames3((0, import_node_path11.join)(this.rootPath, folder));
162734
+ const fileNames = await readFolderNames3((0, import_node_path10.join)(this.rootPath, folder));
162877
162735
  return fileNames.filter((fileName) => fileName.endsWith(FILE_EXTENSION)).map((fileName) => ({
162878
162736
  repositoryName: fromFileName(folder),
162879
162737
  targetId: fromFileName(fileName.slice(0, -FILE_EXTENSION.length))
@@ -162889,7 +162747,7 @@ var FileConversationStore = class {
162889
162747
  if (conversation === null) throw new Error(UNREADABLE_MESSAGE2);
162890
162748
  return conversation;
162891
162749
  };
162892
- pathOf = (key) => (0, import_node_path11.join)(this.rootPath, toFileName(key.repositoryName), `${toFileName(key.targetId)}${FILE_EXTENSION}`);
162750
+ pathOf = (key) => (0, import_node_path10.join)(this.rootPath, toFileName(key.repositoryName), `${toFileName(key.targetId)}${FILE_EXTENSION}`);
162893
162751
  queue = async (key, task) => {
162894
162752
  const id = reviewKeyId(key);
162895
162753
  const previous = this.writes.get(id) ?? Promise.resolve();
@@ -163099,11 +162957,11 @@ var createGitIdentityReader = (options) => {
163099
162957
  };
163100
162958
 
163101
162959
  // apps/backend/src/utils/import-file-store/index.ts
163102
- var import_node_path12 = require("node:path");
162960
+ var import_node_path11 = require("node:path");
163103
162961
  var IMPORTS_FOLDER_NAME = "imports";
163104
162962
  var FILE_EXTENSION2 = ".json";
163105
162963
  var createImportFileStore = (dataPath) => {
163106
- const pathOf2 = (key) => (0, import_node_path12.join)(dataPath, IMPORTS_FOLDER_NAME, toFileName(key.repositoryName), `${toFileName(key.targetId)}${FILE_EXTENSION2}`);
162964
+ const pathOf2 = (key) => (0, import_node_path11.join)(dataPath, IMPORTS_FOLDER_NAME, toFileName(key.repositoryName), `${toFileName(key.targetId)}${FILE_EXTENSION2}`);
163107
162965
  return {
163108
162966
  pathOf: pathOf2,
163109
162967
  read: async (key) => await readTextOrNull(pathOf2(key)),
@@ -163114,30 +162972,30 @@ var createImportFileStore = (dataPath) => {
163114
162972
  };
163115
162973
 
163116
162974
  // apps/backend/src/utils/instruction-file-reader/index.ts
163117
- var import_promises8 = require("node:fs/promises");
163118
- var import_node_path13 = require("node:path");
162975
+ var import_promises7 = require("node:fs/promises");
162976
+ var import_node_path12 = require("node:path");
163119
162977
  var INSTRUCTION_FILE_EXTENSION3 = ".md";
163120
162978
  var readFolderTexts = async (folderPath) => {
163121
- const fileNames = (await (0, import_promises8.readdir)(folderPath)).filter((name) => name.endsWith(INSTRUCTION_FILE_EXTENSION3));
162979
+ const fileNames = (await (0, import_promises7.readdir)(folderPath)).filter((name) => name.endsWith(INSTRUCTION_FILE_EXTENSION3));
163122
162980
  const entries = await Promise.all(
163123
162981
  fileNames.map(async (name) => [
163124
162982
  name.slice(0, -INSTRUCTION_FILE_EXTENSION3.length),
163125
- await (0, import_promises8.readFile)((0, import_node_path13.join)(folderPath, name), "utf8")
162983
+ await (0, import_promises7.readFile)((0, import_node_path12.join)(folderPath, name), "utf8")
163126
162984
  ])
163127
162985
  );
163128
162986
  return new Map(entries);
163129
162987
  };
163130
162988
  var readPresetTexts = async (presetsPath) => {
163131
- const folders = (await (0, import_promises8.readdir)(presetsPath, { withFileTypes: true })).filter((entry) => entry.isDirectory());
162989
+ const folders = (await (0, import_promises7.readdir)(presetsPath, { withFileTypes: true })).filter((entry) => entry.isDirectory());
163132
162990
  const entries = await Promise.all(
163133
- folders.map(async (folder) => [folder.name, await readFolderTexts((0, import_node_path13.join)(presetsPath, folder.name))])
162991
+ folders.map(async (folder) => [folder.name, await readFolderTexts((0, import_node_path12.join)(presetsPath, folder.name))])
163134
162992
  );
163135
162993
  return new Map(entries);
163136
162994
  };
163137
162995
  var readInstructionTexts = async (configurationsPath) => {
163138
162996
  const [system, presets] = await Promise.all([
163139
- readFolderTexts((0, import_node_path13.join)(configurationsPath, SYSTEM_INSTRUCTIONS_FOLDER)),
163140
- readPresetTexts((0, import_node_path13.join)(configurationsPath, DEFAULT_CONFIGURATIONS_FOLDER))
162997
+ readFolderTexts((0, import_node_path12.join)(configurationsPath, SYSTEM_INSTRUCTIONS_FOLDER)),
162998
+ readPresetTexts((0, import_node_path12.join)(configurationsPath, DEFAULT_CONFIGURATIONS_FOLDER))
163141
162999
  ]);
163142
163000
  return { system, presets };
163143
163001
  };
@@ -163354,21 +163212,21 @@ var RegistryReader = class {
163354
163212
  var createLatestVersionReader = (request) => new RegistryReader(request).read;
163355
163213
 
163356
163214
  // apps/backend/src/utils/published-page-writer/index.ts
163357
- var import_promises9 = require("node:fs/promises");
163358
- var import_node_path14 = require("node:path");
163215
+ var import_promises8 = require("node:fs/promises");
163216
+ var import_node_path13 = require("node:path");
163359
163217
  var PUBLICATIONS_FOLDER_NAME = "publications";
163360
163218
  var createPublishedPageWriter = ({ bundlePath, dataPath }) => async (key, texts) => {
163361
163219
  const bundleFileNames = await readFolderNames3(bundlePath);
163362
163220
  if (bundleFileNames.length === 0) return null;
163363
- const folderPath = (0, import_node_path14.join)(dataPath, PUBLICATIONS_FOLDER_NAME, toFileName(key.repositoryName), toFileName(key.targetId));
163364
- await (0, import_promises9.mkdir)(folderPath, { recursive: true });
163221
+ const folderPath = (0, import_node_path13.join)(dataPath, PUBLICATIONS_FOLDER_NAME, toFileName(key.repositoryName), toFileName(key.targetId));
163222
+ await (0, import_promises8.mkdir)(folderPath, { recursive: true });
163365
163223
  await Promise.all([
163366
163224
  ...bundleFileNames.map(async (fileName) => {
163367
- await (0, import_promises9.copyFile)((0, import_node_path14.join)(bundlePath, fileName), (0, import_node_path14.join)(folderPath, fileName));
163225
+ await (0, import_promises8.copyFile)((0, import_node_path13.join)(bundlePath, fileName), (0, import_node_path13.join)(folderPath, fileName));
163368
163226
  }),
163369
- writeText((0, import_node_path14.join)(folderPath, PAGE_FILE_NAME), texts.html),
163370
- writeText((0, import_node_path14.join)(folderPath, REVIEW_SCRIPT_FILE_NAME), texts.reviewScript),
163371
- texts.flightPlanFile === null ? removeFile((0, import_node_path14.join)(folderPath, FLIGHT_PLAN_FILE_NAME)) : writeText((0, import_node_path14.join)(folderPath, FLIGHT_PLAN_FILE_NAME), texts.flightPlanFile)
163227
+ writeText((0, import_node_path13.join)(folderPath, PAGE_FILE_NAME), texts.html),
163228
+ writeText((0, import_node_path13.join)(folderPath, REVIEW_SCRIPT_FILE_NAME), texts.reviewScript),
163229
+ texts.flightPlanFile === null ? removeFile((0, import_node_path13.join)(folderPath, FLIGHT_PLAN_FILE_NAME)) : writeText((0, import_node_path13.join)(folderPath, FLIGHT_PLAN_FILE_NAME), texts.flightPlanFile)
163372
163230
  ]);
163373
163231
  return {
163374
163232
  folderPath,
@@ -163582,8 +163440,8 @@ var createRepositoryActivityReader = (options) => {
163582
163440
  };
163583
163441
 
163584
163442
  // apps/backend/src/utils/repository-folder-finder/index.ts
163585
- var import_promises10 = require("node:fs/promises");
163586
- var import_node_path15 = require("node:path");
163443
+ var import_promises9 = require("node:fs/promises");
163444
+ var import_node_path14 = require("node:path");
163587
163445
  var MAX_SCAN_DEPTH = 6;
163588
163446
  var REPOSITORY_CACHE_MS = 6e4;
163589
163447
  var HIDDEN_FOLDER_PREFIX = ".";
@@ -163591,7 +163449,7 @@ var SKIPPED_FOLDER_NAMES = /* @__PURE__ */ new Set(["node_modules", "Library", "
163591
163449
  var isScannableFolderName = (name) => !name.startsWith(HIDDEN_FOLDER_PREFIX) && !SKIPPED_FOLDER_NAMES.has(name);
163592
163450
  var readSubfolderNames = async (path11) => {
163593
163451
  try {
163594
- const entries = await (0, import_promises10.readdir)(path11, { withFileTypes: true });
163452
+ const entries = await (0, import_promises9.readdir)(path11, { withFileTypes: true });
163595
163453
  return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
163596
163454
  } catch {
163597
163455
  return [];
@@ -163600,7 +163458,7 @@ var readSubfolderNames = async (path11) => {
163600
163458
  var findRepositoriesBelow = async (folderPath, depthLeft) => {
163601
163459
  if (depthLeft === 0) return [];
163602
163460
  const subfolderNames = await readSubfolderNames(folderPath);
163603
- const subfolderPaths = subfolderNames.filter(isScannableFolderName).map((name) => (0, import_node_path15.join)(folderPath, name));
163461
+ const subfolderPaths = subfolderNames.filter(isScannableFolderName).map((name) => (0, import_node_path14.join)(folderPath, name));
163604
163462
  const repositoryPathsBySubfolder = await Promise.all(
163605
163463
  subfolderPaths.map(async (subfolderPath) => await isRepositoryFolder(subfolderPath) ? [subfolderPath] : await findRepositoriesBelow(subfolderPath, depthLeft - 1))
163606
163464
  );
@@ -163751,7 +163609,7 @@ var RepositoryInspector = class {
163751
163609
  };
163752
163610
 
163753
163611
  // apps/backend/src/utils/review-file-store/index.ts
163754
- var import_node_path16 = require("node:path");
163612
+ var import_node_path15 = require("node:path");
163755
163613
 
163756
163614
  // apps/backend/src/procedures/list-repositories/utils/schemas.ts
163757
163615
  var LABEL_SEGMENT_KINDS = ["plain", "code"];
@@ -163937,17 +163795,7 @@ var staleNoticeSchema = external_exports.strictObject({
163937
163795
  prompt: external_exports.string(),
163938
163796
  copiedMessage: external_exports.string()
163939
163797
  });
163940
- var PROGRESS_STATES = ["writing", "waiting", "stopped"];
163941
- var permissionRequestSchema = external_exports.strictObject({
163942
- batchId: external_exports.string(),
163943
- questionId: external_exports.string(),
163944
- title: external_exports.string(),
163945
- description: external_exports.string(),
163946
- detail: external_exports.string().nullable(),
163947
- allowLabel: external_exports.string(),
163948
- denyLabel: external_exports.string(),
163949
- reasonLabel: external_exports.string()
163950
- });
163798
+ var PROGRESS_STATES = ["writing", "waiting"];
163951
163799
  var reviewProgressSchema = external_exports.strictObject({
163952
163800
  state: external_exports.enum(PROGRESS_STATES),
163953
163801
  label: external_exports.string(),
@@ -163956,8 +163804,7 @@ var reviewProgressSchema = external_exports.strictObject({
163956
163804
  countLabel: external_exports.string(),
163957
163805
  writtenCount: external_exports.number().int(),
163958
163806
  totalCount: external_exports.number().int(),
163959
- prompt: external_exports.strictObject({ label: external_exports.string(), text: external_exports.string(), copiedMessage: external_exports.string() }).nullable(),
163960
- permission: permissionRequestSchema.nullable()
163807
+ prompt: external_exports.strictObject({ label: external_exports.string(), text: external_exports.string(), copiedMessage: external_exports.string() }).nullable()
163961
163808
  });
163962
163809
  var completionNoticeSchema = external_exports.strictObject({ title: external_exports.string(), body: external_exports.string() });
163963
163810
  var railSectionSchema = external_exports.strictObject({
@@ -164235,13 +164082,13 @@ var FileReviewStore = class {
164235
164082
  sampleStore;
164236
164083
  amendTurns = /* @__PURE__ */ new Map();
164237
164084
  constructor(dataPath, sampleStore) {
164238
- this.rootPath = (0, import_node_path16.join)(dataPath, REVIEWS_FOLDER_NAME);
164085
+ this.rootPath = (0, import_node_path15.join)(dataPath, REVIEWS_FOLDER_NAME);
164239
164086
  this.sampleStore = sampleStore;
164240
164087
  }
164241
164088
  findArtifact = async (key) => (await this.readStored(key))?.artifact ?? await this.sampleStore.findArtifact(key);
164242
164089
  listTargetIds = async (repositoryName) => {
164243
164090
  const [fileNames, sampleTargetIds] = await Promise.all([
164244
- readFolderNames3((0, import_node_path16.join)(this.rootPath, toFileName(repositoryName))),
164091
+ readFolderNames3((0, import_node_path15.join)(this.rootPath, toFileName(repositoryName))),
164245
164092
  this.sampleStore.listTargetIds(repositoryName)
164246
164093
  ]);
164247
164094
  const savedTargetIds = fileNames.filter((fileName) => fileName.endsWith(FILE_EXTENSION3)).map((fileName) => fromFileName(fileName.slice(0, -FILE_EXTENSION3.length)));
@@ -164331,7 +164178,7 @@ var FileReviewStore = class {
164331
164178
  await removeFile(this.filePathOf(from, extension));
164332
164179
  };
164333
164180
  pathOf = (key) => this.filePathOf(key, FILE_EXTENSION3);
164334
- filePathOf = (key, extension) => (0, import_node_path16.join)(this.rootPath, toFileName(key.repositoryName), `${toFileName(key.targetId)}${extension}`);
164181
+ filePathOf = (key, extension) => (0, import_node_path15.join)(this.rootPath, toFileName(key.repositoryName), `${toFileName(key.targetId)}${extension}`);
164335
164182
  };
164336
164183
 
164337
164184
  // apps/backend/src/utils/run-command/index.ts
@@ -164350,7 +164197,7 @@ var runCommand = async (executable, commandArguments, environment) => await new
164350
164197
  });
164351
164198
 
164352
164199
  // apps/backend/src/utils/setup-file-store/index.ts
164353
- var import_node_path17 = require("node:path");
164200
+ var import_node_path16 = require("node:path");
164354
164201
 
164355
164202
  // apps/backend/src/procedures/get-review-preferences/utils/schemas.ts
164356
164203
  var writeUpTimingSchema = external_exports.enum(WRITE_UP_TIMINGS);
@@ -164472,7 +164319,7 @@ var UNREADABLE_MESSAGE4 = "The saved settings cannot be read.";
164472
164319
  var FileSetupStore = class {
164473
164320
  settingsPath;
164474
164321
  constructor(dataPath) {
164475
- this.settingsPath = (0, import_node_path17.join)(dataPath, ...SETTINGS_PATH);
164322
+ this.settingsPath = (0, import_node_path16.join)(dataPath, ...SETTINGS_PATH);
164476
164323
  }
164477
164324
  read = async () => {
164478
164325
  const setup = parseSettings(await readTextOrNull(this.settingsPath));
@@ -164485,7 +164332,7 @@ var FileSetupStore = class {
164485
164332
  };
164486
164333
 
164487
164334
  // apps/backend/src/utils/target-memory-file-store/index.ts
164488
- var import_node_path18 = require("node:path");
164335
+ var import_node_path17 = require("node:path");
164489
164336
  var MEMORY_FILE_NAME = "targets.json";
164490
164337
  var UNSAFE_NAME_PATTERN = /^\.{1,2}$|[\\/\0]/;
164491
164338
  var isSafeName = (repositoryName) => repositoryName !== "" && !UNSAFE_NAME_PATTERN.test(repositoryName);
@@ -164514,11 +164361,28 @@ var FileTargetMemoryStore = class {
164514
164361
  const memory2 = await this.ensure(repositoryName, now);
164515
164362
  await writeText(this.pathOf(repositoryName), formatTargetMemory(withAddedTarget(memory2, target)));
164516
164363
  };
164517
- pathOf = (repositoryName) => (0, import_node_path18.join)(this.dataPath, repositoryName, MEMORY_FILE_NAME);
164364
+ pathOf = (repositoryName) => (0, import_node_path17.join)(this.dataPath, repositoryName, MEMORY_FILE_NAME);
164518
164365
  };
164519
164366
 
164367
+ // apps/backend/src/utils/terminal-opener/index.ts
164368
+ var MAC = "darwin";
164369
+ var APPLE_SCRIPT_EXECUTABLE = "osascript";
164370
+ var SUCCESS_EXIT_CODE9 = 0;
164371
+ var NO_REASON = "macOS gave no reason.";
164372
+ var appleScriptStringOf = (text2) => `"${text2.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
164373
+ var terminalScriptArgumentsOf = (command) => [
164374
+ "-e",
164375
+ 'tell application "Terminal" to activate',
164376
+ "-e",
164377
+ `tell application "Terminal" to do script ${appleScriptStringOf(command)}`
164378
+ ];
164379
+ var createTerminalOpener = (options) => options.platform === MAC ? async (command) => {
164380
+ const outcome = await options.runCommand(APPLE_SCRIPT_EXECUTABLE, terminalScriptArgumentsOf(command), options.environment);
164381
+ return outcome.exitCode === SUCCESS_EXIT_CODE9 ? { kind: "opened" } : { kind: "failed", reason: outcome.errorOutput.trim() === "" ? NO_REASON : outcome.errorOutput.trim() };
164382
+ } : null;
164383
+
164520
164384
  // apps/backend/src/utils/update-check-file/index.ts
164521
- var import_node_path19 = require("node:path");
164385
+ var import_node_path18 = require("node:path");
164522
164386
 
164523
164387
  // apps/backend/src/procedures/get-update-notice/utils/schemas.ts
164524
164388
  var updateCheckSchema = external_exports.strictObject({
@@ -164554,7 +164418,7 @@ var parseUpdateCheck = (text2) => {
164554
164418
  var readNothing = () => null;
164555
164419
  var ignoreFailure = () => void 0;
164556
164420
  var createUpdateCheckFile = (dataPath) => {
164557
- const filePath = (0, import_node_path19.join)(dataPath, FILE_NAME);
164421
+ const filePath = (0, import_node_path18.join)(dataPath, FILE_NAME);
164558
164422
  return {
164559
164423
  read: async () => parseUpdateCheck(await readTextOrNull(filePath).catch(readNothing)),
164560
164424
  write: async (check2) => {
@@ -164567,15 +164431,12 @@ var createUpdateCheckFile = (dataPath) => {
164567
164431
  // apps/backend/src/procedures/context.ts
164568
164432
  var MOCK_SERVER_URL = "http://localhost:3000";
164569
164433
  var MOCK_APP_URL = "http://localhost:4200";
164570
- var NO_SPAWNERS = {};
164571
164434
  var createConversationEnvironment = (overrides = {}) => ({
164572
164435
  store: new InMemoryConversationStore(),
164573
164436
  hub: createConversationHub(),
164574
- agents: new InMemoryAgentRegistry(),
164575
- writeUpQueue: new InMemoryWriteUpQueue(),
164576
- spawners: NO_SPAWNERS,
164437
+ presence: new InMemoryAgentPresenceRegistry(),
164577
164438
  serverUrl: MOCK_SERVER_URL,
164578
- newId: import_node_crypto4.randomUUID,
164439
+ newId: import_node_crypto5.randomUUID,
164579
164440
  ...overrides
164580
164441
  });
164581
164442
  var SAMPLE_REPOSITORY_MESSAGE = "This is a sample repository, so there is no code to read.";
@@ -164601,6 +164462,7 @@ var createMockProcedureContext = (startedAt) => ({
164601
164462
  readInstructionTexts: readSampleInstructionTexts,
164602
164463
  writePublishedPage: writeSamplePublishedPage,
164603
164464
  importFiles: new InMemoryImportFileStore(),
164465
+ openTerminal: null,
164604
164466
  appUrl: MOCK_APP_URL,
164605
164467
  now: () => /* @__PURE__ */ new Date(),
164606
164468
  dataNotice: SAMPLE_DATA_NOTICE
@@ -164637,13 +164499,13 @@ var createLocalProcedureContext = (startedAt, machine) => {
164637
164499
  readInstructionTexts: new InstructionFileReader(machine.configurationsPath).read,
164638
164500
  writePublishedPage: createPublishedPageWriter({ bundlePath: machine.publishedBundlePath, dataPath: machine.dataPath }),
164639
164501
  importFiles: createImportFileStore(machine.dataPath),
164502
+ openTerminal: createTerminalOpener({ platform: machine.platform, runCommand, environment: machine.environment }),
164640
164503
  appUrl: machine.appUrl,
164641
164504
  changeReader: createGitChangeReader({ runCommand, environment: machine.environment }),
164642
164505
  pullBranch: createBranchPuller({ runCommand, environment: machine.environment }),
164643
164506
  openRepository,
164644
164507
  conversationEnvironment: createConversationEnvironment({
164645
164508
  store: new FileConversationStore(machine.dataPath),
164646
- spawners: { claude: createClaudeSpawner({ ...machine, spawnPluginPath: spawnPluginPathOf(machine.dataPath) }) },
164647
164509
  serverUrl: machine.serverUrl
164648
164510
  }),
164649
164511
  repositoryStore: new LiveRepositoryStore({
@@ -164659,6 +164521,7 @@ var createLocalProcedureContext = (startedAt, machine) => {
164659
164521
  hasAgentCli: createAgentCliProbe(machine),
164660
164522
  listAgentModels: createAgentModelLister(machine),
164661
164523
  installAgent: createAgentInstaller({ ...machine, runCommand }),
164524
+ isAgentInstallCurrent: createInstallChecker({ ...machine, runCommand }),
164662
164525
  probeForgeClis: createForgeCliProbe({ ...machine, runCommand }),
164663
164526
  saveForgeCredential: createForgeCredentialSaver(saveInKeychain),
164664
164527
  ...createAuthorLookup({
@@ -164747,7 +164610,9 @@ var getUpdateNotice = async (_input, context) => {
164747
164610
  // apps/backend/src/procedures/refresh-agent-installs/index.ts
164748
164611
  var refreshAgentInstalls = async (context) => {
164749
164612
  const setup = await context.setupStore.read();
164750
- const installs = setup.agents?.installs ?? [];
164613
+ const saved = setup.agents?.installs ?? [];
164614
+ const currents = await Promise.all(saved.map(async (install) => await context.setupEnvironment.isAgentInstallCurrent(install)));
164615
+ const installs = saved.filter((_install, position) => !currents[position]);
164751
164616
  const attempts = await Promise.all(
164752
164617
  installs.map(async (install) => ({ install, installation: await context.setupEnvironment.installAgent(install) }))
164753
164618
  );
@@ -164760,314 +164625,9 @@ var refreshAgentInstalls = async (context) => {
164760
164625
  var import_sensible = __toESM(require_sensible());
164761
164626
  var import_fastify4 = __toESM(require_fastify());
164762
164627
 
164763
- // apps/backend/src/procedures/close-all-agents/index.ts
164764
- var closeAllAgents = (context) => context.conversationEnvironment.agents.list().filter((agent) => closeAgent(agent.key, "shutdown", context)).length;
164765
-
164766
- // apps/backend/src/procedures/deliver-to-agent/utils/agent-problems.ts
164767
- var SCREEN_EXCERPT_LENGTH = 600;
164768
- var MS_PER_SECOND2 = 1e3;
164769
- var WHITESPACE_PATTERN3 = /\s+/g;
164770
- var screenExcerptOf = (output2) => output2.replace(WHITESPACE_PATTERN3, " ").trim().slice(-SCREEN_EXCERPT_LENGTH);
164771
- var withScreen = (sentence, output2) => {
164772
- const excerpt = screenExcerptOf(output2);
164773
- return excerpt === "" ? sentence : `${sentence} Its terminal last showed: ${excerpt}`;
164774
- };
164775
- var unexpectedExitReason = (agent, exitCode, output2) => withScreen(`${AGENT_LABELS[agent]} stopped before it finished (exit code ${String(exitCode)}).`, output2);
164776
- var startTimeoutReason = (agent, timeoutMs, output2) => withScreen(
164777
- `${AGENT_LABELS[agent]} did not start within ${String(Math.round(timeoutMs / MS_PER_SECOND2))} seconds, so Flight Plan closed it.`,
164778
- output2
164779
- );
164780
-
164781
- // apps/backend/src/procedures/close-idle-agents/utils/find-stale-agents.ts
164782
- var AGENT_START_TIMEOUT_MS = 9e4;
164783
- var AGENT_IDLE_MS = 6e5;
164784
- var ageMs = (since, now) => now.getTime() - since.getTime();
164785
- var staleReasonOf = (agent, now) => {
164786
- if (agent.closing !== null || agent.isAwaitingPermission) return null;
164787
- if (agent.phase === "starting") return ageMs(agent.startedAt, now) > AGENT_START_TIMEOUT_MS ? "start-timeout" : null;
164788
- return ageMs(agent.lastActivityAt, now) > AGENT_IDLE_MS ? "idle" : null;
164789
- };
164790
- var findStaleAgents = (agents, now) => agents.flatMap((agent) => {
164791
- const reason = staleReasonOf(agent, now);
164792
- return reason === null ? [] : [{ agent, reason }];
164793
- });
164794
-
164795
- // apps/backend/src/procedures/close-idle-agents/index.ts
164796
- var closeStaleAgent = async ({ agent, reason }, context) => {
164797
- if (reason === "start-timeout") {
164798
- const output2 = agent.process?.readOutput() ?? "";
164799
- await recordAgentFailure(agent.key, startTimeoutReason(agent.agent, AGENT_START_TIMEOUT_MS, output2), context);
164800
- }
164801
- closeAgent(agent.key, reason, context);
164802
- };
164803
- var closeIdleAgents = async (context) => {
164804
- const stale = findStaleAgents(context.conversationEnvironment.agents.list(), context.now());
164805
- await Promise.all(stale.map(async (staleAgent) => {
164806
- await closeStaleAgent(staleAgent, context);
164807
- }));
164808
- return stale.length;
164809
- };
164810
-
164811
- // apps/backend/src/server/agent-lifecycle.ts
164812
- var AGENT_SWEEP_INTERVAL_MS = 15e3;
164813
- var registerAgentLifecycle = (server, context) => {
164814
- const sweep2 = setInterval(() => {
164815
- closeIdleAgents(context).catch((error61) => {
164816
- server.log.error({ err: error61 }, "closing idle agents failed");
164817
- });
164818
- }, AGENT_SWEEP_INTERVAL_MS);
164819
- sweep2.unref();
164820
- server.addHook("onClose", async () => {
164821
- clearInterval(sweep2);
164822
- closeAllAgents(context);
164823
- await context.conversationEnvironment.hub.close();
164824
- });
164825
- };
164826
-
164827
164628
  // apps/backend/src/server/api/plugin.ts
164828
164629
  var import_sse = __toESM(require_sse());
164829
164630
 
164830
- // apps/backend/src/procedures/get-conversation/utils/conversation-hub.ts
164831
- var NO_TIME_LEFT_MS = 0;
164832
- var nextChangeOf = async (subscription, options) => {
164833
- const timeoutMs = options.remainingMs === null ? null : Math.max(options.remainingMs(), NO_TIME_LEFT_MS);
164834
- if (timeoutMs === NO_TIME_LEFT_MS) return "timed-out";
164835
- return await subscription.nextChange({ timeoutMs, signal: options.signal });
164836
- };
164837
- var readsOnChange = (subscription, options, read2) => ({
164838
- [Symbol.asyncIterator]: () => ({
164839
- next: async () => {
164840
- const wait = await nextChangeOf(subscription, options);
164841
- return wait === "changed" ? { value: await read2(), done: false } : { value: void 0, done: true };
164842
- }
164843
- })
164844
- });
164845
-
164846
- // apps/backend/src/procedures/request-permission/index.ts
164847
- var PERMISSION_WAIT_MS = 59e4;
164848
- var DENIED_MESSAGE = "The reviewer denied this.";
164849
- var UNANSWERED_MESSAGE = "The reviewer did not answer in time, so this was refused.";
164850
- var UNANSWERED_REASON = "The agent stopped waiting for an answer.";
164851
- var STOPPED_REASON = "The agent stopped before you answered.";
164852
- var readAnswer = async (key, batchId, context) => {
164853
- const { journal } = await context.conversationEnvironment.store.read(key);
164854
- const answerSet = findAnswerSet(journal, batchId);
164855
- if (answerSet !== null) return { kind: "answered", answerSet };
164856
- return findWithdrawal(journal, batchId) === null ? { kind: "pending" } : { kind: "withdrawn" };
164857
- };
164858
- var isAgentGone = (sessionId, context) => context.conversationEnvironment.agents.findBySessionId(sessionId) === null;
164859
- var waitForAnswer = async (wait, context) => {
164860
- const first = await readAnswer(wait.key, wait.batchId, context);
164861
- if (first.kind !== "pending") return first;
164862
- const remainingMs = () => wait.deadlineMs - context.now().getTime();
164863
- const answers = readsOnChange(wait.subscription, { signal: null, remainingMs }, async () => await readAnswer(wait.key, wait.batchId, context));
164864
- for await (const answer of answers) {
164865
- if (answer.kind !== "pending" || isAgentGone(wait.sessionId, context)) return answer;
164866
- }
164867
- return await readAnswer(wait.key, wait.batchId, context);
164868
- };
164869
- var decisionOf = (answerSet) => {
164870
- const answer = answerSet.answers.at(0);
164871
- if (answer === void 0) return { kind: "deny", message: DENIED_MESSAGE };
164872
- if (answer.pickedLabels.includes(ALLOW_LABEL)) return { kind: "allow" };
164873
- return { kind: "deny", message: answer.text === null ? DENIED_MESSAGE : `${DENIED_MESSAGE} ${answer.text}` };
164874
- };
164875
- var withdraw = async (wait, context) => {
164876
- const reason = isAgentGone(wait.sessionId, context) ? STOPPED_REASON : UNANSWERED_REASON;
164877
- await appendAndPublish(
164878
- wait.key,
164879
- [{ kind: "questions-withdrawn", id: context.conversationEnvironment.newId(), batchId: wait.batchId, reason, withdrawnAt: context.now() }],
164880
- context
164881
- );
164882
- };
164883
- var requestPermission = async (input2, context) => {
164884
- const { agents, hub, newId } = context.conversationEnvironment;
164885
- const agent = await confirmAgentStart(input2.sessionId, context);
164886
- if (agent === null) return { kind: "unknown-session" };
164887
- const { key } = agent;
164888
- const batchId = newId();
164889
- const now = context.now();
164890
- const subscription = hub.subscribe(key);
164891
- agents.update(key, (current) => ({ ...current, isAwaitingPermission: true, lastActivityAt: now }));
164892
- try {
164893
- await appendAndPublish(
164894
- key,
164895
- [{ kind: "question-batch", id: batchId, origin: "permission", questions: [permissionQuestionOf(`${batchId}-1`, input2.toolName, input2.toolInput)], askedAt: now }],
164896
- context
164897
- );
164898
- const wait = { key, batchId, sessionId: input2.sessionId, subscription, deadlineMs: now.getTime() + PERMISSION_WAIT_MS };
164899
- const answer = await waitForAnswer(wait, context);
164900
- if (answer.kind === "answered") return decisionOf(answer.answerSet);
164901
- if (answer.kind === "pending") await withdraw(wait, context);
164902
- return { kind: "deny", message: UNANSWERED_MESSAGE };
164903
- } finally {
164904
- subscription.close();
164905
- agents.update(key, (current) => ({ ...current, isAwaitingPermission: false, lastActivityAt: context.now() }));
164906
- hub.publish(key);
164907
- }
164908
- };
164909
-
164910
- // apps/backend/src/procedures/request-permission/utils/schemas.ts
164911
- var permissionHookInputSchema = external_exports.looseObject({
164912
- session_id: external_exports.string().min(1),
164913
- tool_name: external_exports.string().min(1),
164914
- tool_input: external_exports.unknown().optional()
164915
- });
164916
- var permissionHookOutputSchema = external_exports.strictObject({
164917
- hookSpecificOutput: external_exports.strictObject({
164918
- hookEventName: external_exports.literal("PermissionRequest"),
164919
- decision: external_exports.strictObject({ behavior: external_exports.enum(["allow", "deny"]), message: external_exports.string().optional() })
164920
- }).optional()
164921
- });
164922
-
164923
- // apps/backend/src/server/api/route-schema.ts
164924
- var JSON_SCHEMA_TARGET = "draft-7";
164925
- var toRouteSchema = (schema) => external_exports.toJSONSchema(schema, { target: JSON_SCHEMA_TARGET });
164926
-
164927
- // apps/backend/src/server/api/routes/agent-permission-request/index.ts
164928
- var toHookOutput = (decision) => {
164929
- if (decision.kind === "unknown-session") return {};
164930
- return {
164931
- hookSpecificOutput: {
164932
- hookEventName: "PermissionRequest",
164933
- decision: decision.kind === "allow" ? { behavior: "allow" } : { behavior: "deny", message: decision.message }
164934
- }
164935
- };
164936
- };
164937
- var registerAgentPermissionRequestRoute = (server, context) => {
164938
- server.post(
164939
- AGENT_HOOK_PATHS.permissionRequest,
164940
- {
164941
- schema: { body: toRouteSchema(permissionHookInputSchema), response: { 200: toRouteSchema(permissionHookOutputSchema) } }
164942
- },
164943
- async (request) => {
164944
- const { session_id: sessionId, tool_name: toolName, tool_input: toolInput } = request.body;
164945
- return toHookOutput(await requestPermission({ sessionId, toolName, toolInput }, context));
164946
- }
164947
- );
164948
- };
164949
-
164950
- // apps/backend/src/procedures/record-agent-activity/index.ts
164951
- var recordAgentActivity = async (input2, context) => await confirmAgentStart(input2.sessionId, context) === null ? { kind: "unknown-session" } : { kind: "recorded" };
164952
-
164953
- // apps/backend/src/procedures/record-agent-activity/utils/schemas.ts
164954
- var agentHookInputSchema = external_exports.looseObject({ session_id: external_exports.string().min(1) });
164955
- var emptyHookOutputSchema = external_exports.strictObject({});
164956
-
164957
- // apps/backend/src/server/api/routes/agent-prompt-submit/index.ts
164958
- var registerAgentPromptSubmitRoute = (server, context) => {
164959
- server.post(
164960
- AGENT_HOOK_PATHS.promptSubmit,
164961
- { schema: { body: toRouteSchema(agentHookInputSchema), response: { 200: toRouteSchema(emptyHookOutputSchema) } } },
164962
- async (request) => {
164963
- await recordAgentActivity({ sessionId: request.body.session_id }, context);
164964
- return {};
164965
- }
164966
- );
164967
- };
164968
-
164969
- // apps/backend/src/procedures/end-agent-turn/utils/schemas.ts
164970
- var stopHookInputSchema = external_exports.looseObject({
164971
- session_id: external_exports.string().min(1),
164972
- last_assistant_message: external_exports.string().nullable().optional()
164973
- });
164974
- var stopHookOutputSchema = external_exports.strictObject({
164975
- decision: external_exports.literal("block").optional(),
164976
- reason: external_exports.string().optional()
164977
- });
164978
-
164979
- // apps/backend/src/server/api/routes/agent-stop/index.ts
164980
- var AGENT_CLOSE_DELAY_MS = 1500;
164981
- var toHookOutput2 = (ending) => ending.kind === "continue" ? { decision: "block", reason: ending.reason } : {};
164982
- var registerAgentStopRoute = (server, context) => {
164983
- server.post(
164984
- AGENT_HOOK_PATHS.stop,
164985
- { schema: { body: toRouteSchema(stopHookInputSchema), response: { 200: toRouteSchema(stopHookOutputSchema) } } },
164986
- async (request) => {
164987
- const ending = await endAgentTurn(
164988
- { sessionId: request.body.session_id, lastAssistantMessage: request.body.last_assistant_message ?? null },
164989
- context
164990
- );
164991
- if (ending.kind === "close") {
164992
- setTimeout(() => {
164993
- closeAgent(ending.key, "turn-ended", context);
164994
- }, AGENT_CLOSE_DELAY_MS).unref();
164995
- }
164996
- return toHookOutput2(ending);
164997
- }
164998
- );
164999
- };
165000
-
165001
- // apps/backend/src/server/api/routes/agent-tool-use/index.ts
165002
- var registerAgentToolUseRoute = (server, context) => {
165003
- server.post(
165004
- AGENT_HOOK_PATHS.postToolUse,
165005
- { schema: { body: toRouteSchema(agentHookInputSchema), response: { 200: toRouteSchema(emptyHookOutputSchema) } } },
165006
- async (request) => {
165007
- await recordAgentActivity({ sessionId: request.body.session_id }, context);
165008
- return {};
165009
- }
165010
- );
165011
- };
165012
-
165013
- // apps/backend/src/procedures/listen-for-reviewer/index.ts
165014
- var readPacket = async (key, context) => {
165015
- const { store, serverUrl } = context.conversationEnvironment;
165016
- const { journal } = await store.read(key);
165017
- const pending = pendingInboundOf(journal);
165018
- if (pending.length === 0) return null;
165019
- return {
165020
- throughSequence: lastSequenceOf(pending),
165021
- text: formatListenPacket(formatInbound(pending, journal), listenCommandOf(serverUrl, key))
165022
- };
165023
- };
165024
- var recordDelivery = async (key, packet, context) => {
165025
- await appendAndPublish(
165026
- key,
165027
- [
165028
- {
165029
- kind: "delivery",
165030
- id: context.conversationEnvironment.newId(),
165031
- throughSequence: packet.throughSequence,
165032
- channel: "listener",
165033
- deliveredAt: context.now()
165034
- }
165035
- ],
165036
- context
165037
- );
165038
- };
165039
- var waitForPacket = async (key, subscription, context, signal) => {
165040
- const waiting = await readPacket(key, context);
165041
- if (waiting !== null) return waiting;
165042
- const packets = readsOnChange(subscription, { signal, remainingMs: null }, async () => await readPacket(key, context));
165043
- for await (const packet of packets) {
165044
- if (packet !== null) return packet;
165045
- }
165046
- return null;
165047
- };
165048
- var packetsOf = async function* (key, context, signal) {
165049
- const { agents, hub } = context.conversationEnvironment;
165050
- const subscription = hub.subscribe(key);
165051
- agents.openListener(key);
165052
- hub.publish(key);
165053
- try {
165054
- const packet = await waitForPacket(key, subscription, context, signal);
165055
- if (packet === null) return;
165056
- yield packet;
165057
- await recordDelivery(key, packet, context);
165058
- } finally {
165059
- subscription.close();
165060
- agents.closeListener(key, context.now());
165061
- hub.publish(key);
165062
- }
165063
- };
165064
- var listenForReviewer = async (input2, context, signal) => {
165065
- const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
165066
- const target = await findReviewTarget(key, context);
165067
- if (target.kind === "rejected") return target;
165068
- return { kind: "listening", packets: packetsOf(key, context, signal) };
165069
- };
165070
-
165071
164631
  // apps/backend/src/procedures/listen-for-reviewer/utils/schemas.ts
165072
164632
  var listenParamsSchema = external_exports.strictObject({
165073
164633
  repositoryName: external_exports.string().min(1),
@@ -165075,14 +164635,17 @@ var listenParamsSchema = external_exports.strictObject({
165075
164635
  });
165076
164636
  var listenRejectionSchema = external_exports.strictObject({ message: external_exports.string() });
165077
164637
 
164638
+ // apps/backend/src/server/api/route-schema.ts
164639
+ var JSON_SCHEMA_TARGET = "draft-7";
164640
+ var toRouteSchema = (schema) => external_exports.toJSONSchema(schema, { target: JSON_SCHEMA_TARGET });
164641
+
165078
164642
  // apps/backend/src/server/api/routes/listen-for-reviewer/index.ts
165079
164643
  var LISTEN_ROUTE = `/api/:repositoryName/:targetId/${LISTEN_PATH_SUFFIX}`;
165080
164644
  var HTTP_NOT_FOUND3 = 404;
165081
- var REPLY_EVENT = "reply";
165082
164645
  var passText = (data) => String(data);
165083
164646
  var toMessages = async function* (packets) {
165084
164647
  for await (const packet of packets) {
165085
- yield { id: String(packet.throughSequence), event: REPLY_EVENT, data: packet.text };
164648
+ yield packet.kind === "work" ? { id: String(packet.throughSequence), event: packet.kind, data: packet.text } : { event: packet.kind, data: packet.text };
165086
164649
  }
165087
164650
  };
165088
164651
  var registerListenForReviewerRoute = (server, context) => {
@@ -165113,10 +164676,6 @@ var registerListenForReviewerRoute = (server, context) => {
165113
164676
  var registerApi = async (server, context) => {
165114
164677
  await server.register(import_sse.default);
165115
164678
  registerListenForReviewerRoute(server, context);
165116
- registerAgentPromptSubmitRoute(server, context);
165117
- registerAgentToolUseRoute(server, context);
165118
- registerAgentPermissionRequestRoute(server, context);
165119
- registerAgentStopRoute(server, context);
165120
164679
  };
165121
164680
 
165122
164681
  // node_modules/.pnpm/@trpc+server@11.19.0_typescript@6.0.3/node_modules/@trpc/server/dist/codes-D5Ya6_Bi.mjs
@@ -168517,217 +168076,6 @@ var addSetupRepositoryRoute = publicProcedure.input(addSetupRepositoryInputSchem
168517
168076
  return { name: plan4.repository.name };
168518
168077
  });
168519
168078
 
168520
- // apps/backend/src/procedures/handle-agent-exit/index.ts
168521
- var handleAgentExit = async (input2, context) => {
168522
- const { agents, hub } = context.conversationEnvironment;
168523
- const released = agents.release(input2.key, input2.sessionId);
168524
- if (released === null) return { shouldRedeliver: false };
168525
- if (released.closing !== null) {
168526
- hub.publish(input2.key);
168527
- return { shouldRedeliver: shouldRedeliverAfter(released.closing) };
168528
- }
168529
- const output2 = released.process?.readOutput() ?? "";
168530
- await recordAgentFailure(input2.key, unexpectedExitReason(released.agent, input2.exitCode, output2), context);
168531
- return { shouldRedeliver: false };
168532
- };
168533
-
168534
- // apps/backend/src/procedures/request-write-up/utils/plan-node-write-up.ts
168535
- var DRAFTING_STATUS3 = "drafting";
168536
- var ALREADY_WRITTEN = { kind: "written", message: "This is already explained." };
168537
- var rejected2 = (message) => ({ kind: "rejected", field: "target", message });
168538
- var planned = (artifact, ask, message) => ({ kind: "planned", artifact: { ...artifact, status: DRAFTING_STATUS3 }, ask, message });
168539
- var quoted6 = (text2) => `\`${text2}\``;
168540
- var listWaypoints3 = (artifact) => artifact.segments.flatMap(waypointsOf).map(({ waypoint }) => waypoint);
168541
- var isWaypointUnwritten = (waypoint) => waypoint.title === null || waypoint.reading.trim() === "";
168542
- var waypointIdsOf = (artifact, target) => target.kind === "waypoint" ? listWaypoints3(artifact).filter((waypoint) => waypoint.id === target.waypointId).map((waypoint) => waypoint.id) : listRootBlockWaypointIds(artifact, target.rootBlockId);
168543
- var waypointAskOf = (ids) => ids.length === 1 ? `The reviewer asks for the write-up of the waypoint ${quoted6(ids[0])} alone: write its title and its reading, and nothing else.` : `The reviewer asks for the write-up of the waypoints ${joinedList2(ids.map(quoted6))} alone: write their titles and readings, and nothing else.`;
168544
- var planWaypoints = (artifact, target) => {
168545
- const waypointIds = waypointIdsOf(artifact, target);
168546
- if (waypointIds.length === 0) {
168547
- return rejected2(target.kind === "waypoint" ? "This waypoint is not in the review." : "This code block has no waypoint in the review.");
168548
- }
168549
- const unwrittenIds = listWaypoints3(artifact).filter((waypoint) => waypointIds.includes(waypoint.id) && isWaypointUnwritten(waypoint)).map((waypoint) => waypoint.id);
168550
- if (unwrittenIds.length === 0) return ALREADY_WRITTEN;
168551
- return planned(withRequiredWaypoints(artifact, unwrittenIds), waypointAskOf(unwrittenIds), `Explaining ${countLabel2(unwrittenIds.length, "waypoint", "waypoints")}`);
168552
- };
168553
- var isTraceNodeUnwritten = (node2) => node2.label === null || node2.triggerLabels.some((trigger) => trigger.label === null) || node2.blockLabels.some((listed) => listed.label === null);
168554
- var findTraceNode = (artifact, { segmentId, nodeId }) => listTraceNodes(artifact.segments.find((segment) => segment.id === segmentId)?.traceDiagram?.traceNodes ?? []).find((node2) => node2.id === nodeId);
168555
- var planTraceNode = (artifact, target) => {
168556
- const node2 = findTraceNode(artifact, target);
168557
- if (node2 === void 0) return rejected2("This card is not in the Trace Diagram.");
168558
- if (!isTraceNodeUnwritten(node2)) return ALREADY_WRITTEN;
168559
- return planned(
168560
- withRequiredCard(artifact, target.segmentId, target.nodeId),
168561
- `The reviewer asks for the write-up of the Trace Diagram node ${quoted6(target.nodeId)} of segment ${quoted6(target.segmentId)} alone: write its label and the labels of its trigger labels and listed blocks, and nothing else.`,
168562
- "Explaining the card"
168563
- );
168564
- };
168565
- var blastFileIn = (segment, path11) => segment?.blastDiagram?.nodes.find((node2) => node2.path === path11);
168566
- var findBlastFile = (artifact, { segmentId, path: path11 }) => blastFileIn(artifact.segments.find((segment) => segment.id === segmentId), path11);
168567
- var blastTargetOf = (artifact, target) => {
168568
- const isNamedSegment = artifact.segments.some((segment) => segment.id === target.segmentId);
168569
- if (artifact.isSegmented || !isNamedSegment || findBlastFile(artifact, target) !== void 0) return target;
168570
- const holder = artifact.segments.find((segment) => blastFileIn(segment, target.path) !== void 0);
168571
- return holder === void 0 ? target : { ...target, segmentId: holder.id };
168572
- };
168573
- var planBlastFile = (artifact, requested2) => {
168574
- const target = blastTargetOf(artifact, requested2);
168575
- const node2 = findBlastFile(artifact, target);
168576
- if (node2 === void 0) return rejected2("This file is not in the Blast Diagram.");
168577
- if (node2.diffType !== null) return rejected2("This file is part of the change, so it has no impact to assess.");
168578
- if (node2.impactType !== null) return ALREADY_WRITTEN;
168579
- return planned(
168580
- withRequiredBlastFile(artifact, target.segmentId, target.path),
168581
- `The reviewer asks for the impact of the change on ${quoted6(target.path)} in the Blast Diagram of segment ${quoted6(target.segmentId)} alone: write its impact type and its reason, and nothing else.`,
168582
- "Assessing the file"
168583
- );
168584
- };
168585
- var planNodeWriteUp = (artifact, target) => {
168586
- if (target.kind === "trace-node") return planTraceNode(artifact, target);
168587
- if (target.kind === "blast-file") return planBlastFile(artifact, target);
168588
- return planWaypoints(artifact, target);
168589
- };
168590
-
168591
- // apps/backend/src/procedures/request-queued-write-ups/utils/plan-queued-write-ups.ts
168592
- var SEVERAL_ASKS_LEAD = "The reviewer asked for these write-ups while you were busy. Do each one as it asks:";
168593
- var planQueuedWriteUps = (artifact, targets) => targets.reduce((queued, target) => {
168594
- const planned2 = planNodeWriteUp(queued.artifact, target);
168595
- return planned2.kind === "planned" ? { artifact: planned2.artifact, asks: [...queued.asks, planned2.ask] } : queued;
168596
- }, { artifact, asks: [] });
168597
- var queuedAmendmentOf = (queued) => ({
168598
- artifact: queued.asks.length === 0 ? null : queued.artifact,
168599
- outcome: queued.asks
168600
- });
168601
- var queuedAskOf = (asks) => asks.length === 1 ? asks[0] : [SEVERAL_ASKS_LEAD, ...asks.map((ask) => `- ${ask}`)].join("\n");
168602
-
168603
- // apps/backend/src/procedures/request-queued-write-ups/index.ts
168604
- var requestQueuedWriteUps = async (key, context) => {
168605
- const { agents, writeUpQueue, newId } = context.conversationEnvironment;
168606
- if (agents.find(key) !== null) return false;
168607
- const targets = writeUpQueue.take(key);
168608
- if (targets.length === 0) return false;
168609
- const repository = (await context.repositoryStore.list()).find((candidate) => candidate.name === key.repositoryName);
168610
- const target = repository === void 0 ? null : await findReviewTarget2(repository, key.targetId, context);
168611
- if (repository === void 0 || target === null) return false;
168612
- const asks = await context.reviewStore.amendArtifact(key, (artifact) => queuedAmendmentOf(planQueuedWriteUps(artifact, targets)));
168613
- if (asks === null || asks.length === 0) return false;
168614
- const body = buildWriteUpRequestPrompt(repository, target, key, queuedAskOf(asks));
168615
- await appendAndPublish(key, [{ kind: "reviewer-message", id: newId(), body, reference: null, sentAt: context.now() }], context);
168616
- return true;
168617
- };
168618
-
168619
- // apps/backend/src/procedures/deliver-to-agent/utils/choose-writer.ts
168620
- var writerOfRequest = (install, request) => request?.writer.kind === "agent" && request.writer.agent === install.agent ? { install: { ...install, model: request.writer.model }, effort: request.writer.effort } : { install, effort: install.effort };
168621
- var chooseWriter = (install, request, modelChoice) => modelChoice?.agent === install.agent ? { install: { ...install, model: modelChoice.model }, effort: modelChoice.effort } : writerOfRequest(install, request);
168622
-
168623
- // apps/backend/src/procedures/deliver-to-agent/utils/plan-delivery.ts
168624
- var LISTENER_GRACE_MS = 6e5;
168625
- var NO_AGENTS_REASON = "Finish the Agents step of the setup so Flight Plan can start an agent.";
168626
- var unspawnableReason = (agents) => {
168627
- const labels = agents.installs.map((install) => AGENT_LABELS[install.agent]).join(" or ");
168628
- return `Flight Plan can start only Claude. Start ${labels} yourself and have it listen for the reviewer.`;
168629
- };
168630
- var isListenerNear = (presence, now) => presence.openCount > 0 || presence.lastClosedAt !== null && now.getTime() - presence.lastClosedAt.getTime() < LISTENER_GRACE_MS;
168631
- var chooseInstall = (agents, spawnableAgents) => {
168632
- const candidates = agents.installs.filter((install) => spawnableAgents.includes(install.agent));
168633
- return candidates.find((install) => install.agent === agents.defaultAgent) ?? candidates.at(0) ?? null;
168634
- };
168635
- var planDelivery = (facts) => {
168636
- if (facts.pendingCount === 0) return { kind: "nothing-pending" };
168637
- if (facts.runningAgent !== null) return { kind: "agent-running" };
168638
- if (isListenerNear(facts.listenerPresence, facts.now)) return { kind: "listener-waiting" };
168639
- if (facts.target.kind === "rejected") return { kind: "unavailable", reason: facts.target.message };
168640
- if (facts.agents === null) return { kind: "unavailable", reason: NO_AGENTS_REASON };
168641
- const install = chooseInstall(facts.agents, facts.spawnableAgents);
168642
- return install === null ? { kind: "unavailable", reason: unspawnableReason(facts.agents) } : { kind: "spawn", install, workingPath: facts.target.workingPath };
168643
- };
168644
-
168645
- // apps/backend/src/procedures/deliver-to-agent/index.ts
168646
- var spawnableAgentsOf = (context) => AGENT_NAMES.filter((agent) => context.conversationEnvironment.spawners[agent] !== void 0);
168647
- var startingAgent = (key, agent, sessionId, now) => ({
168648
- key,
168649
- agent,
168650
- sessionId,
168651
- process: null,
168652
- phase: "starting",
168653
- startedAt: now,
168654
- lastActivityAt: now,
168655
- closing: null,
168656
- isAwaitingPermission: false,
168657
- wasReminded: false
168658
- });
168659
- var onExitOf = (key, sessionId, context) => (exit) => {
168660
- void handleAgentExit({ key, sessionId, exitCode: exit.exitCode }, context).then(async (outcome) => {
168661
- const hasQueuedWriteUps = await requestQueuedWriteUps(key, context);
168662
- if (outcome.shouldRedeliver || hasQueuedWriteUps) await deliverToAgent(key, context);
168663
- });
168664
- };
168665
- var launchAgent = async (spawning, sessionId, spawner, context) => {
168666
- const { key, conversation, pending, install, effort, workingPath } = spawning;
168667
- const { agents, newId } = context.conversationEnvironment;
168668
- const start2 = await spawner(
168669
- {
168670
- agent: install.agent,
168671
- installPath: install.installPath,
168672
- sessionId,
168673
- isResume: conversation.sessionId === sessionId,
168674
- workingPath,
168675
- model: install.model,
168676
- effort,
168677
- prompt: formatInbound(pending, conversation.journal),
168678
- instructions: spawnInstructionsOf(key)
168679
- },
168680
- onExitOf(key, sessionId, context)
168681
- );
168682
- if (start2.kind === "failed") {
168683
- agents.release(key, sessionId);
168684
- await recordAgentFailure(key, start2.reason, context);
168685
- return start2;
168686
- }
168687
- const started2 = agents.update(key, (agent) => ({ ...agent, process: start2.process }));
168688
- if (started2?.closing !== null) start2.process.stop();
168689
- await appendAndPublish(
168690
- key,
168691
- [{ kind: "delivery", id: newId(), throughSequence: lastSequenceOf(pending), channel: "spawn", deliveredAt: context.now() }],
168692
- context
168693
- );
168694
- return { kind: "spawned", sessionId };
168695
- };
168696
- var spawnAgent = async (spawning, context) => {
168697
- const { agents, spawners, newId, hub } = context.conversationEnvironment;
168698
- const spawner = spawners[spawning.install.agent];
168699
- if (spawner === void 0) return { kind: "unavailable", reason: `No spawner is registered for ${spawning.install.agent}.` };
168700
- const sessionId = spawning.conversation.sessionId ?? newId();
168701
- if (!agents.claim(startingAgent(spawning.key, spawning.install.agent, sessionId, context.now()))) {
168702
- return { kind: "agent-running" };
168703
- }
168704
- hub.publish(spawning.key);
168705
- return await launchAgent(spawning, sessionId, spawner, context);
168706
- };
168707
- var deliverToAgent = async (key, context) => {
168708
- const { store, agents } = context.conversationEnvironment;
168709
- const [conversation, setup, target, request] = await Promise.all([
168710
- store.read(key),
168711
- context.setupStore.read(),
168712
- findReviewTarget(key, context),
168713
- context.reviewStore.findRequest(key)
168714
- ]);
168715
- const pending = pendingInboundOf(conversation.journal);
168716
- const plan4 = planDelivery({
168717
- pendingCount: pending.length,
168718
- runningAgent: agents.find(key),
168719
- listenerPresence: agents.listenerPresence(key),
168720
- agents: setup.agents,
168721
- spawnableAgents: spawnableAgentsOf(context),
168722
- target,
168723
- now: context.now()
168724
- });
168725
- if (plan4.kind === "unavailable") await recordAgentFailure(key, plan4.reason, context);
168726
- if (plan4.kind !== "spawn") return plan4;
168727
- const writer = chooseWriter(plan4.install, request, latestModelChoiceOf(conversation.journal, plan4.install.agent));
168728
- return await spawnAgent({ key, conversation, pending, ...writer, workingPath: plan4.workingPath }, context);
168729
- };
168730
-
168731
168079
  // apps/backend/src/procedures/answer-questions/utils/plan-answers.ts
168732
168080
  var MISSING_BATCH_MESSAGE = "The agent did not ask these questions.";
168733
168081
  var SETTLED_MESSAGE = "These questions are closed. They were answered already, or the agent stopped waiting.";
@@ -168791,7 +168139,6 @@ var answerQuestions = async (input2, context) => {
168791
168139
  ],
168792
168140
  context
168793
168141
  );
168794
- if (batch.origin === "agent") await deliverToAgent(key, context);
168795
168142
  return { kind: "accepted" };
168796
168143
  };
168797
168144
 
@@ -168929,8 +168276,8 @@ var buildCodeReferenceRoute = publicProcedure.input(buildCodeReferenceInputSchem
168929
168276
  return reference2;
168930
168277
  });
168931
168278
 
168932
- // apps/backend/src/procedures/get-review/utils/start/startable-agents.ts
168933
- var startableAgentsOf = (spawners) => AGENT_NAMES.filter((agent) => spawners[agent] !== void 0);
168279
+ // apps/backend/src/procedures/get-review/utils/start/live-agents.ts
168280
+ var LIVE_AGENTS = ["claude"];
168934
168281
 
168935
168282
  // apps/backend/src/procedures/get-setup/utils/agent-models.ts
168936
168283
  var DEFAULT_EFFORT_OFFER = { effort: DEFAULT_EFFORT, label: "Default" };
@@ -168983,6 +168330,14 @@ var listModelsOf = async (agents, environment) => {
168983
168330
  var agentsWithCli = (detections) => detections.filter((detection) => detection.hasCli).map((detection) => detection.agent);
168984
168331
  var listOfferedModels = async (detections, choice, environment) => await listModelsOf([.../* @__PURE__ */ new Set([...agentsWithCli(detections), ...installedAgentsOf(choice)])], environment);
168985
168332
 
168333
+ // apps/backend/src/procedures/get-model-choice/utils/choose-writer.ts
168334
+ var writerOfRequest = (install, request) => request?.writer.kind === "agent" && request.writer.agent === install.agent ? { install: { ...install, model: request.writer.model }, effort: request.writer.effort } : { install, effort: install.effort };
168335
+ var chooseWriter = (install, request, modelChoice) => modelChoice?.agent === install.agent ? { install: { ...install, model: modelChoice.model }, effort: modelChoice.effort } : writerOfRequest(install, request);
168336
+ var chooseInstall = (agents, liveAgents) => {
168337
+ const candidates = agents.installs.filter((install) => liveAgents.includes(install.agent));
168338
+ return candidates.find((install) => install.agent === agents.defaultAgent) ?? candidates.at(0) ?? null;
168339
+ };
168340
+
168986
168341
  // apps/backend/src/procedures/get-model-choice/utils/chat-writer.ts
168987
168342
  var readChatWriter = async (key, context) => {
168988
168343
  const [setup, conversation, request] = await Promise.all([
@@ -168990,8 +168345,7 @@ var readChatWriter = async (key, context) => {
168990
168345
  context.conversationEnvironment.store.read(key),
168991
168346
  context.reviewStore.findRequest(key)
168992
168347
  ]);
168993
- const startableAgents = startableAgentsOf(context.conversationEnvironment.spawners);
168994
- const install = setup.agents === null ? null : chooseInstall(setup.agents, startableAgents);
168348
+ const install = setup.agents === null ? null : chooseInstall(setup.agents, LIVE_AGENTS);
168995
168349
  if (install === null) return null;
168996
168350
  const writer = chooseWriter(install, request, latestModelChoiceOf(conversation.journal, install.agent));
168997
168351
  const agentModels = await listModelsOf([install.agent], context.setupEnvironment);
@@ -169568,8 +168922,8 @@ var plan = (artifact, segments) => ({
169568
168922
  kind: "planned",
169569
168923
  artifact: { ...artifact, segments, isSegmented: artifact.isSegmented || segments.length > 1 }
169570
168924
  });
169571
- var quoted7 = (paths) => paths.map((path11) => `\`${path11}\``).join(", ");
169572
- var unchangedFilesMessage = (paths) => `${paths.length === 1 ? "This file is not a changed file" : "These files are not changed files"} of the Flight Plan: ${quoted7(paths)}.`;
168925
+ var quoted6 = (paths) => paths.map((path11) => `\`${path11}\``).join(", ");
168926
+ var unchangedFilesMessage = (paths) => `${paths.length === 1 ? "This file is not a changed file" : "These files are not changed files"} of the Flight Plan: ${quoted6(paths)}.`;
169573
168927
  var inOrder4 = (paths) => [...new Set(paths)].sort((one, two) => one.localeCompare(two));
169574
168928
  var normalize3 = ({ title, paths }) => ({ title: title.trim(), paths: inOrder4(paths) });
169575
168929
  var contentProblemOf = (artifact, { title, paths }) => {
@@ -170061,7 +169415,7 @@ var editWalkthroughRoute = publicProcedure.input(editWalkthroughInputSchema).out
170061
169415
  });
170062
169416
 
170063
169417
  // apps/backend/src/procedures/get-author-form/index.ts
170064
- var import_node_path20 = require("node:path");
169418
+ var import_node_path19 = require("node:path");
170065
169419
 
170066
169420
  // apps/backend/src/procedures/get-setup/utils/build-repositories-step.ts
170067
169421
  var COMMIT_SEARCH_PLACEHOLDER = "Type a name or email";
@@ -170123,7 +169477,7 @@ var getAuthorForm = async (input2, context) => {
170123
169477
  context.setupEnvironment.findRepositoryForge(path11, setup.forges),
170124
169478
  context.setupEnvironment.readGitIdentity(path11)
170125
169479
  ]);
170126
- return buildAuthorForm(forge, identity2, (0, import_node_path20.basename)(path11));
169480
+ return buildAuthorForm(forge, identity2, (0, import_node_path19.basename)(path11));
170127
169481
  };
170128
169482
 
170129
169483
  // apps/backend/src/procedures/get-setup/utils/forge-catalog.ts
@@ -170579,13 +169933,9 @@ var getConfigurationCreationFormRoute = publicProcedure.output(configurationCrea
170579
169933
 
170580
169934
  // apps/backend/src/procedures/get-conversation/utils/build-conversation.ts
170581
169935
  var parseMessage = (body) => parseProse(body, new Set(codeLinkIdsIn([body])));
170582
- var STATE_LABELS = {
170583
- "not-running": "Agent not running",
170584
- "starting": "Agent starting",
170585
- "working": "Agent working",
170586
- "waiting-for-reviewer": "Agent waiting for your answers",
170587
- "listening": "Agent listening",
170588
- "failed": "Agent could not start"
169936
+ var AGENT_STATE_LABELS = {
169937
+ online: "Agent online",
169938
+ offline: "Agent offline"
170589
169939
  };
170590
169940
  var REVIEWER_LABEL = "You";
170591
169941
  var AGENT_LABEL = "Agent";
@@ -170603,17 +169953,7 @@ var PANEL_LABELS = {
170603
169953
  placeholder: "Ask about this review",
170604
169954
  sendLabel: "Send"
170605
169955
  };
170606
- var EMPTY_MESSAGE = "Ask about this review. The agent starts with your first message.";
170607
- var runningStateOf = (agent) => {
170608
- if (agent.phase === "starting") return "starting";
170609
- return agent.isAwaitingPermission ? "waiting-for-reviewer" : "working";
170610
- };
170611
- var agentStateOf = ({ conversation, runningAgent, listenerPresence }) => {
170612
- if (runningAgent !== null && runningAgent.closing === null) return runningStateOf(runningAgent);
170613
- if (listenerPresence.openCount > 0) return "listening";
170614
- if (openQuestionBatchesOf(conversation.journal).length > 0) return "waiting-for-reviewer";
170615
- return latestFailureOf(conversation.journal) === null ? "not-running" : "failed";
170616
- };
169956
+ var EMPTY_MESSAGE = "Ask about this review. Your agent answers here once it is online.";
170617
169957
  var headingOf2 = (batch) => {
170618
169958
  if (batch.origin === "permission") return PERMISSION_HEADING;
170619
169959
  return batch.questions.length === 1 ? "The agent asks a question" : `The agent asks ${String(batch.questions.length)} questions`;
@@ -170687,30 +170027,25 @@ var toEntry = ({ event }, journal, now) => {
170687
170027
  };
170688
170028
  var buildConversation2 = (snapshot, now) => {
170689
170029
  const { journal } = snapshot.conversation;
170690
- const agentState = agentStateOf(snapshot);
170030
+ const agentState = snapshot.isAgentOnline ? "online" : "offline";
170691
170031
  const entries = journal.map((entry) => toEntry(entry, journal, now)).filter((entry) => entry !== null);
170692
170032
  return {
170693
170033
  ...PANEL_LABELS,
170694
170034
  emptyMessage: entries.length === 0 ? EMPTY_MESSAGE : null,
170695
170035
  agentState,
170696
- agentStateLabel: STATE_LABELS[agentState],
170697
- problem: agentState === "failed" ? latestFailureOf(journal)?.reason ?? null : null,
170036
+ agentStateLabel: AGENT_STATE_LABELS[agentState],
170698
170037
  entries
170699
170038
  };
170700
170039
  };
170701
170040
 
170702
170041
  // apps/backend/src/procedures/get-conversation/index.ts
170703
170042
  var getConversation = async (input2, context) => {
170704
- const { store, agents } = context.conversationEnvironment;
170705
- const conversation = await store.read(input2);
170706
- return buildConversation2(
170707
- { conversation, runningAgent: agents.find(input2), listenerPresence: agents.listenerPresence(input2) },
170708
- context.now()
170709
- );
170043
+ const conversation = await context.conversationEnvironment.store.read(input2);
170044
+ return buildConversation2({ conversation, isAgentOnline: isAgentOnlineFor(input2, context) }, context.now());
170710
170045
  };
170711
170046
 
170712
170047
  // apps/backend/src/procedures/get-conversation/utils/schemas.ts
170713
- var AGENT_STATES = ["not-running", "starting", "working", "waiting-for-reviewer", "listening", "failed"];
170048
+ var AGENT_STATES = ["online", "offline"];
170714
170049
  var QUESTIONS_STATUSES = ["open", "answered", "withdrawn"];
170715
170050
  var conversationKeySchema = external_exports.strictObject({
170716
170051
  repositoryName: external_exports.string().min(1),
@@ -170755,7 +170090,6 @@ var conversationViewSchema = external_exports.strictObject({
170755
170090
  sendLabel: external_exports.string(),
170756
170091
  agentState: external_exports.enum(AGENT_STATES),
170757
170092
  agentStateLabel: external_exports.string(),
170758
- problem: external_exports.string().nullable(),
170759
170093
  entries: external_exports.array(external_exports.discriminatedUnion("kind", [messageEntrySchema, questionsEntrySchema]))
170760
170094
  });
170761
170095
 
@@ -170867,9 +170201,9 @@ var getFileCodeRoute = publicProcedure.input(getFileCodeInputSchema).output(file
170867
170201
  // apps/backend/src/procedures/import-review/utils/import-request.ts
170868
170202
  var importIdOf = (request) => request.requestedAt.toISOString();
170869
170203
  var LISTENER_IMPORT_WAIT_MS = 6e5;
170870
- var isImportUnderway = (request, isAgentRunning, now) => {
170204
+ var isImportUnderway = (request, isAgentOnline2, now) => {
170871
170205
  if (request?.importedAt !== null) return false;
170872
- return request.channel === "agent" ? isAgentRunning : now.getTime() - request.requestedAt.getTime() < LISTENER_IMPORT_WAIT_MS;
170206
+ return request.channel === "agent" ? isAgentOnline2 : now.getTime() - request.requestedAt.getTime() < LISTENER_IMPORT_WAIT_MS;
170873
170207
  };
170874
170208
 
170875
170209
  // apps/backend/src/procedures/get-import-form/utils/build-import-form.ts
@@ -170963,7 +170297,7 @@ var getImportForm = async (input2, context) => {
170963
170297
  target,
170964
170298
  hasFlightPlan: artifact !== null,
170965
170299
  request,
170966
- isImporting: isImportUnderway(request, context.conversationEnvironment.agents.find(key) !== null, context.now())
170300
+ isImporting: isImportUnderway(request, isAgentOnlineFor(key, context), context.now())
170967
170301
  });
170968
170302
  };
170969
170303
 
@@ -170994,6 +170328,85 @@ var importFormSchema = external_exports.strictObject({
170994
170328
  // apps/backend/src/server/trpc/routes/get-import-form/index.ts
170995
170329
  var getImportFormRoute = publicProcedure.input(getImportFormInputSchema).output(importFormSchema).query(async ({ ctx, input: input2 }) => await getImportForm(input2, ctx));
170996
170330
 
170331
+ // apps/backend/src/procedures/get-write-up-plan/utils/default-writer.ts
170332
+ var defaultWriterOf = (agents, startableAgents) => {
170333
+ const install = agents?.installs.find((candidate) => candidate.agent === agents.defaultAgent) ?? agents?.installs.at(0);
170334
+ if (install === void 0) return null;
170335
+ return {
170336
+ agent: install.agent,
170337
+ label: AGENT_LABELS[install.agent],
170338
+ writer: startableAgents.includes(install.agent) ? { kind: "agent", agent: install.agent, model: install.model, effort: install.effort } : { kind: "external", agent: install.agent }
170339
+ };
170340
+ };
170341
+
170342
+ // apps/backend/src/procedures/get-live-agent/utils/build-live-agent.ts
170343
+ var externalLivePromptOf = (key) => `Use the flightplan-review skill and Flight Plan's MCP tools on repositoryName "${key.repositoryName}" and targetId "${key.targetId}". Write what the review still needs, answer the reviewer, and keep listening with the curl command the tools give you.`;
170344
+ var promptOf = (facts) => facts.isLiveAgent ? liveCommandOf(facts.key) : externalLivePromptOf(facts.key);
170345
+ var dialogOf = (facts) => {
170346
+ const agentLabel = AGENT_LABELS[facts.agent];
170347
+ return {
170348
+ title: `${agentLabel} is offline`,
170349
+ description: `${agentLabel} writes up this review and answers you in Chat. Start it in a terminal and leave it open while you review.`,
170350
+ terminal: facts.isLiveAgent && facts.canOpenTerminal ? { label: "Open a terminal for me", description: `Flight Plan opens Terminal in the repository's folder and starts ${agentLabel} there.` } : null,
170351
+ manual: {
170352
+ label: "I'll start it myself",
170353
+ description: `Start ${agentLabel} in the repository's folder and run this.`,
170354
+ prompt: promptOf(facts),
170355
+ copyLabel: "Copy the command",
170356
+ copiedMessage: "Command copied"
170357
+ },
170358
+ closeLabel: "Not now"
170359
+ };
170360
+ };
170361
+ var buildLiveAgent = (facts) => ({
170362
+ kind: "live",
170363
+ isOnline: facts.isOnline,
170364
+ statusLabel: `${AGENT_LABELS[facts.agent]} ${facts.isOnline ? "online" : "offline"}`,
170365
+ dialog: dialogOf(facts)
170366
+ });
170367
+
170368
+ // apps/backend/src/procedures/get-live-agent/index.ts
170369
+ var UNAVAILABLE = { kind: "unavailable" };
170370
+ var getLiveAgent = async (input2, context) => {
170371
+ const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
170372
+ const [target, setup] = await Promise.all([findReviewTarget(key, context), context.setupStore.read()]);
170373
+ const writer = defaultWriterOf(setup.agents, LIVE_AGENTS);
170374
+ if (target.kind === "rejected" || writer === null) return UNAVAILABLE;
170375
+ return buildLiveAgent({
170376
+ key,
170377
+ agent: writer.agent,
170378
+ isLiveAgent: writer.writer.kind === "agent",
170379
+ canOpenTerminal: context.openTerminal !== null,
170380
+ isOnline: isAgentOnlineFor(key, context)
170381
+ });
170382
+ };
170383
+
170384
+ // apps/backend/src/procedures/get-live-agent/utils/schemas.ts
170385
+ var getLiveAgentInputSchema = external_exports.strictObject({
170386
+ repositoryName: external_exports.string().min(1),
170387
+ targetId: external_exports.string().min(1)
170388
+ });
170389
+ var optionSchema = external_exports.strictObject({ label: external_exports.string(), description: external_exports.string() });
170390
+ var launchAgentDialogSchema = external_exports.strictObject({
170391
+ title: external_exports.string(),
170392
+ description: external_exports.string(),
170393
+ terminal: optionSchema.nullable(),
170394
+ manual: optionSchema.extend({ prompt: external_exports.string(), copyLabel: external_exports.string(), copiedMessage: external_exports.string() }),
170395
+ closeLabel: external_exports.string()
170396
+ });
170397
+ var liveAgentSchema = external_exports.discriminatedUnion("kind", [
170398
+ external_exports.strictObject({
170399
+ kind: external_exports.literal("live"),
170400
+ isOnline: external_exports.boolean(),
170401
+ statusLabel: external_exports.string(),
170402
+ dialog: launchAgentDialogSchema
170403
+ }),
170404
+ external_exports.strictObject({ kind: external_exports.literal("unavailable") })
170405
+ ]);
170406
+
170407
+ // apps/backend/src/server/trpc/routes/get-live-agent/index.ts
170408
+ var getLiveAgentRoute = publicProcedure.input(getLiveAgentInputSchema).output(liveAgentSchema).query(async ({ ctx, input: input2 }) => await getLiveAgent(input2, ctx));
170409
+
170997
170410
  // apps/backend/src/procedures/get-model-choice/utils/build-model-choice.ts
170998
170411
  var MINIMUM_OFFER_COUNT = 2;
170999
170412
  var NEXT_TURN_HINT = "The agent uses it from its next turn.";
@@ -171619,7 +171032,7 @@ var STEP_PURPOSES = {
171619
171032
  agents: "Configure how Flight Plan communicates with your agent",
171620
171033
  configuration: "Configure how Flight Plan puts your reviews together"
171621
171034
  };
171622
- var STATE_LABELS2 = {
171035
+ var STATE_LABELS = {
171623
171036
  saved: "Saved",
171624
171037
  current: "Current step",
171625
171038
  upcoming: "Not reached"
@@ -171663,7 +171076,7 @@ var toRouteStop = (setup, step, currentStep, agentModels) => {
171663
171076
  step,
171664
171077
  label: STEP_LABELS[step],
171665
171078
  state,
171666
- stateLabel: STATE_LABELS2[state],
171079
+ stateLabel: STATE_LABELS[state],
171667
171080
  isReachable: isStepReachable(setup, step),
171668
171081
  lines: [...SUMMARY_LINES[step](setup, agentModels) ?? [[plain5(STEP_PURPOSES[step])]]]
171669
171082
  };
@@ -171794,11 +171207,11 @@ var listRepositories = async (input2, context) => {
171794
171207
  var SEARCH_PLACEHOLDER = "Search by title or author";
171795
171208
  var NO_TARGETS_LABEL = "No other branches are listed in this repository.";
171796
171209
  var NO_MATCH_LABEL = "No pull request or branch matches.";
171797
- var WHITESPACE_PATTERN4 = /\s+/;
171210
+ var WHITESPACE_PATTERN3 = /\s+/;
171798
171211
  var headingOf3 = (repository) => `Branches in ${repository.name}`;
171799
171212
  var isListed = (currentTargetId) => (target) => DEFAULT_STATUSES.includes(target.branchStatus) || targetIdOf3(target) === currentTargetId;
171800
171213
  var searchTextOf2 = (target) => (target.kind === "pull-request" ? `${target.title} #${String(target.number)} ${target.branch} ${target.author}` : target.name).toLowerCase();
171801
- var termsOf2 = (query) => query.toLowerCase().split(WHITESPACE_PATTERN4).filter((term) => term !== "");
171214
+ var termsOf2 = (query) => query.toLowerCase().split(WHITESPACE_PATTERN3).filter((term) => term !== "");
171802
171215
  var matchesEvery2 = (terms) => (target) => {
171803
171216
  const searchText = searchTextOf2(target);
171804
171217
  return terms.every((term) => searchText.includes(term));
@@ -171900,17 +171313,6 @@ var buildWriteUpPlan = (source) => ({
171900
171313
  isSegmented: source.isSegmented
171901
171314
  });
171902
171315
 
171903
- // apps/backend/src/procedures/get-write-up-plan/utils/default-writer.ts
171904
- var defaultWriterOf = (agents, startableAgents) => {
171905
- const install = agents?.installs.find((candidate) => candidate.agent === agents.defaultAgent) ?? agents?.installs.at(0);
171906
- if (install === void 0) return null;
171907
- return {
171908
- agent: install.agent,
171909
- label: AGENT_LABELS[install.agent],
171910
- writer: startableAgents.includes(install.agent) ? { kind: "agent", agent: install.agent, model: install.model, effort: install.effort } : { kind: "external", agent: install.agent }
171911
- };
171912
- };
171913
-
171914
171316
  // apps/backend/src/procedures/get-write-up-plan/utils/write-up-counts.ts
171915
171317
  var SECTION_OF_TARGET = {
171916
171318
  "context": "context",
@@ -171946,7 +171348,7 @@ var getWriteUpPlan = async (input2, context) => {
171946
171348
  context.configurationStore.read()
171947
171349
  ]);
171948
171350
  if (artifact === null) return null;
171949
- const writer = defaultWriterOf(setup.agents, startableAgentsOf(context.conversationEnvironment.spawners));
171351
+ const writer = defaultWriterOf(setup.agents, LIVE_AGENTS);
171950
171352
  if (writer === null) return { kind: "no-agent", message: NO_AGENT_MESSAGE2 };
171951
171353
  const preferences = reviewPreferencesOf(setup.reviewPreferences);
171952
171354
  return buildWriteUpPlan({
@@ -172023,14 +171425,8 @@ var fetchingRequestOf = (key, artifactUrl, destinationPath) => [
172023
171425
  // apps/backend/src/procedures/import-review/index.ts
172024
171426
  var NOT_AN_ARTIFACT_MESSAGE3 = "That is not the address of a Claude artifact.";
172025
171427
  var STARTED_MESSAGE = "Asked your agent to fetch the Flight Plan.";
172026
- var NO_AGENT_PREFIX = "No agent could be asked to fetch it.";
172027
- var channelOf = (outcome) => {
172028
- if (outcome.kind === "spawned" || outcome.kind === "agent-running") return "agent";
172029
- return outcome.kind === "listener-waiting" ? "listener" : null;
172030
- };
172031
171428
  var refused2 = (message) => ({ importId: null, message });
172032
171429
  var started = (request) => ({ importId: importIdOf(request), message: STARTED_MESSAGE });
172033
- var reasonOf4 = (outcome) => outcome.kind === "unavailable" || outcome.kind === "failed" ? `${NO_AGENT_PREFIX} ${outcome.reason}` : NO_AGENT_PREFIX;
172034
171430
  var importReview = async (input2, context) => {
172035
171431
  if (!isClaudeArtifactUrl(input2.artifactUrl)) return refused2(NOT_AN_ARTIFACT_MESSAGE3);
172036
171432
  const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
@@ -172038,8 +171434,7 @@ var importReview = async (input2, context) => {
172038
171434
  if (target.kind === "rejected") return refused2(target.message);
172039
171435
  const requestedAt = context.now();
172040
171436
  const underway = await context.reviewStore.readImportRequest(key);
172041
- const isAgentRunning = context.conversationEnvironment.agents.find(key) !== null;
172042
- if (underway !== null && isImportUnderway(underway, isAgentRunning, requestedAt)) return started(underway);
171437
+ if (underway !== null && isImportUnderway(underway, isAgentOnlineFor(key, context), requestedAt)) return started(underway);
172043
171438
  await appendAndPublish(
172044
171439
  key,
172045
171440
  [{
@@ -172051,10 +171446,7 @@ var importReview = async (input2, context) => {
172051
171446
  }],
172052
171447
  context
172053
171448
  );
172054
- const outcome = await deliverToAgent(key, context);
172055
- const channel = channelOf(outcome);
172056
- if (channel === null) return refused2(reasonOf4(outcome));
172057
- const request = { artifactUrl: input2.artifactUrl, channel, requestedAt, importedAt: null };
171449
+ const request = { artifactUrl: input2.artifactUrl, channel: "agent", requestedAt, importedAt: null };
172058
171450
  await context.reviewStore.writeImportRequest(key, request);
172059
171451
  return started(request);
172060
171452
  };
@@ -172126,13 +171518,13 @@ var listCardBlocksRoute = publicProcedure.input(listCardBlocksInputSchema).outpu
172126
171518
  var listConfigurationsRoute = publicProcedure.output(configurationListSchema).query(async ({ ctx }) => listConfigurations(ctx));
172127
171519
 
172128
171520
  // apps/backend/src/procedures/list-recent-repositories/utils/build-recent-repositories.ts
172129
- var import_node_path21 = require("node:path");
171521
+ var import_node_path20 = require("node:path");
172130
171522
  var NO_RECENT_REPOSITORY_MESSAGE = "No repository in your home folder has a commit from you on main or master.";
172131
171523
  var toRecentRepository = ({ path: path11, activity }, addedPaths) => {
172132
171524
  const stateLabel = addedLabelOf(path11, addedPaths);
172133
171525
  return {
172134
171526
  path: path11,
172135
- name: (0, import_node_path21.basename)(path11),
171527
+ name: (0, import_node_path20.basename)(path11),
172136
171528
  branch: activity.branch,
172137
171529
  latestCommitAt: activity.latestCommitAt.toISOString(),
172138
171530
  isAdded: stateLabel !== null,
@@ -172250,19 +171642,70 @@ var markCompleteRoute = publicProcedure.input(markCompleteInputSchema).output(ma
172250
171642
  return result;
172251
171643
  });
172252
171644
 
171645
+ // apps/backend/src/procedures/open-agent-terminal/utils/launch-command.ts
171646
+ var FLIGHT_PLAN_TOOLS_RULE = `mcp__plugin_${PLUGIN_NAME}_${MCP_SERVER_NAME}__*`;
171647
+ var CLAUDE_EXECUTABLE = "claude";
171648
+ var SINGLE_QUOTE = "'";
171649
+ var ESCAPED_SINGLE_QUOTE = String.raw`'\''`;
171650
+ var quoteForShell = (text2) => `${SINGLE_QUOTE}${text2.replaceAll(SINGLE_QUOTE, ESCAPED_SINGLE_QUOTE)}${SINGLE_QUOTE}`;
171651
+ var claudeLiveArguments = (launch2) => [
171652
+ launch2.prompt,
171653
+ ...launch2.model === DEFAULT_MODEL ? [] : ["--model", launch2.model],
171654
+ ...launch2.effort === DEFAULT_EFFORT ? [] : ["--effort", launch2.effort],
171655
+ "--allowedTools",
171656
+ FLIGHT_PLAN_TOOLS_RULE,
171657
+ listenPermissionRuleOf(launch2.serverUrl)
171658
+ ];
171659
+ var buildLaunchCommand = (launch2) => [
171660
+ `cd ${quoteForShell(launch2.workingPath)}`,
171661
+ [CLAUDE_EXECUTABLE, ...claudeLiveArguments(launch2).map(quoteForShell)].join(" ")
171662
+ ].join(" && ");
171663
+
171664
+ // apps/backend/src/procedures/open-agent-terminal/index.ts
171665
+ var NO_TERMINAL_MESSAGE = "Flight Plan can open a terminal on macOS only. Copy the command and run it yourself.";
171666
+ var NO_LIVE_AGENT_MESSAGE = "Flight Plan can start only Claude in a terminal. Copy the command and run it yourself.";
171667
+ var rejected2 = (message) => ({ kind: "rejected", message });
171668
+ var failedMessageOf = (reason) => `Flight Plan could not open Terminal. ${reason}`;
171669
+ var openAgentTerminal = async (input2, context) => {
171670
+ const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
171671
+ const { openTerminal } = context;
171672
+ if (openTerminal === null) return rejected2(NO_TERMINAL_MESSAGE);
171673
+ const [target, setup, conversation, request] = await Promise.all([
171674
+ findReviewTarget(key, context),
171675
+ context.setupStore.read(),
171676
+ context.conversationEnvironment.store.read(key),
171677
+ context.reviewStore.findRequest(key)
171678
+ ]);
171679
+ if (target.kind === "rejected") return target;
171680
+ const install = setup.agents === null ? null : chooseInstall(setup.agents, LIVE_AGENTS);
171681
+ if (install === null) return rejected2(NO_LIVE_AGENT_MESSAGE);
171682
+ const writer = chooseWriter(install, request, latestModelChoiceOf(conversation.journal, install.agent));
171683
+ const opening = await openTerminal(buildLaunchCommand({
171684
+ workingPath: target.workingPath,
171685
+ prompt: liveCommandOf(key),
171686
+ model: writer.install.model,
171687
+ effort: writer.effort,
171688
+ serverUrl: context.conversationEnvironment.serverUrl
171689
+ }));
171690
+ return opening.kind === "opened" ? { kind: "opened", message: `${AGENT_LABELS[install.agent]} is starting in Terminal.` } : rejected2(failedMessageOf(opening.reason));
171691
+ };
171692
+
171693
+ // apps/backend/src/procedures/open-agent-terminal/utils/schemas.ts
171694
+ var openAgentTerminalInputSchema = external_exports.strictObject({
171695
+ repositoryName: external_exports.string().min(1),
171696
+ targetId: external_exports.string().min(1)
171697
+ });
171698
+ var agentTerminalOpeningSchema = external_exports.discriminatedUnion("kind", [
171699
+ external_exports.strictObject({ kind: external_exports.literal("opened"), message: external_exports.string() }),
171700
+ external_exports.strictObject({ kind: external_exports.literal("rejected"), message: external_exports.string() })
171701
+ ]);
171702
+
171703
+ // apps/backend/src/server/trpc/routes/open-agent-terminal/index.ts
171704
+ var openAgentTerminalRoute = publicProcedure.input(openAgentTerminalInputSchema).output(agentTerminalOpeningSchema).mutation(async ({ ctx, input: input2 }) => await openAgentTerminal(input2, ctx));
171705
+
172253
171706
  // apps/backend/src/procedures/publish-artifact/index.ts
172254
171707
  var REQUESTED_MESSAGE = "Asked your agent to publish it. The link shows under Share when it is done.";
172255
171708
  var PAGE_NOT_BUILT_MESSAGE = "This build of Flight Plan has no page to publish.";
172256
- var NO_AGENT_PREFIX2 = "No agent could be asked to publish it.";
172257
- var reportOf = (outcome) => outcome.kind === "unavailable" || outcome.kind === "failed" ? { message: `${NO_AGENT_PREFIX2} ${outcome.reason}` } : { message: REQUESTED_MESSAGE };
172258
- var channelOf2 = (outcome) => {
172259
- if (outcome.kind === "spawned" || outcome.kind === "agent-running") return "agent";
172260
- return outcome.kind === "listener-waiting" ? "listener" : null;
172261
- };
172262
- var requestOf2 = (outcome, requestedAt) => {
172263
- const channel = channelOf2(outcome);
172264
- return channel === null ? null : { requestedAt, channel };
172265
- };
172266
171709
  var attachToDescription = async (key, context) => {
172267
171710
  const [artifact, detail, publications] = await Promise.all([
172268
171711
  context.reviewStore.findArtifact(key),
@@ -172293,9 +171736,8 @@ var requestClaudeArtifact = async (key, context) => {
172293
171736
  }],
172294
171737
  context
172295
171738
  );
172296
- const outcome = await deliverToAgent(key, context);
172297
- await context.reviewStore.writePublishingRequest(key, requestOf2(outcome, requestedAt));
172298
- return reportOf(outcome);
171739
+ await context.reviewStore.writePublishingRequest(key, { requestedAt, channel: "agent" });
171740
+ return { message: REQUESTED_MESSAGE };
172299
171741
  };
172300
171742
  var publishArtifact = async (input2, context) => {
172301
171743
  const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
@@ -172395,33 +171837,83 @@ var requestReviewer = async (input2, context) => {
172395
171837
  var requestReviewerRoute = publicProcedure.input(reviewerLoginInputSchema).output(reviewActionOutputSchema).mutation(async ({ ctx, input: input2 }) => toActionOutput(await requestReviewer(input2, ctx)));
172396
171838
 
172397
171839
  // apps/backend/src/procedures/request-review/index.ts
172398
- var STARTED_MESSAGE2 = "Review started";
172399
171840
  var CREATED_MESSAGE = "Flight Plan created";
172400
171841
  var externalStartMessage = (agent) => `Run this line in ${AGENT_LABELS[agent]} to start explaining the review`;
172401
- var unstartedMessage = (reason) => `The review is ready to read, but the agent did not start: ${reason}`;
172402
- var startedMessageOf = (outcome) => {
172403
- if (outcome.kind === "failed" || outcome.kind === "unavailable") return unstartedMessage(outcome.reason);
172404
- return STARTED_MESSAGE2;
172405
- };
171842
+ var sentMessageOf = (agent) => `Sent to ${AGENT_LABELS[agent]}`;
172406
171843
  var sendToAgent = async (key, prompt, context) => {
172407
171844
  await appendAndPublish(
172408
171845
  key,
172409
171846
  [{ kind: "reviewer-message", id: context.conversationEnvironment.newId(), body: prompt, reference: null, sentAt: context.now() }],
172410
171847
  context
172411
171848
  );
172412
- return await deliverToAgent(key, context);
172413
171849
  };
172414
- var startAgent = async (key, prompt, context) => startedMessageOf(await sendToAgent(key, prompt, context));
172415
171850
  var handToWriter = async ({ key, repository, target, request }, context) => {
172416
171851
  const { writer } = request;
172417
171852
  if (writer.kind === "author" || writer.kind === "none") return { prompt: null, message: CREATED_MESSAGE };
172418
171853
  const prompt = buildReviewPrompt(repository, target, key, request.writeUp);
172419
- return writer.kind === "external" ? { prompt: toOneLine(prompt), message: externalStartMessage(writer.agent) } : { prompt: null, message: await startAgent(key, prompt, context) };
171854
+ if (writer.kind === "external") return { prompt: toOneLine(prompt), message: externalStartMessage(writer.agent) };
171855
+ await sendToAgent(key, prompt, context);
171856
+ return { prompt: null, message: sentMessageOf(writer.agent) };
171857
+ };
171858
+
171859
+ // apps/backend/src/procedures/request-write-up/utils/plan-node-write-up.ts
171860
+ var DRAFTING_STATUS3 = "drafting";
171861
+ var ALREADY_WRITTEN = { kind: "written", message: "This is already explained." };
171862
+ var rejected3 = (message) => ({ kind: "rejected", field: "target", message });
171863
+ var planned = (artifact, ask, message) => ({ kind: "planned", artifact: { ...artifact, status: DRAFTING_STATUS3 }, ask, message });
171864
+ var quoted7 = (text2) => `\`${text2}\``;
171865
+ var listWaypoints3 = (artifact) => artifact.segments.flatMap(waypointsOf).map(({ waypoint }) => waypoint);
171866
+ var isWaypointUnwritten = (waypoint) => waypoint.title === null || waypoint.reading.trim() === "";
171867
+ var waypointIdsOf = (artifact, target) => target.kind === "waypoint" ? listWaypoints3(artifact).filter((waypoint) => waypoint.id === target.waypointId).map((waypoint) => waypoint.id) : listRootBlockWaypointIds(artifact, target.rootBlockId);
171868
+ var waypointAskOf = (ids) => ids.length === 1 ? `The reviewer asks for the write-up of the waypoint ${quoted7(ids[0])} alone: write its title and its reading, and nothing else.` : `The reviewer asks for the write-up of the waypoints ${joinedList2(ids.map(quoted7))} alone: write their titles and readings, and nothing else.`;
171869
+ var planWaypoints = (artifact, target) => {
171870
+ const waypointIds = waypointIdsOf(artifact, target);
171871
+ if (waypointIds.length === 0) {
171872
+ return rejected3(target.kind === "waypoint" ? "This waypoint is not in the review." : "This code block has no waypoint in the review.");
171873
+ }
171874
+ const unwrittenIds = listWaypoints3(artifact).filter((waypoint) => waypointIds.includes(waypoint.id) && isWaypointUnwritten(waypoint)).map((waypoint) => waypoint.id);
171875
+ if (unwrittenIds.length === 0) return ALREADY_WRITTEN;
171876
+ return planned(withRequiredWaypoints(artifact, unwrittenIds), waypointAskOf(unwrittenIds), `Explaining ${countLabel2(unwrittenIds.length, "waypoint", "waypoints")}`);
171877
+ };
171878
+ var isTraceNodeUnwritten = (node2) => node2.label === null || node2.triggerLabels.some((trigger) => trigger.label === null) || node2.blockLabels.some((listed) => listed.label === null);
171879
+ var findTraceNode = (artifact, { segmentId, nodeId }) => listTraceNodes(artifact.segments.find((segment) => segment.id === segmentId)?.traceDiagram?.traceNodes ?? []).find((node2) => node2.id === nodeId);
171880
+ var planTraceNode = (artifact, target) => {
171881
+ const node2 = findTraceNode(artifact, target);
171882
+ if (node2 === void 0) return rejected3("This card is not in the Trace Diagram.");
171883
+ if (!isTraceNodeUnwritten(node2)) return ALREADY_WRITTEN;
171884
+ return planned(
171885
+ withRequiredCard(artifact, target.segmentId, target.nodeId),
171886
+ `The reviewer asks for the write-up of the Trace Diagram node ${quoted7(target.nodeId)} of segment ${quoted7(target.segmentId)} alone: write its label and the labels of its trigger labels and listed blocks, and nothing else.`,
171887
+ "Explaining the card"
171888
+ );
171889
+ };
171890
+ var blastFileIn = (segment, path11) => segment?.blastDiagram?.nodes.find((node2) => node2.path === path11);
171891
+ var findBlastFile = (artifact, { segmentId, path: path11 }) => blastFileIn(artifact.segments.find((segment) => segment.id === segmentId), path11);
171892
+ var blastTargetOf = (artifact, target) => {
171893
+ const isNamedSegment = artifact.segments.some((segment) => segment.id === target.segmentId);
171894
+ if (artifact.isSegmented || !isNamedSegment || findBlastFile(artifact, target) !== void 0) return target;
171895
+ const holder = artifact.segments.find((segment) => blastFileIn(segment, target.path) !== void 0);
171896
+ return holder === void 0 ? target : { ...target, segmentId: holder.id };
171897
+ };
171898
+ var planBlastFile = (artifact, requested2) => {
171899
+ const target = blastTargetOf(artifact, requested2);
171900
+ const node2 = findBlastFile(artifact, target);
171901
+ if (node2 === void 0) return rejected3("This file is not in the Blast Diagram.");
171902
+ if (node2.diffType !== null) return rejected3("This file is part of the change, so it has no impact to assess.");
171903
+ if (node2.impactType !== null) return ALREADY_WRITTEN;
171904
+ return planned(
171905
+ withRequiredBlastFile(artifact, target.segmentId, target.path),
171906
+ `The reviewer asks for the impact of the change on ${quoted7(target.path)} in the Blast Diagram of segment ${quoted7(target.segmentId)} alone: write its impact type and its reason, and nothing else.`,
171907
+ "Assessing the file"
171908
+ );
171909
+ };
171910
+ var planNodeWriteUp = (artifact, target) => {
171911
+ if (target.kind === "trace-node") return planTraceNode(artifact, target);
171912
+ if (target.kind === "blast-file") return planBlastFile(artifact, target);
171913
+ return planWaypoints(artifact, target);
172420
171914
  };
172421
171915
 
172422
171916
  // apps/backend/src/procedures/request-write-up/index.ts
172423
- var QUEUED_MESSAGE = "The agent is still writing. If this is still missing when it finishes, Flight Plan asks for it next.";
172424
- var unstartedMessage2 = (reason) => `The agent did not start: ${reason}`;
172425
171917
  var amendmentOf = (planned2) => ({
172426
171918
  artifact: planned2.kind === "planned" ? planned2.artifact : null,
172427
171919
  outcome: planned2
@@ -172433,9 +171925,8 @@ var writeWriter = async (key, writer, context) => {
172433
171925
  var handOver = async (key, writer, prompt, message, context) => {
172434
171926
  if (writer.kind === "external") return { kind: "started", prompt: toOneLine(prompt), message: externalStartMessage(writer.agent) };
172435
171927
  await writeWriter(key, writer, context);
172436
- const outcome = await sendToAgent(key, prompt, context);
172437
- const isStarted = outcome.kind !== "failed" && outcome.kind !== "unavailable";
172438
- return { kind: "started", prompt: null, message: isStarted ? message : unstartedMessage2(outcome.reason) };
171928
+ await sendToAgent(key, prompt, context);
171929
+ return { kind: "started", prompt: null, message };
172439
171930
  };
172440
171931
  var requestWriteUp = async (input2, context) => {
172441
171932
  const key = { repositoryName: input2.repositoryName, targetId: input2.targetId };
@@ -172448,16 +171939,11 @@ var requestWriteUp = async (input2, context) => {
172448
171939
  if (repository === void 0 || scaffold === null) return null;
172449
171940
  const target = await findReviewTarget2(repository, input2.targetId, context);
172450
171941
  if (target === null) return null;
172451
- const writer = defaultWriterOf(setup.agents, startableAgentsOf(context.conversationEnvironment.spawners))?.writer ?? null;
171942
+ const writer = defaultWriterOf(setup.agents, LIVE_AGENTS)?.writer ?? null;
172452
171943
  if (writer === null) return { kind: "rejected", field: "agent", message: NO_AGENT_MESSAGE2 };
172453
171944
  const planned2 = await context.reviewStore.amendArtifact(key, (artifact) => amendmentOf(planNodeWriteUp(artifact, input2.target)));
172454
171945
  if (planned2 === null) return null;
172455
171946
  if (planned2.kind !== "planned") return planned2;
172456
- const { agents, writeUpQueue } = context.conversationEnvironment;
172457
- if (writer.kind !== "external" && agents.find(key) !== null) {
172458
- writeUpQueue.add(key, input2.target);
172459
- return { kind: "queued", message: QUEUED_MESSAGE };
172460
- }
172461
171947
  return await handOver(key, writer, buildWriteUpRequestPrompt(repository, target, key, planned2.ask), planned2.message, context);
172462
171948
  };
172463
171949
 
@@ -172476,7 +171962,6 @@ var requestWriteUpInputSchema = external_exports.strictObject({
172476
171962
  var WRITE_UP_REQUEST_FIELDS = ["target", "agent"];
172477
171963
  var writeUpRequestResultSchema = external_exports.discriminatedUnion("kind", [
172478
171964
  external_exports.strictObject({ kind: external_exports.literal("started"), prompt: external_exports.string().nullable(), message: external_exports.string() }),
172479
- external_exports.strictObject({ kind: external_exports.literal("queued"), message: external_exports.string() }),
172480
171965
  external_exports.strictObject({ kind: external_exports.literal("written"), message: external_exports.string() }),
172481
171966
  external_exports.strictObject({ kind: external_exports.literal("rejected"), field: external_exports.enum(WRITE_UP_REQUEST_FIELDS), message: external_exports.string() })
172482
171967
  ]);
@@ -172509,14 +171994,14 @@ var resolveThreadRoute = publicProcedure.input(resolveThreadInputSchema).output(
172509
171994
  var NO_FIELD_MESSAGE = "This configuration has no such field. Nothing was saved.";
172510
171995
  var EMPTY_TEXT_MESSAGE = "An instruction cannot be empty. Write it, or reset the field.";
172511
171996
  var PRESET_NAME_MESSAGE = "A preset keeps its name. Duplicate it to name your own.";
172512
- var rejected3 = (message) => ({ kind: "rejected", message });
171997
+ var rejected4 = (message) => ({ kind: "rejected", message });
172513
171998
  var withoutKeys3 = (instructions, keys) => Object.fromEntries(Object.entries(instructions).filter(([key]) => !keys.includes(key)));
172514
171999
  var planFieldChange = (configuration, change, texts) => {
172515
172000
  const field = EDITABLE_FIELDS.find((editable) => editable.path === change.path);
172516
- if (field === void 0) return rejected3(NO_FIELD_MESSAGE);
172001
+ if (field === void 0) return rejected4(NO_FIELD_MESSAGE);
172517
172002
  const fieldKeys = presetKeysOf(field);
172518
- if (change.texts.some((written) => !fieldKeys.includes(written.key))) return rejected3(NO_FIELD_MESSAGE);
172519
- if (change.texts.some((written) => written.text.trim() === "")) return rejected3(EMPTY_TEXT_MESSAGE);
172003
+ if (change.texts.some((written) => !fieldKeys.includes(written.key))) return rejected4(NO_FIELD_MESSAGE);
172004
+ if (change.texts.some((written) => written.text.trim() === "")) return rejected4(EMPTY_TEXT_MESSAGE);
172520
172005
  const changedTexts = change.texts.filter((written) => written.text !== shippedTextOf(texts, configuration, written.key));
172521
172006
  return {
172522
172007
  kind: "planned",
@@ -172532,7 +172017,7 @@ var planFieldChange = (configuration, change, texts) => {
172532
172017
  };
172533
172018
  var planReset = (configuration, change) => {
172534
172019
  const field = EDITABLE_FIELDS.find((editable) => editable.path === change.path);
172535
- if (field === void 0) return rejected3(NO_FIELD_MESSAGE);
172020
+ if (field === void 0) return rejected4(NO_FIELD_MESSAGE);
172536
172021
  return {
172537
172022
  kind: "planned",
172538
172023
  message: "This field is reset.",
@@ -172548,14 +172033,14 @@ var planSwitches = (configuration, change) => ({
172548
172033
  }
172549
172034
  });
172550
172035
  var planRename = (configuration, change, otherConfigurations) => {
172551
- if (configuration.isPreset) return rejected3(PRESET_NAME_MESSAGE);
172036
+ if (configuration.isPreset) return rejected4(PRESET_NAME_MESSAGE);
172552
172037
  const name = change.name.trim();
172553
172038
  const nameProblem = nameProblemOf(name, otherConfigurations);
172554
- return nameProblem === null ? { kind: "planned", message: "The name is saved.", configuration: { ...configuration, name } } : rejected3(nameProblem);
172039
+ return nameProblem === null ? { kind: "planned", message: "The name is saved.", configuration: { ...configuration, name } } : rejected4(nameProblem);
172555
172040
  };
172556
172041
  var planChange2 = (configurations2, change, texts, missingMessage) => {
172557
172042
  const configuration = configurations2.find((listed) => listed.id === change.id);
172558
- if (configuration === void 0) return rejected3(missingMessage);
172043
+ if (configuration === void 0) return rejected4(missingMessage);
172559
172044
  if (change.change === "field") return planFieldChange(configuration, change, texts);
172560
172045
  if (change.change === "reset") return planReset(configuration, change);
172561
172046
  if (change.change === "switches") return planSwitches(configuration, change);
@@ -173142,7 +172627,7 @@ var searchCreationTargetsRoute = publicProcedure.input(searchCreationTargetsInpu
173142
172627
  // apps/backend/src/procedures/search-files/utils/fuzzy-score.ts
173143
172628
  var PATH_SEPARATOR18 = "/";
173144
172629
  var WORD_START_PATTERN = /[/\\._\-\s]/;
173145
- var WHITESPACE_PATTERN5 = /\s+/g;
172630
+ var WHITESPACE_PATTERN4 = /\s+/g;
173146
172631
  var MATCH_SCORE = 1;
173147
172632
  var CONSECUTIVE_BONUS = 2;
173148
172633
  var WORD_START_BONUS = 3;
@@ -173170,7 +172655,7 @@ var scoreSubsequence = (needle, text2) => {
173170
172655
  };
173171
172656
  var fileNameOf2 = (path11) => path11.slice(path11.lastIndexOf(PATH_SEPARATOR18) + 1);
173172
172657
  var fuzzyScoreOf = (query, path11) => {
173173
- const needle = query.replaceAll(WHITESPACE_PATTERN5, "").toLowerCase();
172658
+ const needle = query.replaceAll(WHITESPACE_PATTERN4, "").toLowerCase();
173174
172659
  const fileNameScore = scoreSubsequence(needle, fileNameOf2(path11));
173175
172660
  const score = fileNameScore === null ? scoreSubsequence(needle, path11) : fileNameScore + FILE_NAME_BONUS;
173176
172661
  return score === null ? null : score - path11.length * LENGTH_PENALTY;
@@ -173299,15 +172784,15 @@ var ASK_FOR_MORE_CHARACTERS = {
173299
172784
  var hasEnoughCharacters = (query) => query.trim().length >= MIN_QUERY_LENGTH;
173300
172785
 
173301
172786
  // apps/backend/src/procedures/search-repository-folders/utils/rank-repository-folders.ts
173302
- var import_node_path22 = require("node:path");
172787
+ var import_node_path21 = require("node:path");
173303
172788
  var PARENT_SEGMENT2 = "..";
173304
172789
  var pathBelowRoot = (path11, rootPath) => {
173305
- const relativePath = (0, import_node_path22.relative)(rootPath, path11);
173306
- const isOutsideRoot = relativePath.startsWith(PARENT_SEGMENT2) || (0, import_node_path22.isAbsolute)(relativePath);
172790
+ const relativePath = (0, import_node_path21.relative)(rootPath, path11);
172791
+ const isOutsideRoot = relativePath.startsWith(PARENT_SEGMENT2) || (0, import_node_path21.isAbsolute)(relativePath);
173307
172792
  return isOutsideRoot ? path11 : relativePath;
173308
172793
  };
173309
172794
  var matchedLettersInFolderName = (folderPath, matchedPositions) => {
173310
- const folderNameStart = folderPath.lastIndexOf(import_node_path22.sep) + 1;
172795
+ const folderNameStart = folderPath.lastIndexOf(import_node_path21.sep) + 1;
173311
172796
  return [...matchedPositions].filter((position) => position >= folderNameStart).length;
173312
172797
  };
173313
172798
  var byMostLettersInFolderName = (first, second, selector) => matchedLettersInFolderName(selector(second.item), second.positions) - matchedLettersInFolderName(selector(first.item), first.positions);
@@ -173403,7 +172888,6 @@ var sendReviewerMessage = async (input2, context) => {
173403
172888
  [{ kind: "reviewer-message", id: messageId, body: input2.body, reference: input2.reference, sentAt: context.now() }],
173404
172889
  context
173405
172890
  );
173406
- await deliverToAgent(key, context);
173407
172891
  return { kind: "sent", messageId };
173408
172892
  };
173409
172893
 
@@ -173566,8 +173050,8 @@ var watchConversationRoute = publicProcedure.input(conversationKeySchema).subscr
173566
173050
  var MISSING_CONFIGURATION_MESSAGE3 = "This configuration no longer exists. Pick another one.";
173567
173051
  var NO_SECTION_MESSAGE = "Choose at least one part for the agent to explain.";
173568
173052
  var DRAFTING_STATUS4 = "drafting";
173569
- var rejected4 = (field, message) => ({ kind: "rejected", field, message });
173570
- var requestOf3 = (previous, input2, configuration, writer, now) => ({
173053
+ var rejected5 = (field, message) => ({ kind: "rejected", field, message });
173054
+ var requestOf2 = (previous, input2, configuration, writer, now) => ({
173571
173055
  ...previous,
173572
173056
  configurationId: configuration.id,
173573
173057
  preset: configuration.preset,
@@ -173576,9 +173060,9 @@ var requestOf3 = (previous, input2, configuration, writer, now) => ({
173576
173060
  writeUp: { sections: input2.sections, walkthroughOrder: input2.walkthroughOrder }
173577
173061
  });
173578
173062
  var planWriteUp = (input2, { scaffold, configuration, writer, noAgentMessage, now }) => {
173579
- if (input2.sections.length === 0) return rejected4("sections", NO_SECTION_MESSAGE);
173580
- if (configuration === null) return rejected4("configuration", MISSING_CONFIGURATION_MESSAGE3);
173581
- if (writer === null) return rejected4("agent", noAgentMessage);
173063
+ if (input2.sections.length === 0) return rejected5("sections", NO_SECTION_MESSAGE);
173064
+ if (configuration === null) return rejected5("configuration", MISSING_CONFIGURATION_MESSAGE3);
173065
+ if (writer === null) return rejected5("agent", noAgentMessage);
173582
173066
  const configured = withConfiguration({ ...scaffold.artifact, isSegmented: input2.isSegmented }, { requiredContextKinds: configuration.contextCodeBlockKinds });
173583
173067
  const required2 = withRequiredSections(configured, input2.sections);
173584
173068
  const hasWork = unwrittenWork(required2).length > 0 || input2.sections.includes("findings");
@@ -173587,7 +173071,7 @@ var planWriteUp = (input2, { scaffold, configuration, writer, noAgentMessage, no
173587
173071
  hasWork,
173588
173072
  scaffold: {
173589
173073
  artifact: hasWork ? { ...required2, status: DRAFTING_STATUS4 } : required2,
173590
- request: requestOf3(scaffold.request, input2, configuration, writer, now),
173074
+ request: requestOf2(scaffold.request, input2, configuration, writer, now),
173591
173075
  baseCommit: scaffold.baseCommit
173592
173076
  }
173593
173077
  };
@@ -173610,7 +173094,7 @@ var writeUpReview = async (input2, context) => {
173610
173094
  const planned2 = planWriteUp(input2, {
173611
173095
  scaffold,
173612
173096
  configuration: findConfiguration(savedConfigurations, input2.configurationId),
173613
- writer: defaultWriterOf(setup.agents, startableAgentsOf(context.conversationEnvironment.spawners))?.writer ?? null,
173097
+ writer: defaultWriterOf(setup.agents, LIVE_AGENTS)?.writer ?? null,
173614
173098
  noAgentMessage: NO_AGENT_MESSAGE2,
173615
173099
  now: context.now()
173616
173100
  });
@@ -173698,6 +173182,8 @@ var appRouter = createRouter({
173698
173182
  writeUpPlan: getWriteUpPlanRoute,
173699
173183
  writeUp: writeUpReviewRoute,
173700
173184
  declineWriteUp: declineWriteUpRoute,
173185
+ liveAgent: getLiveAgentRoute,
173186
+ openAgentTerminal: openAgentTerminalRoute,
173701
173187
  requestWriteUp: requestWriteUpRoute,
173702
173188
  delete: deleteReviewRoute,
173703
173189
  setReviewed: setReviewedRoute,
@@ -173771,7 +173257,9 @@ var buildServer = async (options = {}, context = createMockProcedureContext(/* @
173771
173257
  await registerTrpc(server, context);
173772
173258
  registerMcp(server, context);
173773
173259
  await registerApi(server, context);
173774
- registerAgentLifecycle(server, context);
173260
+ server.addHook("onClose", async () => {
173261
+ await context.conversationEnvironment.hub.close();
173262
+ });
173775
173263
  if (webPath !== null) await registerWeb(server, webPath);
173776
173264
  return server;
173777
173265
  };
@@ -173779,10 +173267,10 @@ var buildServer = async (options = {}, context = createMockProcedureContext(/* @
173779
173267
  // apps/backend/src/utils/browser-opener/index.ts
173780
173268
  var import_node_child_process6 = require("node:child_process");
173781
173269
  var WINDOWS = "win32";
173782
- var MAC = "darwin";
173270
+ var MAC2 = "darwin";
173783
173271
  var EMPTY_WINDOW_TITLE = "";
173784
173272
  var browserCommandOf = (platform, url2) => {
173785
- if (platform === MAC) return { executable: "open", commandArguments: [url2] };
173273
+ if (platform === MAC2) return { executable: "open", commandArguments: [url2] };
173786
173274
  if (platform === WINDOWS) return { executable: "cmd", commandArguments: ["/c", "start", EMPTY_WINDOW_TITLE, url2] };
173787
173275
  return { executable: "xdg-open", commandArguments: [url2] };
173788
173276
  };
@@ -173801,7 +173289,7 @@ var openBrowser = async (platform, url2) => {
173801
173289
  };
173802
173290
 
173803
173291
  // apps/backend/src/utils/port-probe/index.ts
173804
- var import_promises11 = require("node:dns/promises");
173292
+ var import_promises10 = require("node:dns/promises");
173805
173293
  var import_node_net2 = require("node:net");
173806
173294
  var ANY_PORT = 0;
173807
173295
  var isAddressFree = async (address, port) => await new Promise((resolve3) => {
@@ -173815,7 +173303,7 @@ var isAddressFree = async (address, port) => await new Promise((resolve3) => {
173815
173303
  });
173816
173304
  });
173817
173305
  });
173818
- var addressesOf = async (host) => (await (0, import_promises11.lookup)(host, { all: true })).map(({ address }) => address);
173306
+ var addressesOf = async (host) => (await (0, import_promises10.lookup)(host, { all: true })).map(({ address }) => address);
173819
173307
  var isPortFree = async (host, port) => {
173820
173308
  const addresses = await addressesOf(host);
173821
173309
  const checks = await Promise.all(addresses.map(async (address) => await isAddressFree(address, port)));
@@ -173847,7 +173335,7 @@ var isFlightPlanRunning = async (serverUrl) => {
173847
173335
  };
173848
173336
 
173849
173337
  // apps/backend/src/utils/saved-port-file/index.ts
173850
- var import_node_path23 = require("node:path");
173338
+ var import_node_path22 = require("node:path");
173851
173339
  var FILE_NAME2 = "server.json";
173852
173340
  var JSON_INDENT9 = 2;
173853
173341
  var savedPortSchema = external_exports.object({ port: external_exports.number().int().positive() });
@@ -173861,7 +173349,7 @@ var parseSavedPort = (text2) => {
173861
173349
  }
173862
173350
  };
173863
173351
  var createSavedPortFile = (dataPath) => {
173864
- const filePath = (0, import_node_path23.join)(dataPath, FILE_NAME2);
173352
+ const filePath = (0, import_node_path22.join)(dataPath, FILE_NAME2);
173865
173353
  return {
173866
173354
  read: async () => parseSavedPort(await readTextOrNull(filePath)),
173867
173355
  write: async (port) => {
@@ -173874,10 +173362,10 @@ var createSavedPortFile = (dataPath) => {
173874
173362
  // apps/backend/src/main.ts
173875
173363
  var PLUGIN_FOLDER_NAME = "plugin";
173876
173364
  var CONFIGURATIONS_FOLDER_NAME2 = "configurations";
173877
- var PUBLISHED_BUNDLE_PATH_FROM_BUILD = (0, import_node_path24.join)("..", "published-review");
173878
- var WEB_PATH_FROM_BUILD = (0, import_node_path24.join)("..", "frontend");
173879
- var PACKAGE_MANIFEST_PATH_FROM_BUILD = (0, import_node_path24.join)("..", "package.json");
173880
- var FIXTURE_CASES_PATH_FROM_BUILD = (0, import_node_path24.join)("..", "..", "..", "tools", "playground-fixtures", "test-cases");
173365
+ var PUBLISHED_BUNDLE_PATH_FROM_BUILD = (0, import_node_path23.join)("..", "published-review");
173366
+ var WEB_PATH_FROM_BUILD = (0, import_node_path23.join)("..", "frontend");
173367
+ var PACKAGE_MANIFEST_PATH_FROM_BUILD = (0, import_node_path23.join)("..", "package.json");
173368
+ var FIXTURE_CASES_PATH_FROM_BUILD = (0, import_node_path23.join)("..", "..", "..", "tools", "playground-fixtures", "test-cases");
173881
173369
  var PUBLISHED_LOG_LEVEL = "warn";
173882
173370
  var ARGUMENTS_START = 2;
173883
173371
  var FAILURE_EXIT_CODE2 = 1;
@@ -173887,13 +173375,14 @@ var openLocalContext = (serverConfig, appUrl) => {
173887
173375
  const homePath = (0, import_node_os.homedir)();
173888
173376
  return createLocalProcedureContext(/* @__PURE__ */ new Date(), {
173889
173377
  dataPath: readDataPath(environment, homePath),
173890
- pluginPath: (0, import_node_path24.join)(__dirname, PLUGIN_FOLDER_NAME),
173891
- configurationsPath: (0, import_node_path24.join)(__dirname, CONFIGURATIONS_FOLDER_NAME2),
173892
- publishedBundlePath: (0, import_node_path24.join)(__dirname, PUBLISHED_BUNDLE_PATH_FROM_BUILD),
173378
+ pluginPath: (0, import_node_path23.join)(__dirname, PLUGIN_FOLDER_NAME),
173379
+ configurationsPath: (0, import_node_path23.join)(__dirname, CONFIGURATIONS_FOLDER_NAME2),
173380
+ publishedBundlePath: (0, import_node_path23.join)(__dirname, PUBLISHED_BUNDLE_PATH_FROM_BUILD),
173893
173381
  mcpUrl: mcpUrlOf(serverConfig),
173894
173382
  serverUrl: serverUrlOf(serverConfig),
173895
173383
  appUrl,
173896
173384
  homePath,
173385
+ platform: process.platform,
173897
173386
  environment
173898
173387
  });
173899
173388
  };
@@ -173910,7 +173399,7 @@ var choosePort = async (host, choice) => {
173910
173399
  return null;
173911
173400
  };
173912
173401
  var readPackageVersion = async () => {
173913
- const manifest = JSON.parse(await (0, import_promises12.readFile)((0, import_node_path24.join)(__dirname, PACKAGE_MANIFEST_PATH_FROM_BUILD), "utf8"));
173402
+ const manifest = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path23.join)(__dirname, PACKAGE_MANIFEST_PATH_FROM_BUILD), "utf8"));
173914
173403
  return packageManifestSchema.parse(manifest).version;
173915
173404
  };
173916
173405
  var readNoVersion = () => null;
@@ -173927,7 +173416,7 @@ var printUpdateNotice = async (pendingNotice) => {
173927
173416
  var refreshAgents = async (context) => {
173928
173417
  const failures = await refreshAgentInstalls(context);
173929
173418
  failures.forEach(({ agent, reason }) => {
173930
- console.error(`${agent} still points at the previous address. ${reason} Save the agents again in Settings.`);
173419
+ console.error(`${agent} could not be brought up to date with this Flight Plan. ${reason} Save the agents again in Settings.`);
173931
173420
  });
173932
173421
  return failures.length === 0;
173933
173422
  };
@@ -173954,12 +173443,13 @@ var launch = async (command) => {
173954
173443
  const serverConfig = { host, port };
173955
173444
  const serverUrl = serverUrlOf(serverConfig);
173956
173445
  const context = { ...openLocalContext(serverConfig, serverUrl), updateChecker: await pendingChecker };
173957
- const server = await buildServer({ logger: { level: PUBLISHED_LOG_LEVEL } }, context, (0, import_node_path24.join)(__dirname, WEB_PATH_FROM_BUILD));
173446
+ const server = await buildServer({ logger: { level: PUBLISHED_LOG_LEVEL } }, context, (0, import_node_path23.join)(__dirname, WEB_PATH_FROM_BUILD));
173958
173447
  await server.listen(serverConfig);
173959
173448
  console.log(`Flight Plan is running at ${serverUrl}`);
173960
173449
  const printedNotice = printUpdateNotice(pendingNotice);
173961
173450
  await showInBrowser(serverUrl, command);
173962
- if (port !== savedPort && await refreshAgents(context)) await savedPortFile.write(port);
173451
+ const isRefreshed = await refreshAgents(context);
173452
+ if (port !== savedPort && isRefreshed) await savedPortFile.write(port);
173963
173453
  await printedNotice;
173964
173454
  };
173965
173455
  var printVersion = async () => {