@vinihcrosa/lumem-os 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7629,41 +7629,41 @@ var require_queue = __commonJS({
7629
7629
  queue.drained = drained;
7630
7630
  return queue;
7631
7631
  function push(value) {
7632
- var p = new Promise(function(resolve5, reject) {
7632
+ var p = new Promise(function(resolve6, reject) {
7633
7633
  pushCb(value, function(err, result) {
7634
7634
  if (err) {
7635
7635
  reject(err);
7636
7636
  return;
7637
7637
  }
7638
- resolve5(result);
7638
+ resolve6(result);
7639
7639
  });
7640
7640
  });
7641
7641
  p.catch(noop2);
7642
7642
  return p;
7643
7643
  }
7644
7644
  function unshift(value) {
7645
- var p = new Promise(function(resolve5, reject) {
7645
+ var p = new Promise(function(resolve6, reject) {
7646
7646
  unshiftCb(value, function(err, result) {
7647
7647
  if (err) {
7648
7648
  reject(err);
7649
7649
  return;
7650
7650
  }
7651
- resolve5(result);
7651
+ resolve6(result);
7652
7652
  });
7653
7653
  });
7654
7654
  p.catch(noop2);
7655
7655
  return p;
7656
7656
  }
7657
7657
  function drained() {
7658
- var p = new Promise(function(resolve5) {
7658
+ var p = new Promise(function(resolve6) {
7659
7659
  process.nextTick(function() {
7660
7660
  if (queue.idle()) {
7661
- resolve5();
7661
+ resolve6();
7662
7662
  } else {
7663
7663
  var previousDrain = queue.drain;
7664
7664
  queue.drain = function() {
7665
7665
  if (typeof previousDrain === "function") previousDrain();
7666
- resolve5();
7666
+ resolve6();
7667
7667
  queue.drain = previousDrain;
7668
7668
  };
7669
7669
  }
@@ -8006,8 +8006,8 @@ var require_create_promise = __commonJS({
8006
8006
  reject: null,
8007
8007
  promise: null
8008
8008
  };
8009
- obj.promise = new Promise((resolve5, reject) => {
8010
- obj.resolve = resolve5;
8009
+ obj.promise = new Promise((resolve6, reject) => {
8010
+ obj.resolve = resolve6;
8011
8011
  obj.reject = reject;
8012
8012
  });
8013
8013
  return obj;
@@ -8276,11 +8276,11 @@ var require_thenify = __commonJS({
8276
8276
  return;
8277
8277
  }
8278
8278
  debug("thenify");
8279
- return (resolve5, reject) => {
8279
+ return (resolve6, reject) => {
8280
8280
  const p = this._loadRegistered();
8281
8281
  return p.then(() => {
8282
8282
  this[kThenifyDoNotWrap] = true;
8283
- return resolve5(this._server);
8283
+ return resolve6(this._server);
8284
8284
  }, reject);
8285
8285
  };
8286
8286
  }
@@ -8417,12 +8417,12 @@ var require_avvio = __commonJS({
8417
8417
  inherits(Boot, EE);
8418
8418
  if ("asyncDispose" in Symbol) {
8419
8419
  Boot.prototype[Symbol.asyncDispose] = function() {
8420
- return new Promise((resolve5, reject) => {
8420
+ return new Promise((resolve6, reject) => {
8421
8421
  this.close((err) => {
8422
8422
  if (err) {
8423
8423
  return reject(err);
8424
8424
  }
8425
- resolve5();
8425
+ resolve6();
8426
8426
  });
8427
8427
  });
8428
8428
  };
@@ -8569,12 +8569,12 @@ var require_avvio = __commonJS({
8569
8569
  throw new AVV_ERR_CALLBACK_NOT_FN("close", typeof func);
8570
8570
  }
8571
8571
  } else {
8572
- promise2 = new Promise(function(resolve5, reject) {
8572
+ promise2 = new Promise(function(resolve6, reject) {
8573
8573
  func = function(err) {
8574
8574
  if (err) {
8575
8575
  return reject(err);
8576
8576
  }
8577
- resolve5();
8577
+ resolve6();
8578
8578
  };
8579
8579
  });
8580
8580
  }
@@ -8594,7 +8594,7 @@ var require_avvio = __commonJS({
8594
8594
  queueMicrotask(this.start.bind(this));
8595
8595
  return;
8596
8596
  }
8597
- return new Promise((resolve5, reject) => {
8597
+ return new Promise((resolve6, reject) => {
8598
8598
  this._readyQ.push(readyPromiseCB);
8599
8599
  this.start();
8600
8600
  const relativeContext = this._current[0].server;
@@ -8602,7 +8602,7 @@ var require_avvio = __commonJS({
8602
8602
  if (err) {
8603
8603
  reject(err);
8604
8604
  } else {
8605
- resolve5(relativeContext);
8605
+ resolve6(relativeContext);
8606
8606
  }
8607
8607
  process.nextTick(done);
8608
8608
  }
@@ -9982,8 +9982,8 @@ var require_promise = __commonJS({
9982
9982
  var { kTestInternals } = require_symbols2();
9983
9983
  function withResolvers2() {
9984
9984
  let res, rej;
9985
- const promise2 = new Promise((resolve5, reject) => {
9986
- res = resolve5;
9985
+ const promise2 = new Promise((resolve6, reject) => {
9986
+ res = resolve6;
9987
9987
  rej = reject;
9988
9988
  });
9989
9989
  return { promise: promise2, resolve: res, reject: rej };
@@ -10082,15 +10082,15 @@ var require_server = __commonJS({
10082
10082
  if (cb === void 0) {
10083
10083
  const listening = listenPromise.call(this, server, listenOptions);
10084
10084
  return listening.then((address) => {
10085
- const { promise: promise2, resolve: resolve5 } = PonyPromise.withResolvers();
10085
+ const { promise: promise2, resolve: resolve6 } = PonyPromise.withResolvers();
10086
10086
  if (host === "localhost") {
10087
10087
  multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
10088
10088
  this[kState].listening = true;
10089
- resolve5(address);
10089
+ resolve6(address);
10090
10090
  onListenHookRunner(this);
10091
10091
  });
10092
10092
  } else {
10093
- resolve5(address);
10093
+ resolve6(address);
10094
10094
  onListenHookRunner(this);
10095
10095
  }
10096
10096
  return promise2;
@@ -10217,7 +10217,7 @@ var require_server = __commonJS({
10217
10217
  }
10218
10218
  return this.ready().then(() => {
10219
10219
  if (this[kState].aborted) return;
10220
- const { promise: promise2, resolve: resolve5, reject } = PonyPromise.withResolvers();
10220
+ const { promise: promise2, resolve: resolve6, reject } = PonyPromise.withResolvers();
10221
10221
  const errEventHandler = (err) => {
10222
10222
  cleanup();
10223
10223
  this[kState].listening = false;
@@ -10226,7 +10226,7 @@ var require_server = __commonJS({
10226
10226
  const listeningEventHandler = () => {
10227
10227
  cleanup();
10228
10228
  this[kState].listening = true;
10229
- resolve5(logServerAddress.call(
10229
+ resolve6(logServerAddress.call(
10230
10230
  this,
10231
10231
  server,
10232
10232
  listenOptions.listenTextResolver || defaultResolveServerListeningText
@@ -13993,7 +13993,7 @@ var require_redact = __commonJS({
13993
13993
  } = options;
13994
13994
  validatePaths(paths);
13995
13995
  const pathStructure = buildPathStructure(paths);
13996
- return function redact(obj) {
13996
+ return function redact2(obj) {
13997
13997
  if (strict && (obj === null || typeof obj !== "object")) {
13998
13998
  if (obj === null || obj === void 0) {
13999
13999
  return serialize ? serialize(obj) : obj;
@@ -15181,7 +15181,7 @@ var require_thread_stream = __commonJS({
15181
15181
  var { version: version3 } = require_package();
15182
15182
  var { EventEmitter: EventEmitter2 } = __require("events");
15183
15183
  var { Worker } = __require("worker_threads");
15184
- var { join: join20 } = __require("path");
15184
+ var { join: join21 } = __require("path");
15185
15185
  var { pathToFileURL } = __require("url");
15186
15186
  var { wait } = require_wait();
15187
15187
  var {
@@ -15232,7 +15232,7 @@ var require_thread_stream = __commonJS({
15232
15232
  function createWorker(stream, opts) {
15233
15233
  const { filename, workerData } = opts;
15234
15234
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
15235
- const toExecute = bundlerOverrides["thread-stream-worker"] || join20(__dirname, "lib", "worker.js");
15235
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join21(__dirname, "lib", "worker.js");
15236
15236
  const worker = new Worker(toExecute, {
15237
15237
  ...opts.workerOpts,
15238
15238
  name: opts.workerOpts?.name || "thread-stream",
@@ -15700,7 +15700,7 @@ var require_transport = __commonJS({
15700
15700
  var { createRequire } = __require("module");
15701
15701
  var { existsSync: existsSync10 } = __require("node:fs");
15702
15702
  var getCallers = require_caller();
15703
- var { join: join20, isAbsolute: isAbsolute7, sep: sep6 } = __require("node:path");
15703
+ var { join: join21, isAbsolute: isAbsolute8, sep: sep6 } = __require("node:path");
15704
15704
  var { fileURLToPath: fileURLToPath3 } = __require("node:url");
15705
15705
  var sleep = require_atomic_sleep();
15706
15706
  var onExit = require_on_exit_leak_free();
@@ -15772,7 +15772,7 @@ var require_transport = __commonJS({
15772
15772
  return false;
15773
15773
  }
15774
15774
  }
15775
- return isAbsolute7(path) && !existsSync10(path);
15775
+ return isAbsolute8(path) && !existsSync10(path);
15776
15776
  }
15777
15777
  function stripQuotes(value) {
15778
15778
  const first = value[0];
@@ -15853,7 +15853,7 @@ var require_transport = __commonJS({
15853
15853
  throw new Error("only one of target or targets can be specified");
15854
15854
  }
15855
15855
  if (targets) {
15856
- target = bundlerOverrides["pino-worker"] || join20(__dirname, "worker.js");
15856
+ target = bundlerOverrides["pino-worker"] || join21(__dirname, "worker.js");
15857
15857
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
15858
15858
  return {
15859
15859
  ...dest,
@@ -15871,7 +15871,7 @@ var require_transport = __commonJS({
15871
15871
  });
15872
15872
  });
15873
15873
  } else if (pipeline) {
15874
- target = bundlerOverrides["pino-worker"] || join20(__dirname, "worker.js");
15874
+ target = bundlerOverrides["pino-worker"] || join21(__dirname, "worker.js");
15875
15875
  options.pipelines = [pipeline.map((dest) => {
15876
15876
  return {
15877
15877
  ...dest,
@@ -15890,11 +15890,11 @@ var require_transport = __commonJS({
15890
15890
  return buildStream(fixTarget(target), options, worker, sync, name);
15891
15891
  function fixTarget(origin) {
15892
15892
  origin = bundlerOverrides[origin] || origin;
15893
- if (isAbsolute7(origin) || origin.indexOf("file://") === 0) {
15893
+ if (isAbsolute8(origin) || origin.indexOf("file://") === 0) {
15894
15894
  return origin;
15895
15895
  }
15896
15896
  if (origin === "pino/file") {
15897
- return join20(__dirname, "..", "file.js");
15897
+ return join21(__dirname, "..", "file.js");
15898
15898
  }
15899
15899
  let fixTarget2;
15900
15900
  for (const filePath of callers) {
@@ -16874,7 +16874,7 @@ var require_safe_stable_stringify = __commonJS({
16874
16874
  return circularValue;
16875
16875
  }
16876
16876
  let res = "";
16877
- let join20 = ",";
16877
+ let join21 = ",";
16878
16878
  const originalIndentation = indentation;
16879
16879
  if (Array.isArray(value)) {
16880
16880
  if (value.length === 0) {
@@ -16888,7 +16888,7 @@ var require_safe_stable_stringify = __commonJS({
16888
16888
  indentation += spacer;
16889
16889
  res += `
16890
16890
  ${indentation}`;
16891
- join20 = `,
16891
+ join21 = `,
16892
16892
  ${indentation}`;
16893
16893
  }
16894
16894
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -16896,13 +16896,13 @@ ${indentation}`;
16896
16896
  for (; i < maximumValuesToStringify - 1; i++) {
16897
16897
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
16898
16898
  res += tmp2 !== void 0 ? tmp2 : "null";
16899
- res += join20;
16899
+ res += join21;
16900
16900
  }
16901
16901
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
16902
16902
  res += tmp !== void 0 ? tmp : "null";
16903
16903
  if (value.length - 1 > maximumBreadth) {
16904
16904
  const removedKeys = value.length - maximumBreadth - 1;
16905
- res += `${join20}"... ${getItemCount(removedKeys)} not stringified"`;
16905
+ res += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
16906
16906
  }
16907
16907
  if (spacer !== "") {
16908
16908
  res += `
@@ -16923,7 +16923,7 @@ ${originalIndentation}`;
16923
16923
  let separator = "";
16924
16924
  if (spacer !== "") {
16925
16925
  indentation += spacer;
16926
- join20 = `,
16926
+ join21 = `,
16927
16927
  ${indentation}`;
16928
16928
  whitespace = " ";
16929
16929
  }
@@ -16937,13 +16937,13 @@ ${indentation}`;
16937
16937
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
16938
16938
  if (tmp !== void 0) {
16939
16939
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
16940
- separator = join20;
16940
+ separator = join21;
16941
16941
  }
16942
16942
  }
16943
16943
  if (keyLength > maximumBreadth) {
16944
16944
  const removedKeys = keyLength - maximumBreadth;
16945
16945
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
16946
- separator = join20;
16946
+ separator = join21;
16947
16947
  }
16948
16948
  if (spacer !== "" && separator.length > 1) {
16949
16949
  res = `
@@ -16984,7 +16984,7 @@ ${originalIndentation}`;
16984
16984
  }
16985
16985
  const originalIndentation = indentation;
16986
16986
  let res = "";
16987
- let join20 = ",";
16987
+ let join21 = ",";
16988
16988
  if (Array.isArray(value)) {
16989
16989
  if (value.length === 0) {
16990
16990
  return "[]";
@@ -16997,7 +16997,7 @@ ${originalIndentation}`;
16997
16997
  indentation += spacer;
16998
16998
  res += `
16999
16999
  ${indentation}`;
17000
- join20 = `,
17000
+ join21 = `,
17001
17001
  ${indentation}`;
17002
17002
  }
17003
17003
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -17005,13 +17005,13 @@ ${indentation}`;
17005
17005
  for (; i < maximumValuesToStringify - 1; i++) {
17006
17006
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
17007
17007
  res += tmp2 !== void 0 ? tmp2 : "null";
17008
- res += join20;
17008
+ res += join21;
17009
17009
  }
17010
17010
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
17011
17011
  res += tmp !== void 0 ? tmp : "null";
17012
17012
  if (value.length - 1 > maximumBreadth) {
17013
17013
  const removedKeys = value.length - maximumBreadth - 1;
17014
- res += `${join20}"... ${getItemCount(removedKeys)} not stringified"`;
17014
+ res += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
17015
17015
  }
17016
17016
  if (spacer !== "") {
17017
17017
  res += `
@@ -17024,7 +17024,7 @@ ${originalIndentation}`;
17024
17024
  let whitespace = "";
17025
17025
  if (spacer !== "") {
17026
17026
  indentation += spacer;
17027
- join20 = `,
17027
+ join21 = `,
17028
17028
  ${indentation}`;
17029
17029
  whitespace = " ";
17030
17030
  }
@@ -17033,7 +17033,7 @@ ${indentation}`;
17033
17033
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
17034
17034
  if (tmp !== void 0) {
17035
17035
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
17036
- separator = join20;
17036
+ separator = join21;
17037
17037
  }
17038
17038
  }
17039
17039
  if (spacer !== "" && separator.length > 1) {
@@ -17091,20 +17091,20 @@ ${originalIndentation}`;
17091
17091
  indentation += spacer;
17092
17092
  let res2 = `
17093
17093
  ${indentation}`;
17094
- const join21 = `,
17094
+ const join22 = `,
17095
17095
  ${indentation}`;
17096
17096
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
17097
17097
  let i = 0;
17098
17098
  for (; i < maximumValuesToStringify - 1; i++) {
17099
17099
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
17100
17100
  res2 += tmp2 !== void 0 ? tmp2 : "null";
17101
- res2 += join21;
17101
+ res2 += join22;
17102
17102
  }
17103
17103
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
17104
17104
  res2 += tmp !== void 0 ? tmp : "null";
17105
17105
  if (value.length - 1 > maximumBreadth) {
17106
17106
  const removedKeys = value.length - maximumBreadth - 1;
17107
- res2 += `${join21}"... ${getItemCount(removedKeys)} not stringified"`;
17107
+ res2 += `${join22}"... ${getItemCount(removedKeys)} not stringified"`;
17108
17108
  }
17109
17109
  res2 += `
17110
17110
  ${originalIndentation}`;
@@ -17120,16 +17120,16 @@ ${originalIndentation}`;
17120
17120
  return '"[Object]"';
17121
17121
  }
17122
17122
  indentation += spacer;
17123
- const join20 = `,
17123
+ const join21 = `,
17124
17124
  ${indentation}`;
17125
17125
  let res = "";
17126
17126
  let separator = "";
17127
17127
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
17128
17128
  if (isTypedArrayWithEntries(value)) {
17129
- res += stringifyTypedArray(value, join20, maximumBreadth);
17129
+ res += stringifyTypedArray(value, join21, maximumBreadth);
17130
17130
  keys = keys.slice(value.length);
17131
17131
  maximumPropertiesToStringify -= value.length;
17132
- separator = join20;
17132
+ separator = join21;
17133
17133
  }
17134
17134
  if (deterministic) {
17135
17135
  keys = sort(keys, comparator);
@@ -17140,13 +17140,13 @@ ${indentation}`;
17140
17140
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
17141
17141
  if (tmp !== void 0) {
17142
17142
  res += `${separator}${strEscape(key2)}: ${tmp}`;
17143
- separator = join20;
17143
+ separator = join21;
17144
17144
  }
17145
17145
  }
17146
17146
  if (keyLength > maximumBreadth) {
17147
17147
  const removedKeys = keyLength - maximumBreadth;
17148
17148
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
17149
- separator = join20;
17149
+ separator = join21;
17150
17150
  }
17151
17151
  if (separator !== "") {
17152
17152
  res = `
@@ -17554,7 +17554,7 @@ var require_pino = __commonJS({
17554
17554
  const { opts, stream } = normalize5(instance, caller(), ...args);
17555
17555
  if (opts.level && typeof opts.level === "string" && DEFAULT_LEVELS[opts.level.toLowerCase()] !== void 0) opts.level = opts.level.toLowerCase();
17556
17556
  const {
17557
- redact,
17557
+ redact: redact2,
17558
17558
  crlf,
17559
17559
  serializers: serializers2,
17560
17560
  timestamp,
@@ -17588,8 +17588,8 @@ var require_pino = __commonJS({
17588
17588
  const stringifyFn = stringify2.bind({
17589
17589
  [stringifySafeSym]: stringifySafe
17590
17590
  });
17591
- const stringifiers = redact ? redaction(redact, stringifyFn) : {};
17592
- const formatOpts = redact ? { stringify: stringifiers[redactFmtSym] } : { stringify: stringifyFn };
17591
+ const stringifiers = redact2 ? redaction(redact2, stringifyFn) : {};
17592
+ const formatOpts = redact2 ? { stringify: stringifiers[redactFmtSym] } : { stringify: stringifyFn };
17593
17593
  const end = "}" + (crlf ? "\r\n" : "\n");
17594
17594
  const coreChindings = asChindings.bind(null, {
17595
17595
  [chindingsSym]: "",
@@ -24203,7 +24203,7 @@ var require_compile = __commonJS({
24203
24203
  const schOrFunc = root.refs[ref];
24204
24204
  if (schOrFunc)
24205
24205
  return schOrFunc;
24206
- let _sch = resolve5.call(this, root, ref);
24206
+ let _sch = resolve6.call(this, root, ref);
24207
24207
  if (_sch === void 0) {
24208
24208
  const schema2 = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
24209
24209
  const { schemaId } = this.opts;
@@ -24230,7 +24230,7 @@ var require_compile = __commonJS({
24230
24230
  function sameSchemaEnv(s1, s2) {
24231
24231
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
24232
24232
  }
24233
- function resolve5(root, ref) {
24233
+ function resolve6(root, ref) {
24234
24234
  let sch;
24235
24235
  while (typeof (sch = this.refs[ref]) == "string")
24236
24236
  ref = sch;
@@ -24861,7 +24861,7 @@ var require_fast_uri = __commonJS({
24861
24861
  }
24862
24862
  return uri;
24863
24863
  }
24864
- function resolve5(baseURI, relativeURI, options) {
24864
+ function resolve6(baseURI, relativeURI, options) {
24865
24865
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
24866
24866
  const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
24867
24867
  const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
@@ -24872,49 +24872,49 @@ var require_fast_uri = __commonJS({
24872
24872
  schemelessOptions.skipEscape = true;
24873
24873
  return serialize(resolved, schemelessOptions);
24874
24874
  }
24875
- function resolveComponent(base, relative4, options, skipNormalization) {
24875
+ function resolveComponent(base, relative5, options, skipNormalization) {
24876
24876
  const target = {};
24877
24877
  if (!skipNormalization) {
24878
24878
  base = parse5(serialize(base, options), options);
24879
- relative4 = parse5(serialize(relative4, options), options);
24879
+ relative5 = parse5(serialize(relative5, options), options);
24880
24880
  }
24881
24881
  options = options || {};
24882
- if (!options.tolerant && relative4.scheme) {
24883
- target.scheme = relative4.scheme;
24884
- target.userinfo = relative4.userinfo;
24885
- target.host = relative4.host;
24886
- target.port = relative4.port;
24887
- target.path = removeDotSegments(relative4.path || "");
24888
- target.query = relative4.query;
24882
+ if (!options.tolerant && relative5.scheme) {
24883
+ target.scheme = relative5.scheme;
24884
+ target.userinfo = relative5.userinfo;
24885
+ target.host = relative5.host;
24886
+ target.port = relative5.port;
24887
+ target.path = removeDotSegments(relative5.path || "");
24888
+ target.query = relative5.query;
24889
24889
  } else {
24890
- if (relative4.userinfo !== void 0 || relative4.host !== void 0 || relative4.port !== void 0) {
24891
- target.userinfo = relative4.userinfo;
24892
- target.host = relative4.host;
24893
- target.port = relative4.port;
24894
- target.path = removeDotSegments(relative4.path || "");
24895
- target.query = relative4.query;
24890
+ if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
24891
+ target.userinfo = relative5.userinfo;
24892
+ target.host = relative5.host;
24893
+ target.port = relative5.port;
24894
+ target.path = removeDotSegments(relative5.path || "");
24895
+ target.query = relative5.query;
24896
24896
  } else {
24897
- if (!relative4.path) {
24897
+ if (!relative5.path) {
24898
24898
  target.path = base.path;
24899
- if (relative4.query !== void 0) {
24900
- target.query = relative4.query;
24899
+ if (relative5.query !== void 0) {
24900
+ target.query = relative5.query;
24901
24901
  } else {
24902
24902
  target.query = base.query;
24903
24903
  }
24904
24904
  } else {
24905
- if (relative4.path[0] === "/") {
24906
- target.path = removeDotSegments(relative4.path);
24905
+ if (relative5.path[0] === "/") {
24906
+ target.path = removeDotSegments(relative5.path);
24907
24907
  } else {
24908
24908
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
24909
- target.path = "/" + relative4.path;
24909
+ target.path = "/" + relative5.path;
24910
24910
  } else if (!base.path) {
24911
- target.path = relative4.path;
24911
+ target.path = relative5.path;
24912
24912
  } else {
24913
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative4.path;
24913
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
24914
24914
  }
24915
24915
  target.path = removeDotSegments(target.path);
24916
24916
  }
24917
- target.query = relative4.query;
24917
+ target.query = relative5.query;
24918
24918
  }
24919
24919
  target.userinfo = base.userinfo;
24920
24920
  target.host = base.host;
@@ -24922,7 +24922,7 @@ var require_fast_uri = __commonJS({
24922
24922
  }
24923
24923
  target.scheme = base.scheme;
24924
24924
  }
24925
- target.fragment = relative4.fragment;
24925
+ target.fragment = relative5.fragment;
24926
24926
  return target;
24927
24927
  }
24928
24928
  function equal(uriA, uriB, options) {
@@ -25145,7 +25145,7 @@ var require_fast_uri = __commonJS({
25145
25145
  var fastUri = {
25146
25146
  SCHEMES,
25147
25147
  normalize: normalize5,
25148
- resolve: resolve5,
25148
+ resolve: resolve6,
25149
25149
  resolveComponent,
25150
25150
  equal,
25151
25151
  serialize,
@@ -28901,37 +28901,37 @@ var require_parseJson = __commonJS({
28901
28901
  Object.defineProperty(exports, "__esModule", { value: true });
28902
28902
  exports.parseJsonString = exports.parseJsonNumber = exports.parseJson = void 0;
28903
28903
  var rxParseJson = /position\s(\d+)(?: \(line \d+ column \d+\))?$/;
28904
- function parseJson(s, pos) {
28904
+ function parseJson2(s, pos) {
28905
28905
  let endPos;
28906
- parseJson.message = void 0;
28906
+ parseJson2.message = void 0;
28907
28907
  let matches;
28908
28908
  if (pos)
28909
28909
  s = s.slice(pos);
28910
28910
  try {
28911
- parseJson.position = pos + s.length;
28911
+ parseJson2.position = pos + s.length;
28912
28912
  return JSON.parse(s);
28913
28913
  } catch (e) {
28914
28914
  matches = rxParseJson.exec(e.message);
28915
28915
  if (!matches) {
28916
- parseJson.message = "unexpected end";
28916
+ parseJson2.message = "unexpected end";
28917
28917
  return void 0;
28918
28918
  }
28919
28919
  endPos = +matches[1];
28920
28920
  const c = s[endPos];
28921
28921
  s = s.slice(0, endPos);
28922
- parseJson.position = pos + endPos;
28922
+ parseJson2.position = pos + endPos;
28923
28923
  try {
28924
28924
  return JSON.parse(s);
28925
28925
  } catch (e1) {
28926
- parseJson.message = `unexpected token ${c}`;
28926
+ parseJson2.message = `unexpected token ${c}`;
28927
28927
  return void 0;
28928
28928
  }
28929
28929
  }
28930
28930
  }
28931
- exports.parseJson = parseJson;
28932
- parseJson.message = void 0;
28933
- parseJson.position = 0;
28934
- parseJson.code = 'require("ajv/dist/runtime/parseJson").parseJson';
28931
+ exports.parseJson = parseJson2;
28932
+ parseJson2.message = void 0;
28933
+ parseJson2.position = 0;
28934
+ parseJson2.code = 'require("ajv/dist/runtime/parseJson").parseJson';
28935
28935
  function parseJsonNumber(s, pos, maxDigits) {
28936
28936
  let numStr = "";
28937
28937
  let c;
@@ -30145,7 +30145,7 @@ var require_fast_uri2 = __commonJS({
30145
30145
  }
30146
30146
  return uri;
30147
30147
  }
30148
- function resolve5(baseURI, relativeURI, options) {
30148
+ function resolve6(baseURI, relativeURI, options) {
30149
30149
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
30150
30150
  const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
30151
30151
  const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
@@ -30156,49 +30156,49 @@ var require_fast_uri2 = __commonJS({
30156
30156
  schemelessOptions.skipEscape = true;
30157
30157
  return serialize(resolved, schemelessOptions);
30158
30158
  }
30159
- function resolveComponent(base, relative4, options, skipNormalization) {
30159
+ function resolveComponent(base, relative5, options, skipNormalization) {
30160
30160
  const target = {};
30161
30161
  if (!skipNormalization) {
30162
30162
  base = parse5(serialize(base, options), options);
30163
- relative4 = parse5(serialize(relative4, options), options);
30163
+ relative5 = parse5(serialize(relative5, options), options);
30164
30164
  }
30165
30165
  options = options || {};
30166
- if (!options.tolerant && relative4.scheme) {
30167
- target.scheme = relative4.scheme;
30168
- target.userinfo = relative4.userinfo;
30169
- target.host = relative4.host;
30170
- target.port = relative4.port;
30171
- target.path = removeDotSegments(relative4.path || "");
30172
- target.query = relative4.query;
30166
+ if (!options.tolerant && relative5.scheme) {
30167
+ target.scheme = relative5.scheme;
30168
+ target.userinfo = relative5.userinfo;
30169
+ target.host = relative5.host;
30170
+ target.port = relative5.port;
30171
+ target.path = removeDotSegments(relative5.path || "");
30172
+ target.query = relative5.query;
30173
30173
  } else {
30174
- if (relative4.userinfo !== void 0 || relative4.host !== void 0 || relative4.port !== void 0) {
30175
- target.userinfo = relative4.userinfo;
30176
- target.host = relative4.host;
30177
- target.port = relative4.port;
30178
- target.path = removeDotSegments(relative4.path || "");
30179
- target.query = relative4.query;
30174
+ if (relative5.userinfo !== void 0 || relative5.host !== void 0 || relative5.port !== void 0) {
30175
+ target.userinfo = relative5.userinfo;
30176
+ target.host = relative5.host;
30177
+ target.port = relative5.port;
30178
+ target.path = removeDotSegments(relative5.path || "");
30179
+ target.query = relative5.query;
30180
30180
  } else {
30181
- if (!relative4.path) {
30181
+ if (!relative5.path) {
30182
30182
  target.path = base.path;
30183
- if (relative4.query !== void 0) {
30184
- target.query = relative4.query;
30183
+ if (relative5.query !== void 0) {
30184
+ target.query = relative5.query;
30185
30185
  } else {
30186
30186
  target.query = base.query;
30187
30187
  }
30188
30188
  } else {
30189
- if (relative4.path[0] === "/") {
30190
- target.path = removeDotSegments(relative4.path);
30189
+ if (relative5.path[0] === "/") {
30190
+ target.path = removeDotSegments(relative5.path);
30191
30191
  } else {
30192
30192
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
30193
- target.path = "/" + relative4.path;
30193
+ target.path = "/" + relative5.path;
30194
30194
  } else if (!base.path) {
30195
- target.path = relative4.path;
30195
+ target.path = relative5.path;
30196
30196
  } else {
30197
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative4.path;
30197
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative5.path;
30198
30198
  }
30199
30199
  target.path = removeDotSegments(target.path);
30200
30200
  }
30201
- target.query = relative4.query;
30201
+ target.query = relative5.query;
30202
30202
  }
30203
30203
  target.userinfo = base.userinfo;
30204
30204
  target.host = base.host;
@@ -30206,7 +30206,7 @@ var require_fast_uri2 = __commonJS({
30206
30206
  }
30207
30207
  target.scheme = base.scheme;
30208
30208
  }
30209
- target.fragment = relative4.fragment;
30209
+ target.fragment = relative5.fragment;
30210
30210
  return target;
30211
30211
  }
30212
30212
  function equal(uriA, uriB, options) {
@@ -30428,7 +30428,7 @@ var require_fast_uri2 = __commonJS({
30428
30428
  var fastUri = {
30429
30429
  SCHEMES,
30430
30430
  normalize: normalize5,
30431
- resolve: resolve5,
30431
+ resolve: resolve6,
30432
30432
  resolveComponent,
30433
30433
  equal,
30434
30434
  serialize,
@@ -42289,9 +42289,9 @@ var require_light_my_request = __commonJS({
42289
42289
  const res = new Response2(req, callback);
42290
42290
  return makeRequest(dispatchFunc, server, req, res);
42291
42291
  } else {
42292
- return new Promise((resolve5, reject) => {
42292
+ return new Promise((resolve6, reject) => {
42293
42293
  const req = new RequestConstructor(options);
42294
- const res = new Response2(req, resolve5, reject);
42294
+ const res = new Response2(req, resolve6, reject);
42295
42295
  makeRequest(dispatchFunc, server, req, res);
42296
42296
  });
42297
42297
  }
@@ -49470,7 +49470,7 @@ var require_commonjs4 = __commonJS({
49470
49470
  var EMITTING_END = Symbol("emittingEnd");
49471
49471
  var EMITTED_ERROR = Symbol("emittedError");
49472
49472
  var CLOSED = Symbol("closed");
49473
- var READ = Symbol("read");
49473
+ var READ2 = Symbol("read");
49474
49474
  var FLUSH = Symbol("flush");
49475
49475
  var FLUSHCHUNK = Symbol("flushChunk");
49476
49476
  var ENCODING = Symbol("encoding");
@@ -49775,11 +49775,11 @@ var require_commonjs4 = __commonJS({
49775
49775
  this[ENCODING] ? this[BUFFER].join("") : Buffer.concat(this[BUFFER], this[BUFFERLENGTH])
49776
49776
  ];
49777
49777
  }
49778
- const ret = this[READ](n || null, this[BUFFER][0]);
49778
+ const ret = this[READ2](n || null, this[BUFFER][0]);
49779
49779
  this[MAYBE_EMIT_END]();
49780
49780
  return ret;
49781
49781
  }
49782
- [READ](n, chunk) {
49782
+ [READ2](n, chunk) {
49783
49783
  if (this[OBJECTMODE])
49784
49784
  this[BUFFERSHIFT]();
49785
49785
  else {
@@ -50181,10 +50181,10 @@ var require_commonjs4 = __commonJS({
50181
50181
  * Return a void Promise that resolves once the stream ends.
50182
50182
  */
50183
50183
  async promise() {
50184
- return new Promise((resolve5, reject) => {
50184
+ return new Promise((resolve6, reject) => {
50185
50185
  this.on(DESTROYED, () => reject(new Error("stream destroyed")));
50186
50186
  this.on("error", (er) => reject(er));
50187
- this.on("end", () => resolve5());
50187
+ this.on("end", () => resolve6());
50188
50188
  });
50189
50189
  }
50190
50190
  /**
@@ -50208,7 +50208,7 @@ var require_commonjs4 = __commonJS({
50208
50208
  return Promise.resolve({ done: false, value: res });
50209
50209
  if (this[EOF])
50210
50210
  return stop();
50211
- let resolve5;
50211
+ let resolve6;
50212
50212
  let reject;
50213
50213
  const onerr = (er) => {
50214
50214
  this.off("data", ondata);
@@ -50222,19 +50222,19 @@ var require_commonjs4 = __commonJS({
50222
50222
  this.off("end", onend);
50223
50223
  this.off(DESTROYED, ondestroy);
50224
50224
  this.pause();
50225
- resolve5({ value, done: !!this[EOF] });
50225
+ resolve6({ value, done: !!this[EOF] });
50226
50226
  };
50227
50227
  const onend = () => {
50228
50228
  this.off("error", onerr);
50229
50229
  this.off("data", ondata);
50230
50230
  this.off(DESTROYED, ondestroy);
50231
50231
  stop();
50232
- resolve5({ done: true, value: void 0 });
50232
+ resolve6({ done: true, value: void 0 });
50233
50233
  };
50234
50234
  const ondestroy = () => onerr(new Error("stream destroyed"));
50235
50235
  return new Promise((res2, rej) => {
50236
50236
  reject = rej;
50237
- resolve5 = res2;
50237
+ resolve6 = res2;
50238
50238
  this.once(DESTROYED, ondestroy);
50239
50239
  this.once("error", onerr);
50240
50240
  this.once("end", onend);
@@ -51254,9 +51254,9 @@ var require_commonjs5 = __commonJS({
51254
51254
  if (this.#asyncReaddirInFlight) {
51255
51255
  await this.#asyncReaddirInFlight;
51256
51256
  } else {
51257
- let resolve5 = () => {
51257
+ let resolve6 = () => {
51258
51258
  };
51259
- this.#asyncReaddirInFlight = new Promise((res) => resolve5 = res);
51259
+ this.#asyncReaddirInFlight = new Promise((res) => resolve6 = res);
51260
51260
  try {
51261
51261
  for (const e of await this.#fs.promises.readdir(fullpath, {
51262
51262
  withFileTypes: true
@@ -51269,7 +51269,7 @@ var require_commonjs5 = __commonJS({
51269
51269
  children.provisional = 0;
51270
51270
  }
51271
51271
  this.#asyncReaddirInFlight = void 0;
51272
- resolve5();
51272
+ resolve6();
51273
51273
  }
51274
51274
  return children.slice(0, children.provisional);
51275
51275
  }
@@ -52356,10 +52356,10 @@ var require_ignore = __commonJS({
52356
52356
  ignored(p) {
52357
52357
  const fullpath = p.fullpath();
52358
52358
  const fullpaths = `${fullpath}/`;
52359
- const relative4 = p.relative() || ".";
52360
- const relatives = `${relative4}/`;
52359
+ const relative5 = p.relative() || ".";
52360
+ const relatives = `${relative5}/`;
52361
52361
  for (const m of this.relative) {
52362
- if (m.match(relative4) || m.match(relatives))
52362
+ if (m.match(relative5) || m.match(relatives))
52363
52363
  return true;
52364
52364
  }
52365
52365
  for (const m of this.absolute) {
@@ -52370,9 +52370,9 @@ var require_ignore = __commonJS({
52370
52370
  }
52371
52371
  childrenIgnored(p) {
52372
52372
  const fullpath = p.fullpath() + "/";
52373
- const relative4 = (p.relative() || ".") + "/";
52373
+ const relative5 = (p.relative() || ".") + "/";
52374
52374
  for (const m of this.relativeChildren) {
52375
- if (m.match(relative4))
52375
+ if (m.match(relative5))
52376
52376
  return true;
52377
52377
  }
52378
52378
  for (const m of this.absoluteChildren) {
@@ -53741,7 +53741,7 @@ var require_parseTokenList = __commonJS({
53741
53741
  // ../../node_modules/.pnpm/depd@2.0.0/node_modules/depd/index.js
53742
53742
  var require_depd = __commonJS({
53743
53743
  "../../node_modules/.pnpm/depd@2.0.0/node_modules/depd/index.js"(exports, module) {
53744
- var relative4 = __require("path").relative;
53744
+ var relative5 = __require("path").relative;
53745
53745
  module.exports = depd;
53746
53746
  var basePath = process.cwd();
53747
53747
  function containsNamespace(str, namespace) {
@@ -53933,7 +53933,7 @@ var require_depd = __commonJS({
53933
53933
  return formatted;
53934
53934
  }
53935
53935
  function formatLocation(callSite) {
53936
- return relative4(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
53936
+ return relative5(basePath, callSite[0]) + ":" + callSite[1] + ":" + callSite[2];
53937
53937
  }
53938
53938
  function getStack() {
53939
53939
  var limit = Error.stackTraceLimit;
@@ -54466,9 +54466,9 @@ var require_send = __commonJS({
54466
54466
  var { parseTokenList } = require_parseTokenList();
54467
54467
  var { createHttpError } = require_createHttpError();
54468
54468
  var extname = path.extname;
54469
- var join20 = path.join;
54469
+ var join21 = path.join;
54470
54470
  var normalize5 = path.normalize;
54471
- var resolve5 = path.resolve;
54471
+ var resolve6 = path.resolve;
54472
54472
  var sep6 = path.sep;
54473
54473
  var Readable2 = stream.Readable;
54474
54474
  var BYTES_RANGE_REGEXP = /^ *bytes=/;
@@ -54515,7 +54515,7 @@ var require_send = __commonJS({
54515
54515
  const lastModified = options.lastModified !== void 0 ? Boolean(options.lastModified) : true;
54516
54516
  const maxage = normalizeMaxAge(options.maxAge ?? options.maxage);
54517
54517
  const maxContentRangeChunkSize = options.maxContentRangeChunkSize !== void 0 ? Number(options.maxContentRangeChunkSize) : null;
54518
- const root = options.root ? resolve5(options.root) : null;
54518
+ const root = options.root ? resolve6(options.root) : null;
54519
54519
  const highWaterMark = Number.isSafeInteger(options.highWaterMark) && options.highWaterMark > 0 ? options.highWaterMark : null;
54520
54520
  return {
54521
54521
  acceptRanges,
@@ -54553,14 +54553,14 @@ var require_send = __commonJS({
54553
54553
  return { statusCode: 403 };
54554
54554
  }
54555
54555
  parts = path2.split(sep6);
54556
- path2 = normalize5(join20(root, path2));
54556
+ path2 = normalize5(join21(root, path2));
54557
54557
  } else {
54558
54558
  if (UP_PATH_REGEXP.test(path2)) {
54559
54559
  debug('malicious path "%s"', path2);
54560
54560
  return { statusCode: 403 };
54561
54561
  }
54562
54562
  parts = normalize5(path2).split(sep6);
54563
- path2 = resolve5(path2);
54563
+ path2 = resolve6(path2);
54564
54564
  }
54565
54565
  return { path: path2, parts };
54566
54566
  }
@@ -54656,9 +54656,9 @@ var require_send = __commonJS({
54656
54656
  );
54657
54657
  }
54658
54658
  function tryStat(path2) {
54659
- return new Promise((resolve6) => {
54659
+ return new Promise((resolve7) => {
54660
54660
  fs2.stat(path2, function onstat(error40, stat3) {
54661
- resolve6({ error: error40, stat: stat3 });
54661
+ resolve7({ error: error40, stat: stat3 });
54662
54662
  });
54663
54663
  });
54664
54664
  }
@@ -54836,7 +54836,7 @@ var require_send = __commonJS({
54836
54836
  let err;
54837
54837
  for (let i = 0; i < options.index.length; i++) {
54838
54838
  const index2 = options.index[i];
54839
- const p = join20(path2, index2);
54839
+ const p = join21(path2, index2);
54840
54840
  const { error: error40, stat: stat3 } = await tryStat(p);
54841
54841
  if (error40) {
54842
54842
  err = error40;
@@ -55132,7 +55132,7 @@ var require_content_disposition = __commonJS({
55132
55132
  "use strict";
55133
55133
  module.exports = contentDisposition;
55134
55134
  module.exports.parse = parse5;
55135
- var basename4 = __require("path").basename;
55135
+ var basename5 = __require("path").basename;
55136
55136
  var Buffer2 = require_safe_buffer().Buffer;
55137
55137
  var ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g;
55138
55138
  var HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/;
@@ -55168,9 +55168,9 @@ var require_content_disposition = __commonJS({
55168
55168
  if (typeof fallback === "string" && NON_LATIN1_REGEXP.test(fallback)) {
55169
55169
  throw new TypeError("fallback must be ISO-8859-1 string");
55170
55170
  }
55171
- var name = basename4(filename);
55171
+ var name = basename5(filename);
55172
55172
  var isQuotedString = TEXT_REGEXP.test(name);
55173
- var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename4(fallback);
55173
+ var fallbackName = typeof fallback !== "string" ? fallback && getlatin1(name) : basename5(fallback);
55174
55174
  var hasFallback = typeof fallbackName === "string" && fallbackName !== name;
55175
55175
  if (hasFallback || !isQuotedString || HEX_ESCAPE_REGEXP.test(name)) {
55176
55176
  params["filename*"] = name;
@@ -60013,6 +60013,9 @@ var acpPermissionOptionKindSchema = external_exports.enum([
60013
60013
  "reject_once",
60014
60014
  "reject_always"
60015
60015
  ]);
60016
+ var lumemModeSchema = external_exports.enum(["ask", "auto", "free"]);
60017
+ var lumemModeDefaultSchema = external_exports.enum(["ask", "auto"]);
60018
+ var acpModeOwnerSchema = external_exports.enum(["agent", "lumem"]);
60016
60019
  var acpPermissionOptionSchema = external_exports.object({
60017
60020
  optionId: external_exports.string(),
60018
60021
  /** The agent's own label, shown verbatim (A13). */
@@ -60116,13 +60119,40 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
60116
60119
  title: external_exports.string(),
60117
60120
  command: external_exports.string().nullish(),
60118
60121
  cwd: external_exports.string(),
60119
- options: external_exports.array(acpPermissionOptionSchema).min(1)
60122
+ options: external_exports.array(acpPermissionOptionSchema).min(1),
60123
+ /**
60124
+ * Why the Lumem policy did not answer this one (`session-mode`, T7).
60125
+ *
60126
+ * Null under `ask`, and under an agent that owns its own modes: there is
60127
+ * nothing to explain when asking *is* the rule. Set when a policy that would
60128
+ * normally answer did not — `auto` facing a write, or an agent that offered
60129
+ * no way to allow (Q6) — because "why am I being asked in auto mode?" is
60130
+ * otherwise a question the screen cannot answer.
60131
+ */
60132
+ policyReason: external_exports.string().nullable().default(null)
60120
60133
  }),
60121
60134
  external_exports.object({
60122
60135
  type: external_exports.literal("permission_resolved"),
60123
60136
  requestId: external_exports.string(),
60124
60137
  /** `"cancelled"` is the agent giving up on the ask, not the user denying. */
60125
- outcome: external_exports.union([external_exports.literal("cancelled"), external_exports.object({ optionId: external_exports.string() })])
60138
+ outcome: external_exports.union([external_exports.literal("cancelled"), external_exports.object({ optionId: external_exports.string() })]),
60139
+ /**
60140
+ * Who signed it (F1.6).
60141
+ *
60142
+ * `default` and not `optional`, for two reasons that point the same way: a
60143
+ * transcript recorded before this feature has no field and must still open,
60144
+ * and `"user"` is the truthful answer for every one of them — nothing but a
60145
+ * person could answer a permission request back then.
60146
+ */
60147
+ by: external_exports.enum(["user", "lumem"]).default("user"),
60148
+ /**
60149
+ * Why the policy answered, in the words the card shows.
60150
+ *
60151
+ * Only ever set when `by` is `"lumem"`: a person clicking a button is its
60152
+ * own reason, and inventing prose for it would be the daemon narrating the
60153
+ * user's intent.
60154
+ */
60155
+ reason: external_exports.string().nullable().default(null)
60126
60156
  }),
60127
60157
  /**
60128
60158
  * The plan, whole (F2.5).
@@ -60167,7 +60197,24 @@ var acpEventSchema = external_exports.discriminatedUnion("type", [
60167
60197
  * sending this. So the client replaces rather than merging, and there is exactly
60168
60198
  * one place that has to get the merge right.
60169
60199
  */
60170
- options: external_exports.array(acpConfigOptionSchema)
60200
+ options: external_exports.array(acpConfigOptionSchema),
60201
+ /**
60202
+ * Which of the two authorities is in charge right now (A1).
60203
+ *
60204
+ * Defaults to `"agent"` so a transcript recorded before `session-mode` reads
60205
+ * as what it was: a conversation where only the agent could offer a mode.
60206
+ */
60207
+ modeOwner: acpModeOwnerSchema.default("agent"),
60208
+ /**
60209
+ * The policy value, whoever owns the selector.
60210
+ *
60211
+ * Sent even while the agent owns the mode, and that is on purpose: the value
60212
+ * is *kept*, not erased, so an agent that stops reporting modes hands the
60213
+ * conversation back to the policy it already had (T12).
60214
+ */
60215
+ lumemMode: lumemModeSchema.default("ask"),
60216
+ /** What a new session in this workspace would start at — the menu's footer (Q5). */
60217
+ lumemModeDefault: lumemModeDefaultSchema.default("ask")
60171
60218
  }),
60172
60219
  /** What `/` offers (F2.8). An empty list means the agent offers none. */
60173
60220
  external_exports.object({ type: external_exports.literal("commands"), commands: external_exports.array(acpCommandSchema) }),
@@ -60265,7 +60312,17 @@ var acpClientMessageSchema = external_exports.discriminatedUnion("type", [
60265
60312
  type: external_exports.literal("set_config"),
60266
60313
  optionId: external_exports.string().min(1),
60267
60314
  value: external_exports.string().min(1)
60268
- })
60315
+ }),
60316
+ /**
60317
+ * Switch the policy Lumem applies when the agent offers no modes (F1.1).
60318
+ *
60319
+ * A message of its own rather than a `set_config` with `optionId: "mode"`,
60320
+ * because it is not the same act: `set_config` forwards a value to the agent
60321
+ * and this one never leaves the daemon. Sharing the message would put two
60322
+ * destinations behind one name, and the first bug would be a policy change
60323
+ * quietly forwarded to an agent that does not have the option.
60324
+ */
60325
+ external_exports.object({ type: external_exports.literal("set_lumem_mode"), mode: lumemModeSchema })
60269
60326
  ]);
60270
60327
  var acpServerMessageSchema = external_exports.discriminatedUnion("type", [
60271
60328
  /**
@@ -60288,6 +60345,23 @@ var acpServerMessageSchema = external_exports.discriminatedUnion("type", [
60288
60345
  * broken for as long as nothing changed.
60289
60346
  */
60290
60347
  configOptions: external_exports.array(acpConfigOptionSchema).default([]),
60348
+ /**
60349
+ * The mode state, on attach, for the same reason `configOptions` is here: a
60350
+ * composer whose mode pill appeared only once something changed would look
60351
+ * broken for as long as nothing did.
60352
+ */
60353
+ modeOwner: acpModeOwnerSchema.default("agent"),
60354
+ lumemMode: lumemModeSchema.default("ask"),
60355
+ lumemModeDefault: lumemModeDefaultSchema.default("ask"),
60356
+ /**
60357
+ * The checkout this conversation runs in.
60358
+ *
60359
+ * Here because the `free` gate has to name what it is opening, and it names
60360
+ * it as a **path on disk** rather than as "the worktree": the path is what
60361
+ * says the size of the damage (Q4). A gate that could not say it would be
60362
+ * asking for a decision without stating its scope.
60363
+ */
60364
+ cwd: external_exports.string().default(""),
60291
60365
  transcript: external_exports.array(acpTranscriptEntrySchema)
60292
60366
  }),
60293
60367
  external_exports.object({
@@ -60330,16 +60404,46 @@ function encodeAcpServerMessage(message) {
60330
60404
  return JSON.stringify(message);
60331
60405
  }
60332
60406
 
60407
+ // ../shared/src/adapters.ts
60408
+ function adapterInstallCommand(spec) {
60409
+ return spec.package === null ? null : `npm i -g ${spec.package}@${spec.pinnedVersion}`;
60410
+ }
60411
+ var CLAUDE_ADAPTER = {
60412
+ id: "claude",
60413
+ label: "Claude Code",
60414
+ package: "@agentclientprotocol/claude-agent-acp",
60415
+ // Duas strings porque não são a mesma string, e é exatamente essa a armadilha:
60416
+ // o pacote é escopado e o binário não.
60417
+ command: "claude-agent-acp",
60418
+ pinnedVersion: "0.40.0",
60419
+ cli: { command: "claude", install: null },
60420
+ apiKeyEnv: ["ANTHROPIC_API_KEY"]
60421
+ };
60422
+ var CODEX_ADAPTER = {
60423
+ id: "codex",
60424
+ label: "Codex",
60425
+ package: "@agentclientprotocol/codex-acp",
60426
+ command: "codex-acp",
60427
+ // Medido na fase 0, em 2026-09-06: é o que ele reportou como
60428
+ // `agentInfo.version`.
60429
+ pinnedVersion: "1.10.0",
60430
+ // Null, e não `{ command: "codex" }` por simetria: ele traz o próprio.
60431
+ cli: null,
60432
+ apiKeyEnv: ["CODEX_API_KEY", "OPENAI_API_KEY"]
60433
+ };
60434
+ var ADAPTERS = [CLAUDE_ADAPTER, CODEX_ADAPTER];
60435
+ var DEFAULT_ADAPTER_ID = CLAUDE_ADAPTER.id;
60436
+ function adapterById(id) {
60437
+ return ADAPTERS.find((spec) => spec.id === id) ?? null;
60438
+ }
60439
+ function adapterByCommand(command) {
60440
+ return ADAPTERS.find((spec) => spec.command === command) ?? null;
60441
+ }
60442
+
60333
60443
  // ../shared/src/constants.ts
60334
- var LUMEM_VERSION = "0.1.0";
60444
+ var LUMEM_VERSION = "0.3.0";
60335
60445
  var DEFAULT_SERVER_PORT = 4317;
60336
- var ACP_ADAPTER_COMMAND = "claude-agent-acp";
60337
- var ACP_ADAPTER_PACKAGE = "@agentclientprotocol/claude-agent-acp";
60338
- var ACP_ADAPTER_INSTALL = `npm i -g ${ACP_ADAPTER_PACKAGE}`;
60339
- var CLAUDE_CLI_COMMAND = "claude";
60340
- var ANTHROPIC_API_KEY_ENV = "ANTHROPIC_API_KEY";
60341
60446
  var MIN_GIT_VERSION = { major: 2, minor: 30 };
60342
- var ACP_ADAPTER_PINNED_VERSION = "0.40.0";
60343
60447
  var ADAPTERS_DIR_NAME = "adapters";
60344
60448
  var ACP_AUTH_REQUIRED_CODE = -32e3;
60345
60449
 
@@ -60794,6 +60898,32 @@ function createGitService({ exec = execGit } = {}) {
60794
60898
  const [behind, ahead] = stdout.trim().split(/\s+/).map(Number);
60795
60899
  return { ahead: ahead ?? 0, behind: behind ?? 0 };
60796
60900
  },
60901
+ async getRemoteUrl(path) {
60902
+ const { stdout } = await exec(["remote", "get-url", "origin"], { cwd: path }).catch(() => ({
60903
+ stdout: "",
60904
+ stderr: ""
60905
+ }));
60906
+ const url2 = stdout.trim();
60907
+ return url2 === "" ? null : url2;
60908
+ },
60909
+ async getSubject(path) {
60910
+ const { stdout } = await exec(["log", "-1", "--format=%s"], { cwd: path }).catch(() => ({
60911
+ stdout: "",
60912
+ stderr: ""
60913
+ }));
60914
+ const subject = stdout.trim();
60915
+ return subject === "" ? null : subject;
60916
+ },
60917
+ async hasRemoteBranch(path, branch) {
60918
+ const { stdout } = await exec(["for-each-ref", "--format=%(refname)", "refs/remotes/"], {
60919
+ cwd: path
60920
+ }).catch(() => ({ stdout: "", stderr: "" }));
60921
+ return stdout.split("\n").map((line) => line.trim()).filter((line) => line !== "").some((ref) => {
60922
+ const rest = ref.slice("refs/remotes/".length);
60923
+ const slash = rest.indexOf("/");
60924
+ return slash !== -1 && rest.slice(slash + 1) === branch;
60925
+ });
60926
+ },
60797
60927
  async listChanges(path, input) {
60798
60928
  const comparedTo = await resolveComparison(path, input);
60799
60929
  const tracked2 = comparedTo === null ? [] : await trackedChanges(path, comparedTo);
@@ -62026,7 +62156,7 @@ function sql(strings, ...params) {
62026
62156
  return new SQL([new StringChunk(str)]);
62027
62157
  }
62028
62158
  sql2.raw = raw;
62029
- function join20(chunks, separator) {
62159
+ function join21(chunks, separator) {
62030
62160
  const result = [];
62031
62161
  for (const [i, chunk] of chunks.entries()) {
62032
62162
  if (i > 0 && separator !== void 0) {
@@ -62036,7 +62166,7 @@ function sql(strings, ...params) {
62036
62166
  }
62037
62167
  return new SQL(result);
62038
62168
  }
62039
- sql2.join = join20;
62169
+ sql2.join = join21;
62040
62170
  function identifier(value) {
62041
62171
  return new Name(value);
62042
62172
  }
@@ -64603,7 +64733,7 @@ var SQLiteSelectQueryBuilderBase = class extends TypedQueryBuilder {
64603
64733
  const baseTableName = this.tableName;
64604
64734
  const tableName = getTableLikeName(table);
64605
64735
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
64606
- if (typeof tableName === "string" && this.config.joins?.some((join20) => join20.alias === tableName)) {
64736
+ if (typeof tableName === "string" && this.config.joins?.some((join21) => join21.alias === tableName)) {
64607
64737
  throw new Error(`Alias "${tableName}" is already used in this query`);
64608
64738
  }
64609
64739
  if (!this.isPartialSelect) {
@@ -65462,7 +65592,7 @@ var SQLiteUpdateBase = class extends QueryPromise {
65462
65592
  createJoin(joinType) {
65463
65593
  return (table, on2) => {
65464
65594
  const tableName = getTableLikeName(table);
65465
- if (typeof tableName === "string" && this.config.joins.some((join20) => join20.alias === tableName)) {
65595
+ if (typeof tableName === "string" && this.config.joins.some((join21) => join21.alias === tableName)) {
65466
65596
  throw new Error(`Alias "${tableName}" is already used in this query`);
65467
65597
  }
65468
65598
  if (typeof on2 === "function") {
@@ -66328,11 +66458,26 @@ var timestamps = {
66328
66458
  createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().default(NOW),
66329
66459
  updatedAt: integer("updated_at", { mode: "timestamp_ms" }).notNull().default(NOW)
66330
66460
  };
66331
- var workspace = sqliteTable("workspace", {
66332
- id: text("id").primaryKey(),
66333
- name: text("name").notNull().unique(),
66334
- ...timestamps
66335
- });
66461
+ var workspace = sqliteTable(
66462
+ "workspace",
66463
+ {
66464
+ id: text("id").primaryKey(),
66465
+ name: text("name").notNull().unique(),
66466
+ /**
66467
+ * O modo do Lumem que uma sessão nova deste workspace herda (`session-mode`, Q5).
66468
+ *
66469
+ * Sem `'free'` no CHECK, e isso é a decisão inteira em uma linha: o portão do
66470
+ * `liberado` é **por sessão**, e um padrão que o atravessasse sozinho o
66471
+ * anularia. Recusar na ESCRITA, e não silenciar na leitura — silenciar é o
66472
+ * modo de falha que ninguém percebe.
66473
+ */
66474
+ defaultLumemMode: text("default_lumem_mode").notNull().default("ask"),
66475
+ ...timestamps
66476
+ },
66477
+ (table) => [
66478
+ check("workspace_default_lumem_mode", sql`${table.defaultLumemMode} IN ('ask', 'auto')`)
66479
+ ]
66480
+ );
66336
66481
  var project = sqliteTable(
66337
66482
  "project",
66338
66483
  {
@@ -66479,6 +66624,18 @@ var session = sqliteTable(
66479
66624
  /** Current permission mode and model, as the protocol reports them. */
66480
66625
  mode: text("mode"),
66481
66626
  model: text("model"),
66627
+ /**
66628
+ * A política do Lumem desta conversa (`session-mode`, F1.4).
66629
+ *
66630
+ * Coluna própria, e **não** reaproveitamento do `mode` acima: aquele é o modo
66631
+ * do protocolo, relatado pelo agente, e este é o que o daemon responde a um
66632
+ * pedido de permissão. Guardar os dois no mesmo lugar seria perder a única
66633
+ * informação que separa "o agente tentou" de "o Lumem deixou passar".
66634
+ *
66635
+ * `NOT NULL DEFAULT 'ask'` faz toda sessão gravada antes desta feature
66636
+ * acordar perguntando — nenhuma acorda liberada.
66637
+ */
66638
+ lumemMode: text("lumem_mode").notNull().default("ask"),
66482
66639
  /**
66483
66640
  * The session this one continues (F5.2, D12).
66484
66641
  *
@@ -66539,7 +66696,10 @@ var session = sqliteTable(
66539
66696
  "session_acp_id",
66540
66697
  sql`(${table.transport} = 'acp' AND ${table.acpSessionId} IS NOT NULL)
66541
66698
  OR (${table.transport} = 'pty' AND ${table.acpSessionId} IS NULL)`
66542
- )
66699
+ ),
66700
+ // Os três valores da política, e o `free` entra aqui — o que o workspace não
66701
+ // pode é *herdar* liberado; uma sessão pode chegar lá pelo portão.
66702
+ check("session_lumem_mode", sql`${table.lumemMode} IN ('ask', 'auto', 'free')`)
66543
66703
  ]
66544
66704
  );
66545
66705
  var checkoutPort = sqliteTable(
@@ -66758,6 +66918,20 @@ var sessionUsage = sqliteTable(
66758
66918
  projectId: text("project_id").notNull(),
66759
66919
  /** A worktree, quando a sessão rodou numa. `''` quando ela é do projeto. */
66760
66920
  worktreeId: text("worktree_id").notNull().default(""),
66921
+ /**
66922
+ * Qual agente gastou (`second-agent`, F5).
66923
+ *
66924
+ * Resolvido na escrita, como o projeto e a worktree, e pela mesma razão: a
66925
+ * pergunta "quanto cada agente custou" não pode depender de um join com a
66926
+ * `session`, que é uma tabela que muda e de onde a linha pode sumir.
66927
+ *
66928
+ * Anulável, e sem chave estrangeira. Anulável porque a sessão de shell e a de
66929
+ * script não têm agente, e porque a linha gravada **antes** desta coluna não
66930
+ * ganha um agente inventado — ela fica de fora do agrupamento, o que é a
66931
+ * verdade. Sem estrangeira porque consumo é histórico: apagar a configuração
66932
+ * de ontem não pode apagar o que ela gastou.
66933
+ */
66934
+ agentConfigId: text("agent_config_id"),
66761
66935
  /** A variação da janela de contexto neste turno. Nunca negativa. */
66762
66936
  tokens: integer("tokens").notNull().default(0),
66763
66937
  /**
@@ -66916,95 +67090,8 @@ async function withConstraints(run3, translations) {
66916
67090
  }
66917
67091
  }
66918
67092
 
66919
- // src/repositories/agentConfig.ts
66920
- var DEFAULT_AGENT_CONFIG = {
66921
- name: "claude-code",
66922
- command: "claude",
66923
- args: [],
66924
- env: {},
66925
- transport: "pty"
66926
- };
66927
- function conflicts(name) {
66928
- return {
66929
- "unique:agent_config.name": {
66930
- code: "DUPLICATE",
66931
- message: `j\xE1 existe uma configura\xE7\xE3o chamada "${name}"`
66932
- },
66933
- foreignKey: {
66934
- code: "IN_USE",
66935
- message: "a configura\xE7\xE3o ainda est\xE1 em uso por alguma sess\xE3o"
66936
- },
66937
- // Without this the CHECK surfaces as a raw SQLite error, which reads as a
66938
- // daemon defect rather than as the one thing the caller got wrong.
66939
- "check:agent_config_adapter_version": {
66940
- code: "INVALID_ARGUMENT",
66941
- message: "configura\xE7\xE3o ACP precisa de uma vers\xE3o de adaptador fixa, e configura\xE7\xE3o PTY n\xE3o pode ter uma"
66942
- },
66943
- "check:agent_config_transport": {
66944
- code: "INVALID_ARGUMENT",
66945
- message: "transporte precisa ser pty ou acp"
66946
- }
66947
- };
66948
- }
66949
- function createAgentConfigRepository(db) {
66950
- async function require_(id) {
66951
- const found = await db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
66952
- if (!found) throw new DomainError("NOT_FOUND", `configura\xE7\xE3o ${id} n\xE3o existe`);
66953
- return found;
66954
- }
66955
- return {
66956
- async create({ name, command, args = [], env = {}, transport = "pty", adapterVersion = null }) {
66957
- const [row] = await withConstraints(
66958
- () => db.insert(agentConfig).values({ id: newId(), name, command, args, env, transport, adapterVersion }).returning(),
66959
- conflicts(name)
66960
- );
66961
- return row;
66962
- },
66963
- list() {
66964
- return db.select().from(agentConfig).orderBy(asc(agentConfig.name));
66965
- },
66966
- findById(id) {
66967
- return db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
66968
- },
66969
- findByName(name) {
66970
- return db.query.agentConfig.findFirst({ where: eq(agentConfig.name, name) });
66971
- },
66972
- async update(id, input) {
66973
- const current = await require_(id);
66974
- const [row] = await withConstraints(
66975
- () => db.update(agentConfig).set({ ...input, updatedAt: /* @__PURE__ */ new Date() }).where(eq(agentConfig.id, id)).returning(),
66976
- conflicts(input.name ?? current.name)
66977
- );
66978
- return row;
66979
- },
66980
- async remove(id) {
66981
- await require_(id);
66982
- await withConstraints(
66983
- () => db.delete(agentConfig).where(eq(agentConfig.id, id)).returning(),
66984
- conflicts("")
66985
- );
66986
- },
66987
- async seedDefaults() {
66988
- const existing = await db.query.agentConfig.findFirst({
66989
- where: eq(agentConfig.name, DEFAULT_AGENT_CONFIG.name)
66990
- });
66991
- if (existing) return;
66992
- await db.insert(agentConfig).values({
66993
- id: newId(),
66994
- name: DEFAULT_AGENT_CONFIG.name,
66995
- command: DEFAULT_AGENT_CONFIG.command,
66996
- args: DEFAULT_AGENT_CONFIG.args ?? [],
66997
- env: DEFAULT_AGENT_CONFIG.env ?? {},
66998
- // Still PTY. The default configuration changes transport when the
66999
- // conversation can render one end to end, not when the column exists.
67000
- transport: DEFAULT_AGENT_CONFIG.transport ?? "pty"
67001
- });
67002
- }
67003
- };
67004
- }
67005
-
67006
67093
  // src/repositories/project.ts
67007
- function conflicts2(name, path) {
67094
+ function conflicts(name, path) {
67008
67095
  return {
67009
67096
  "unique:project.workspace_id,project.name": {
67010
67097
  code: "DUPLICATE",
@@ -67032,7 +67119,7 @@ function createProjectRepository(db) {
67032
67119
  async create(input) {
67033
67120
  const [row] = await withConstraints(
67034
67121
  () => db.insert(project).values({ id: newId(), ...input }).returning(),
67035
- conflicts2(input.name, input.path)
67122
+ conflicts(input.name, input.path)
67036
67123
  );
67037
67124
  return row;
67038
67125
  },
@@ -67053,7 +67140,7 @@ function createProjectRepository(db) {
67053
67140
  await require_(id);
67054
67141
  const [row] = await withConstraints(
67055
67142
  () => db.update(project).set({ name, updatedAt: /* @__PURE__ */ new Date() }).where(eq(project.id, id)).returning(),
67056
- conflicts2(name)
67143
+ conflicts(name)
67057
67144
  );
67058
67145
  return row;
67059
67146
  },
@@ -67063,11 +67150,9 @@ function createProjectRepository(db) {
67063
67150
  },
67064
67151
  async remove(id) {
67065
67152
  await require_(id);
67066
- await withConstraints(() => db.delete(project).where(eq(project.id, id)).returning(), {
67067
- foreignKey: {
67068
- code: "IN_USE",
67069
- message: "o projeto ainda tem worktrees registradas; remova-as antes"
67070
- }
67153
+ db.transaction((tx) => {
67154
+ tx.delete(worktree).where(eq(worktree.projectId, id)).run();
67155
+ tx.delete(project).where(eq(project.id, id)).run();
67071
67156
  });
67072
67157
  }
67073
67158
  };
@@ -67131,6 +67216,10 @@ function createSessionRepository(db) {
67131
67216
  await db.update(session).set({
67132
67217
  ...config2.mode === void 0 ? {} : { mode: config2.mode },
67133
67218
  ...config2.model === void 0 ? {} : { model: config2.model },
67219
+ // A política do Lumem entra pelo mesmo caminho do modo do protocolo,
67220
+ // e em coluna diferente: uma é o que o agente relatou, a outra é o que
67221
+ // o daemon responde. Guardar as duas juntas apagaria a diferença.
67222
+ ...config2.lumemMode === void 0 ? {} : { lumemMode: config2.lumemMode },
67134
67223
  updatedAt: /* @__PURE__ */ new Date()
67135
67224
  }).where(and(eq(session.id, id), eq(session.state, "running")));
67136
67225
  },
@@ -67201,7 +67290,7 @@ function createWorkspaceRepository(db) {
67201
67290
  }
67202
67291
 
67203
67292
  // src/repositories/worktree.ts
67204
- function conflicts3(name) {
67293
+ function conflicts2(name) {
67205
67294
  return {
67206
67295
  "unique:worktree.project_id,worktree.name": {
67207
67296
  code: "DUPLICATE",
@@ -67224,7 +67313,7 @@ function createWorktreeRepository(db) {
67224
67313
  async create(input) {
67225
67314
  const [row] = await withConstraints(
67226
67315
  () => db.insert(worktree).values({ id: newId(), ...input }).returning(),
67227
- conflicts3(input.name)
67316
+ conflicts2(input.name)
67228
67317
  );
67229
67318
  return row;
67230
67319
  },
@@ -67242,7 +67331,7 @@ function createWorktreeRepository(db) {
67242
67331
  await require_(id);
67243
67332
  const [row] = await withConstraints(
67244
67333
  () => db.update(worktree).set({ state, updatedAt: /* @__PURE__ */ new Date() }).where(eq(worktree.id, id)).returning(),
67245
- conflicts3("")
67334
+ conflicts2("")
67246
67335
  );
67247
67336
  return row;
67248
67337
  },
@@ -67250,7 +67339,7 @@ function createWorktreeRepository(db) {
67250
67339
  await require_(id);
67251
67340
  const [row] = await withConstraints(
67252
67341
  () => db.update(worktree).set({ path, updatedAt: /* @__PURE__ */ new Date() }).where(eq(worktree.id, id)).returning(),
67253
- conflicts3("")
67342
+ conflicts2("")
67254
67343
  );
67255
67344
  return row;
67256
67345
  },
@@ -67468,7 +67557,6 @@ async function subdirectories(path) {
67468
67557
  return entries.filter((entry) => entry.isDirectory()).map((entry) => join4(path, entry.name));
67469
67558
  }
67470
67559
  async function reconcileOnBoot(options) {
67471
- await createAgentConfigRepository(options.db).seedDefaults();
67472
67560
  const layout = await migrateWorktreeLayout(options);
67473
67561
  const clones = await reconcileClones(options).catch(() => 0);
67474
67562
  const worktrees = await reconcileWorktrees(options);
@@ -81018,11 +81106,11 @@ var Connection = class {
81018
81106
  const id = this.nextRequestId++;
81019
81107
  let cancel = () => {
81020
81108
  };
81021
- const response = new Promise((resolve5, reject) => {
81109
+ const response = new Promise((resolve6, reject) => {
81022
81110
  const pendingResponse = {
81023
81111
  resolve: (value) => {
81024
81112
  try {
81025
- resolve5(mapResponse ? mapResponse(value) : value);
81113
+ resolve6(mapResponse ? mapResponse(value) : value);
81026
81114
  } catch (error40) {
81027
81115
  reject(error40);
81028
81116
  }
@@ -81079,8 +81167,8 @@ var Connection = class {
81079
81167
  this.stream = stream;
81080
81168
  this.staticHandlers = handlers2;
81081
81169
  this.allowBatches = options?.allowBatches ?? true;
81082
- this.closedPromise = new Promise((resolve5) => {
81083
- this.abortController.signal.addEventListener("abort", () => resolve5());
81170
+ this.closedPromise = new Promise((resolve6) => {
81171
+ this.abortController.signal.addEventListener("abort", () => resolve6());
81084
81172
  });
81085
81173
  void this.receive();
81086
81174
  }
@@ -81961,8 +82049,8 @@ var AsyncQueue = class {
81961
82049
  if (this.failed) {
81962
82050
  return Promise.reject(this.failure);
81963
82051
  }
81964
- return new Promise((resolve5, reject) => {
81965
- this.waiters.push({ resolve: resolve5, reject });
82052
+ return new Promise((resolve6, reject) => {
82053
+ this.waiters.push({ resolve: resolve6, reject });
81966
82054
  });
81967
82055
  }
81968
82056
  };
@@ -82566,6 +82654,9 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
82566
82654
  CLIENT_METHODS.elicitation_complete
82567
82655
  ]);
82568
82656
 
82657
+ // src/acp/AcpManager.ts
82658
+ import { basename as basename3 } from "node:path";
82659
+
82569
82660
  // src/agents/availability.ts
82570
82661
  import { accessSync, constants } from "node:fs";
82571
82662
  import { delimiter, isAbsolute, join as join5 } from "node:path";
@@ -82654,27 +82745,27 @@ function touchesGit(candidate) {
82654
82745
  return candidate.split(sep2).includes(GIT_DIR);
82655
82746
  }
82656
82747
  async function resolveInsideRoot(root, requested) {
82657
- const relative4 = normalizeRelative(requested);
82748
+ const relative5 = normalizeRelative(requested);
82658
82749
  const realRoot = await realRootOf(root);
82659
- const target = join6(realRoot, relative4);
82750
+ const target = join6(realRoot, relative5);
82660
82751
  let absolute;
82661
82752
  try {
82662
82753
  absolute = await realpath3(target);
82663
82754
  } catch (error40) {
82664
82755
  const code = error40.code;
82665
82756
  if (code === "EACCES" || code === "EPERM") {
82666
- throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative4 || "."}`);
82757
+ throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative5 || "."}`);
82667
82758
  }
82668
- throw new DomainError("NOT_FOUND", `${relative4 || "."} n\xE3o existe no checkout`);
82759
+ throw new DomainError("NOT_FOUND", `${relative5 || "."} n\xE3o existe no checkout`);
82669
82760
  }
82670
82761
  if (!inside(realRoot, absolute)) {
82671
82762
  throw new DomainError(
82672
82763
  "BLOCKED",
82673
- target === absolute ? `${relative4} est\xE1 fora do checkout` : `${relative4} aponta para fora do checkout`
82764
+ target === absolute ? `${relative5} est\xE1 fora do checkout` : `${relative5} aponta para fora do checkout`
82674
82765
  );
82675
82766
  }
82676
- const insideGit = touchesGit(relative4) || touchesGit(relativeTo(realRoot, absolute));
82677
- return { absolute, relative: relative4, insideGit };
82767
+ const insideGit = touchesGit(relative5) || touchesGit(relativeTo(realRoot, absolute));
82768
+ return { absolute, relative: relative5, insideGit };
82678
82769
  }
82679
82770
  function refuseWritingIntoGit(shown, candidate) {
82680
82771
  if (!touchesGit(candidate)) return;
@@ -82690,13 +82781,13 @@ async function diskNameOf(parent, requested) {
82690
82781
  return names.find((name) => name.toLowerCase() === folded) ?? requested;
82691
82782
  }
82692
82783
  async function resolveForWrite(root, requested) {
82693
- const relative4 = normalizeRelative(requested);
82694
- if (relative4 === "") {
82784
+ const relative5 = normalizeRelative(requested);
82785
+ if (relative5 === "") {
82695
82786
  throw new DomainError("INVALID_ARGUMENT", "a raiz do checkout n\xE3o aceita escrita");
82696
82787
  }
82697
- refuseWritingIntoGit(relative4, relative4);
82788
+ refuseWritingIntoGit(relative5, relative5);
82698
82789
  const realRoot = await realRootOf(root);
82699
- const parentRelative = dirname4(relative4) === "." ? "" : dirname4(relative4);
82790
+ const parentRelative = dirname4(relative5) === "." ? "" : dirname4(relative5);
82700
82791
  const literalParent = join6(realRoot, parentRelative);
82701
82792
  let parent;
82702
82793
  try {
@@ -82717,7 +82808,7 @@ async function resolveForWrite(root, requested) {
82717
82808
  literalParent === parent ? `${parentRelative} est\xE1 fora do checkout` : `${parentRelative} aponta para fora do checkout`
82718
82809
  );
82719
82810
  }
82720
- const literalTarget = join6(parent, basename(relative4));
82811
+ const literalTarget = join6(parent, basename(relative5));
82721
82812
  let entry = literalTarget;
82722
82813
  let target = literalTarget;
82723
82814
  let targetless = null;
@@ -82728,7 +82819,7 @@ async function resolveForWrite(root, requested) {
82728
82819
  entry = await realpath3(literalTarget);
82729
82820
  target = entry;
82730
82821
  } else {
82731
- entry = join6(parent, await diskNameOf(parent, basename(relative4)));
82822
+ entry = join6(parent, await diskNameOf(parent, basename(relative5)));
82732
82823
  const destination = await realpath3(literalTarget).catch(() => null);
82733
82824
  if (destination === null) {
82734
82825
  target = null;
@@ -82747,16 +82838,16 @@ async function resolveForWrite(root, requested) {
82747
82838
  throw new DomainError("INVALID_ARGUMENT", `${parentRelative || "."} n\xE3o \xE9 um diret\xF3rio`);
82748
82839
  }
82749
82840
  if (code !== "ENOENT") {
82750
- throw new DomainError("BLOCKED", `sem acesso a ${relative4}`);
82841
+ throw new DomainError("BLOCKED", `sem acesso a ${relative5}`);
82751
82842
  }
82752
82843
  exists2 = false;
82753
82844
  }
82754
- refuseWritingIntoGit(relative4, relativeTo(realRoot, entry));
82755
- if (target !== null) refuseWritingIntoGit(relative4, relativeTo(realRoot, target));
82845
+ refuseWritingIntoGit(relative5, relativeTo(realRoot, entry));
82846
+ if (target !== null) refuseWritingIntoGit(relative5, relativeTo(realRoot, target));
82756
82847
  if (target === realRoot) {
82757
82848
  throw new DomainError("INVALID_ARGUMENT", "a raiz do checkout n\xE3o aceita escrita");
82758
82849
  }
82759
- return { relative: relative4, entry, target, targetless, exists: exists2 };
82850
+ return { relative: relative5, entry, target, targetless, exists: exists2 };
82760
82851
  }
82761
82852
 
82762
82853
  // src/files/FileService.ts
@@ -82808,11 +82899,11 @@ async function writeAtomically(target, content, mode) {
82808
82899
  throw error40;
82809
82900
  }
82810
82901
  }
82811
- function asCreationFailure(error40, relative4) {
82902
+ function asCreationFailure(error40, relative5) {
82812
82903
  const code = error40.code;
82813
- const parent = dirname5(relative4) === "." ? "." : dirname5(relative4);
82904
+ const parent = dirname5(relative5) === "." ? "." : dirname5(relative5);
82814
82905
  if (code === "EEXIST") {
82815
- return new DomainError("DUPLICATE", `j\xE1 existe alguma coisa em ${relative4}`);
82906
+ return new DomainError("DUPLICATE", `j\xE1 existe alguma coisa em ${relative5}`);
82816
82907
  }
82817
82908
  if (code === "ENOENT") {
82818
82909
  return new DomainError("NOT_FOUND", `o diret\xF3rio ${parent} n\xE3o existe no checkout`);
@@ -82847,16 +82938,16 @@ function asRenameFailure(error40, from, to) {
82847
82938
  }
82848
82939
  return error40;
82849
82940
  }
82850
- function asRemovalFailure(error40, relative4) {
82941
+ function asRemovalFailure(error40, relative5) {
82851
82942
  const code = error40.code;
82852
82943
  if (code === "ENOENT") {
82853
- return new DomainError("NOT_FOUND", `${relative4} n\xE3o est\xE1 mais no checkout`);
82944
+ return new DomainError("NOT_FOUND", `${relative5} n\xE3o est\xE1 mais no checkout`);
82854
82945
  }
82855
82946
  if (code === "ENOTEMPTY") {
82856
- return new DomainError("BLOCKED", `${relative4} deixou de estar vazia`);
82947
+ return new DomainError("BLOCKED", `${relative5} deixou de estar vazia`);
82857
82948
  }
82858
82949
  if (code === "EACCES" || code === "EPERM") {
82859
- return new DomainError("BLOCKED", `sem permiss\xE3o para apagar ${relative4}`);
82950
+ return new DomainError("BLOCKED", `sem permiss\xE3o para apagar ${relative5}`);
82860
82951
  }
82861
82952
  return error40;
82862
82953
  }
@@ -82922,10 +83013,10 @@ function createFileService({
82922
83013
  } = {}) {
82923
83014
  return {
82924
83015
  async listDir(root, path, options = {}) {
82925
- const { absolute, relative: relative4 } = await resolveInsideRoot(root, path);
83016
+ const { absolute, relative: relative5 } = await resolveInsideRoot(root, path);
82926
83017
  const info = await stat2(absolute);
82927
83018
  if (!info.isDirectory()) {
82928
- throw new DomainError("INVALID_ARGUMENT", `${relative4} n\xE3o \xE9 um diret\xF3rio`);
83019
+ throw new DomainError("INVALID_ARGUMENT", `${relative5} n\xE3o \xE9 um diret\xF3rio`);
82929
83020
  }
82930
83021
  let dirents;
82931
83022
  try {
@@ -82933,7 +83024,7 @@ function createFileService({
82933
83024
  } catch (error40) {
82934
83025
  const code = error40.code;
82935
83026
  if (code === "EACCES" || code === "EPERM") {
82936
- throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative4 || "."}`);
83027
+ throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative5 || "."}`);
82937
83028
  }
82938
83029
  throw error40;
82939
83030
  }
@@ -82957,16 +83048,16 @@ function createFileService({
82957
83048
  })
82958
83049
  );
82959
83050
  entries.sort(compareEntries);
82960
- return { path: relative4, entries, total, truncated: total > kept.length };
83051
+ return { path: relative5, entries, total, truncated: total > kept.length };
82961
83052
  },
82962
83053
  async readFile(root, path) {
82963
- const { absolute, relative: relative4, insideGit } = await resolveInsideRoot(root, path);
83054
+ const { absolute, relative: relative5, insideGit } = await resolveInsideRoot(root, path);
82964
83055
  const info = await stat2(absolute);
82965
83056
  if (info.isDirectory()) {
82966
- throw new DomainError("INVALID_ARGUMENT", `${relative4} \xE9 um diret\xF3rio`);
83057
+ throw new DomainError("INVALID_ARGUMENT", `${relative5} \xE9 um diret\xF3rio`);
82967
83058
  }
82968
83059
  if (info.size > maxBytes) {
82969
- return { kind: "too-large", path: relative4, bytes: info.size, limit: maxBytes };
83060
+ return { kind: "too-large", path: relative5, bytes: info.size, limit: maxBytes };
82970
83061
  }
82971
83062
  let buffer;
82972
83063
  try {
@@ -82974,16 +83065,16 @@ function createFileService({
82974
83065
  } catch (error40) {
82975
83066
  const code = error40.code;
82976
83067
  if (code === "EACCES" || code === "EPERM") {
82977
- throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative4}`);
83068
+ throw new DomainError("BLOCKED", `sem permiss\xE3o de leitura em ${relative5}`);
82978
83069
  }
82979
83070
  throw error40;
82980
83071
  }
82981
- if (isBinary(buffer)) return { kind: "binary", path: relative4, bytes: buffer.length };
83072
+ if (isBinary(buffer)) return { kind: "binary", path: relative5, bytes: buffer.length };
82982
83073
  const text2 = buffer.toString("utf8");
82983
83074
  const readOnly = await readOnlyReasonFor(absolute, buffer, text2, insideGit);
82984
83075
  return {
82985
83076
  kind: "text",
82986
- path: relative4,
83077
+ path: relative5,
82987
83078
  bytes: buffer.length,
82988
83079
  lines: countLines(text2),
82989
83080
  text: text2,
@@ -82992,15 +83083,15 @@ function createFileService({
82992
83083
  };
82993
83084
  },
82994
83085
  async writeFile(root, path, { text: text2, baseRevision }) {
82995
- const { relative: relative4, target, targetless, exists: exists2 } = await resolveForWrite(root, path);
83086
+ const { relative: relative5, target, targetless, exists: exists2 } = await resolveForWrite(root, path);
82996
83087
  if (target === null) {
82997
83088
  throw new DomainError(
82998
83089
  "BLOCKED",
82999
- targetless === "outside" ? `escrita recusada em ${relative4}: o link aponta para fora do checkout` : `escrita recusada em ${relative4}: o link est\xE1 pendurado, e nada prova onde a grava\xE7\xE3o cairia`
83090
+ targetless === "outside" ? `escrita recusada em ${relative5}: o link aponta para fora do checkout` : `escrita recusada em ${relative5}: o link est\xE1 pendurado, e nada prova onde a grava\xE7\xE3o cairia`
83000
83091
  );
83001
83092
  }
83002
83093
  if (!exists2) {
83003
- throw new DomainError("NOT_FOUND", `${relative4} n\xE3o existe no checkout`);
83094
+ throw new DomainError("NOT_FOUND", `${relative5} n\xE3o existe no checkout`);
83004
83095
  }
83005
83096
  const buffer = Buffer.from(text2, "utf8");
83006
83097
  if (buffer.length > maxBytes) {
@@ -83012,22 +83103,22 @@ function createFileService({
83012
83103
  const info = await stat2(target).catch((error40) => {
83013
83104
  throw new DomainError(
83014
83105
  error40.code === "EACCES" || error40.code === "EPERM" ? "BLOCKED" : "NOT_FOUND",
83015
- `${relative4} n\xE3o est\xE1 mais no checkout`
83106
+ `${relative5} n\xE3o est\xE1 mais no checkout`
83016
83107
  );
83017
83108
  });
83018
83109
  if (info.isDirectory()) {
83019
- throw new DomainError("INVALID_ARGUMENT", `${relative4} \xE9 um diret\xF3rio`);
83110
+ throw new DomainError("INVALID_ARGUMENT", `${relative5} \xE9 um diret\xF3rio`);
83020
83111
  }
83021
83112
  if (info.size > maxBytes) {
83022
83113
  throw new DomainError(
83023
83114
  "BLOCKED",
83024
- `${relative4} tem ${info.size} bytes no disco, acima do limite de ${maxBytes}`
83115
+ `${relative5} tem ${info.size} bytes no disco, acima do limite de ${maxBytes}`
83025
83116
  );
83026
83117
  }
83027
83118
  if (!await isWritable(target)) {
83028
83119
  throw new DomainError(
83029
83120
  "BLOCKED",
83030
- `escrita recusada em ${relative4}: o arquivo \xE9 somente leitura no disco`
83121
+ `escrita recusada em ${relative5}: o arquivo \xE9 somente leitura no disco`
83031
83122
  );
83032
83123
  }
83033
83124
  let current;
@@ -83036,14 +83127,14 @@ function createFileService({
83036
83127
  } catch (error40) {
83037
83128
  const code = error40.code;
83038
83129
  if (code === "EACCES" || code === "EPERM") {
83039
- throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${relative4}`);
83130
+ throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${relative5}`);
83040
83131
  }
83041
83132
  throw error40;
83042
83133
  }
83043
83134
  if (!survivesUtf8(current, current.toString("utf8"))) {
83044
83135
  throw new DomainError(
83045
83136
  "BLOCKED",
83046
- `escrita recusada em ${relative4}: o conte\xFAdo no disco n\xE3o sobrevive a uma ida e volta em UTF-8, e gravar destruiria bytes`
83137
+ `escrita recusada em ${relative5}: o conte\xFAdo no disco n\xE3o sobrevive a uma ida e volta em UTF-8, e gravar destruiria bytes`
83047
83138
  );
83048
83139
  }
83049
83140
  const revision = revisionOf(current);
@@ -83055,29 +83146,29 @@ function createFileService({
83055
83146
  } catch (error40) {
83056
83147
  const code = error40.code;
83057
83148
  if (code === "EACCES" || code === "EPERM") {
83058
- throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${relative4}`);
83149
+ throw new DomainError("BLOCKED", `sem permiss\xE3o de escrita em ${relative5}`);
83059
83150
  }
83060
83151
  throw error40;
83061
83152
  }
83062
83153
  return { ok: true, revision: revisionOf(buffer) };
83063
83154
  },
83064
83155
  async createFile(root, path) {
83065
- const { relative: relative4, entry } = await resolveForWrite(root, path);
83156
+ const { relative: relative5, entry } = await resolveForWrite(root, path);
83066
83157
  try {
83067
83158
  await writeFile(entry, "", { flag: "wx" });
83068
83159
  } catch (error40) {
83069
- throw asCreationFailure(error40, relative4);
83160
+ throw asCreationFailure(error40, relative5);
83070
83161
  }
83071
- return { path: relative4 };
83162
+ return { path: relative5 };
83072
83163
  },
83073
83164
  async createDir(root, path) {
83074
- const { relative: relative4, entry } = await resolveForWrite(root, path);
83165
+ const { relative: relative5, entry } = await resolveForWrite(root, path);
83075
83166
  try {
83076
83167
  await mkdir3(entry);
83077
83168
  } catch (error40) {
83078
- throw asCreationFailure(error40, relative4);
83169
+ throw asCreationFailure(error40, relative5);
83079
83170
  }
83080
- return { path: relative4 };
83171
+ return { path: relative5 };
83081
83172
  },
83082
83173
  async rename(root, from, to) {
83083
83174
  const source = await resolveForWrite(root, from);
@@ -83102,50 +83193,50 @@ function createFileService({
83102
83193
  return { path: destination.relative };
83103
83194
  },
83104
83195
  async remove(root, path, { recursive = false } = {}) {
83105
- const { relative: relative4, entry, exists: exists2 } = await resolveForWrite(root, path);
83196
+ const { relative: relative5, entry, exists: exists2 } = await resolveForWrite(root, path);
83106
83197
  if (!exists2) {
83107
- throw new DomainError("NOT_FOUND", `${relative4} n\xE3o existe no checkout`);
83198
+ throw new DomainError("NOT_FOUND", `${relative5} n\xE3o existe no checkout`);
83108
83199
  }
83109
83200
  const info = await lstat3(entry).catch((error40) => {
83110
- throw asRemovalFailure(error40, relative4);
83201
+ throw asRemovalFailure(error40, relative5);
83111
83202
  });
83112
83203
  if (!info.isDirectory()) {
83113
83204
  await unlink(entry).catch((error40) => {
83114
- throw asRemovalFailure(error40, relative4);
83205
+ throw asRemovalFailure(error40, relative5);
83115
83206
  });
83116
83207
  return;
83117
83208
  }
83118
83209
  if (recursive) {
83119
83210
  await rm2(entry, { recursive: true }).catch((error40) => {
83120
- throw asRemovalFailure(error40, relative4);
83211
+ throw asRemovalFailure(error40, relative5);
83121
83212
  });
83122
83213
  return;
83123
83214
  }
83124
- const inside2 = await readdir4(entry).catch((error40) => {
83125
- throw asRemovalFailure(error40, relative4);
83215
+ const inside3 = await readdir4(entry).catch((error40) => {
83216
+ throw asRemovalFailure(error40, relative5);
83126
83217
  });
83127
- if (inside2.length > 0) {
83218
+ if (inside3.length > 0) {
83128
83219
  throw new DomainError(
83129
83220
  "BLOCKED",
83130
- `a pasta ${relative4} tem ${inside2.length} ${inside2.length === 1 ? "entrada" : "entradas"} dentro; apagar assim mesmo exige recursive`
83221
+ `a pasta ${relative5} tem ${inside3.length} ${inside3.length === 1 ? "entrada" : "entradas"} dentro; apagar assim mesmo exige recursive`
83131
83222
  );
83132
83223
  }
83133
83224
  await rmdir(entry).catch((error40) => {
83134
- throw asRemovalFailure(error40, relative4);
83225
+ throw asRemovalFailure(error40, relative5);
83135
83226
  });
83136
83227
  },
83137
83228
  async deletePreview(root, path) {
83138
- const { relative: relative4, entry, exists: exists2 } = await resolveForWrite(root, path);
83229
+ const { relative: relative5, entry, exists: exists2 } = await resolveForWrite(root, path);
83139
83230
  if (!exists2) {
83140
- throw new DomainError("NOT_FOUND", `${relative4} n\xE3o existe no checkout`);
83231
+ throw new DomainError("NOT_FOUND", `${relative5} n\xE3o existe no checkout`);
83141
83232
  }
83142
83233
  const info = await lstat3(entry).catch((error40) => {
83143
- throw asRemovalFailure(error40, relative4);
83234
+ throw asRemovalFailure(error40, relative5);
83144
83235
  });
83145
83236
  if (!info.isDirectory()) {
83146
- return { kind: "file", path: relative4, tracked: await isTracked(entry) };
83237
+ return { kind: "file", path: relative5, tracked: await isTracked(entry) };
83147
83238
  }
83148
- return { kind: "dir", path: relative4, ...await countTree(entry, maxPreviewEntries) };
83239
+ return { kind: "dir", path: relative5, ...await countTree(entry, maxPreviewEntries) };
83149
83240
  }
83150
83241
  };
83151
83242
  }
@@ -83165,8 +83256,8 @@ function createFsBridge({ files: files2, root }) {
83165
83256
  }
83166
83257
  return candidate;
83167
83258
  }
83168
- async function ensureParents(relative4) {
83169
- const parts = relative4.split(sep3).slice(0, -1);
83259
+ async function ensureParents(relative5) {
83260
+ const parts = relative5.split(sep3).slice(0, -1);
83170
83261
  let walked = "";
83171
83262
  for (const part of parts) {
83172
83263
  walked = walked === "" ? part : `${walked}${sep3}${part}`;
@@ -83196,16 +83287,16 @@ function createFsBridge({ files: files2, root }) {
83196
83287
  return sliceWindow(content.text, window2);
83197
83288
  },
83198
83289
  async write(path, content) {
83199
- const relative4 = toRelative2(path);
83200
- const existing = await files2.readFile(absoluteRoot, relative4).catch((error40) => {
83290
+ const relative5 = toRelative2(path);
83291
+ const existing = await files2.readFile(absoluteRoot, relative5).catch((error40) => {
83201
83292
  if (error40 instanceof DomainError && error40.code === "NOT_FOUND") return null;
83202
83293
  throw error40;
83203
83294
  });
83204
83295
  let before = existing;
83205
83296
  if (before === null) {
83206
- await ensureParents(relative4);
83207
- await files2.createFile(absoluteRoot, relative4);
83208
- before = await files2.readFile(absoluteRoot, relative4);
83297
+ await ensureParents(relative5);
83298
+ await files2.createFile(absoluteRoot, relative5);
83299
+ before = await files2.readFile(absoluteRoot, relative5);
83209
83300
  }
83210
83301
  if (before.kind !== "text") {
83211
83302
  throw new DomainError(
@@ -83213,7 +83304,7 @@ function createFsBridge({ files: files2, root }) {
83213
83304
  `escrita recusada em ${path}: o conte\xFAdo atual n\xE3o \xE9 texto`
83214
83305
  );
83215
83306
  }
83216
- const result = await files2.writeFile(absoluteRoot, relative4, {
83307
+ const result = await files2.writeFile(absoluteRoot, relative5, {
83217
83308
  text: content,
83218
83309
  baseRevision: before.revision
83219
83310
  });
@@ -83271,11 +83362,11 @@ function createTerminalBridge({
83271
83362
  const entry = {
83272
83363
  ptySessionId: spawned.id,
83273
83364
  settled: null,
83274
- exited: new Promise((resolve5) => {
83365
+ exited: new Promise((resolve6) => {
83275
83366
  ptyManager.onExit(spawned.id, ({ exitCode, signal }) => {
83276
83367
  const status = { exitCode, signal: signal === null ? null : String(signal) };
83277
83368
  entry.settled = status;
83278
- resolve5(status);
83369
+ resolve6(status);
83279
83370
  });
83280
83371
  })
83281
83372
  };
@@ -83322,9 +83413,9 @@ function spawnAcpProcess({ command, args, cwd, env }) {
83322
83413
  env: { ...process.env, ...env },
83323
83414
  stdio: ["pipe", "pipe", "inherit"]
83324
83415
  });
83325
- const exited = new Promise((resolve5) => {
83326
- child.once("exit", (exitCode, signal) => resolve5({ exitCode, signal }));
83327
- child.once("error", () => resolve5({ exitCode: null, signal: null }));
83416
+ const exited = new Promise((resolve6) => {
83417
+ child.once("exit", (exitCode, signal) => resolve6({ exitCode, signal }));
83418
+ child.once("error", () => resolve6({ exitCode: null, signal: null }));
83328
83419
  });
83329
83420
  if (!child.stdin || !child.stdout) {
83330
83421
  throw new DomainError("SPAWN_FAILED", `${command} started without a usable stdio pipe`);
@@ -83339,6 +83430,54 @@ function spawnAcpProcess({ command, args, cwd, env }) {
83339
83430
  };
83340
83431
  }
83341
83432
 
83433
+ // src/acp/permission-policy.ts
83434
+ import { isAbsolute as isAbsolute4, relative, resolve as resolve3 } from "node:path";
83435
+ var READ = "read";
83436
+ var AUTO_ONLY_READS = true;
83437
+ function decidePermission(mode, cwd, call) {
83438
+ if (mode === "ask") {
83439
+ return { approve: false, reason: null };
83440
+ }
83441
+ if (mode === "auto" && !isReadInside(cwd, call)) {
83442
+ return { approve: false, reason: whyNotAuto(cwd, call) };
83443
+ }
83444
+ const allow = call.options.find((option) => option.kind === "allow_once");
83445
+ if (!allow) {
83446
+ return {
83447
+ approve: false,
83448
+ reason: "o modo do Lumem aprovaria isto, mas o agente n\xE3o ofereceu nenhuma op\xE7\xE3o de permitir uma vez"
83449
+ };
83450
+ }
83451
+ return {
83452
+ approve: true,
83453
+ optionId: allow.optionId,
83454
+ reason: mode === "free" ? "Modo Liberado: nada \xE9 perguntado dentro deste checkout." : "Modo Autom\xE1tico: leitura de arquivo, caminho dentro do checkout."
83455
+ };
83456
+ }
83457
+ function isReadInside(cwd, call) {
83458
+ if (AUTO_ONLY_READS && call.kind !== READ) return false;
83459
+ if (call.locations.length === 0) return false;
83460
+ return call.locations.every((location) => inside2(cwd, location.path));
83461
+ }
83462
+ function whyNotAuto(cwd, call) {
83463
+ if (call.kind !== READ) {
83464
+ return `O modo Autom\xE1tico do Lumem aprova sozinho s\xF3 leitura de arquivo. Esta chamada \xE9 "${call.kind}".`;
83465
+ }
83466
+ if (call.locations.length === 0) {
83467
+ return "O modo Autom\xE1tico do Lumem precisa saber qual arquivo: esta leitura n\xE3o disse nenhum caminho.";
83468
+ }
83469
+ const outside = call.locations.find((location) => !inside2(cwd, location.path));
83470
+ return `O modo Autom\xE1tico do Lumem aprova sozinho s\xF3 dentro do checkout, e "${outside?.path ?? ""}" est\xE1 fora.`;
83471
+ }
83472
+ function inside2(root, path) {
83473
+ if (root === "") return false;
83474
+ const from = resolve3(root);
83475
+ const to = resolve3(from, path);
83476
+ if (to === from) return true;
83477
+ const step = relative(from, to);
83478
+ return step !== "" && !step.startsWith("..") && !isAbsolute4(step);
83479
+ }
83480
+
83342
83481
  // src/acp/translate.ts
83343
83482
  var TOOL_STATUS = {
83344
83483
  pending: "pending",
@@ -83653,6 +83792,10 @@ function sniffUnknownUpdates(source, { onUnknown }) {
83653
83792
  var ACP_PROTOCOL_VERSION = 1;
83654
83793
  var LUMEM_CLIENT_VERSION = "0.1.0";
83655
83794
  var DEFAULT_HANDSHAKE_TIMEOUT_MS = 15e3;
83795
+ function modeOwnerOf(info) {
83796
+ if (info.mode !== "") return "agent";
83797
+ return info.configOptions.some((option) => option.id === MODE_OPTION) ? "agent" : "lumem";
83798
+ }
83656
83799
  var AcpManager = class {
83657
83800
  sessions = /* @__PURE__ */ new Map();
83658
83801
  exitWatchers = /* @__PURE__ */ new Set();
@@ -83761,6 +83904,82 @@ var AcpManager = class {
83761
83904
  * the spike measured both at zero: nothing is generated until `session/prompt`,
83762
83905
  * which never happens here.
83763
83906
  */
83907
+ /**
83908
+ * Entrar no agente, pelo método que o próprio agente ofereceu (F2, T10).
83909
+ *
83910
+ * O caminho que a `agent-login` construiu roda um **comando** num PTY, e ele
83911
+ * continua valendo — é o que o `claude-agent-acp` oferece. Medido na fase 0
83912
+ * (§4.2): o `codex-acp` não oferece comando nenhum. Os métodos dele são
83913
+ * `api-key`, `chat-gpt` e `chat-gpt-device-code`, e os três se atravessam por
83914
+ * **uma chamada**.
83915
+ *
83916
+ * Um processo por tentativa, e ele morre no fim. A credencial não fica aqui: é
83917
+ * o adaptador que a escreve na casa dele — `~/.codex`, no caso — e é por isso
83918
+ * que um processo curto basta. O que o Lumem guarda de tudo isto é nada.
83919
+ *
83920
+ * **Sem `withTimeout` no `authenticate`.** Todos os outros passos do protocolo
83921
+ * são conversa entre dois programas e um limite de 15 s é generoso; este espera
83922
+ * uma pessoa autorizar em outro lugar, e um limite aqui seria o daemon
83923
+ * desistindo de alguém que está digitando um código. Quem desiste é quem
83924
+ * clicou: o `signal` mata o processo.
83925
+ */
83926
+ async authenticate(options) {
83927
+ if (options.methodId.trim() === "") {
83928
+ throw new DomainError("INVALID_ARGUMENT", "sem o m\xE9todo n\xE3o h\xE1 como entrar");
83929
+ }
83930
+ const { session: session2, child } = this.launch(options, { probe: true });
83931
+ session2.elicit = options.onElicitation;
83932
+ session2.elicitDone = options.onElicitationDone;
83933
+ const abort = () => child.kill();
83934
+ options.signal?.addEventListener("abort", abort, { once: true });
83935
+ try {
83936
+ const initialize = await this.initialize(session2);
83937
+ if (initialize.protocolVersion !== ACP_PROTOCOL_VERSION) {
83938
+ throw new DomainError(
83939
+ "SPAWN_FAILED",
83940
+ `o adaptador fala a vers\xE3o ${initialize.protocolVersion} do protocolo, e este daemon fala a ${ACP_PROTOCOL_VERSION}`
83941
+ );
83942
+ }
83943
+ const offered = (initialize.authMethods ?? []).some(
83944
+ (method) => method.id === options.methodId
83945
+ );
83946
+ if (!offered) {
83947
+ throw new DomainError(
83948
+ "NOT_FOUND",
83949
+ `o adaptador n\xE3o oferece o m\xE9todo de login "${options.methodId}"`
83950
+ );
83951
+ }
83952
+ await session2.connection.agent.request("authenticate", {
83953
+ methodId: options.methodId,
83954
+ /*
83955
+ * A chave viaja no `_meta` e não fica em lugar nenhum.
83956
+ *
83957
+ * Medido no pacote: `authenticateWithApiKey` lê
83958
+ * `_meta["api-key"].apiKey` e, na falta dele, o ambiente
83959
+ * (`CODEX_API_KEY`, `OPENAI_API_KEY`). O daemon é o carteiro — ele não
83960
+ * grava, não loga e não devolve.
83961
+ */
83962
+ ...options.apiKey === void 0 ? {} : { _meta: { "api-key": { apiKey: options.apiKey } } }
83963
+ });
83964
+ try {
83965
+ await this.withTimeout(
83966
+ session2.connection.agent.request("session/new", { cwd: options.cwd, mcpServers: [] }),
83967
+ "session/new"
83968
+ );
83969
+ } catch (error40) {
83970
+ if (isAuthRequired(error40)) {
83971
+ throw new DomainError(
83972
+ "BLOCKED",
83973
+ "o adaptador aceitou o login e continua pedindo credencial \u2014 a conta n\xE3o serve para esta sess\xE3o"
83974
+ );
83975
+ }
83976
+ throw error40;
83977
+ }
83978
+ } finally {
83979
+ options.signal?.removeEventListener("abort", abort);
83980
+ child.kill();
83981
+ }
83982
+ }
83764
83983
  async probe(options) {
83765
83984
  const startedAt = this.now();
83766
83985
  const { session: session2, child } = this.launch(options, { probe: true });
@@ -83854,10 +84073,22 @@ var AcpManager = class {
83854
84073
  exitCode: null,
83855
84074
  mode: "",
83856
84075
  model: "",
83857
- configOptions: []
84076
+ configOptions: [],
84077
+ /*
84078
+ * A session is born asking, always (F1.5).
84079
+ *
84080
+ * The workspace default is applied by the caller that knows which
84081
+ * workspace this is — the store — and even that one cannot make a
84082
+ * session start free: the gate for `free` is per session, and a default
84083
+ * that walked through it would annul it (Q5).
84084
+ */
84085
+ lumemMode: options.lumemMode ?? "ask",
84086
+ lumemModeDefault: options.lumemModeDefault ?? "ask"
83858
84087
  },
83859
84088
  process: child,
83860
84089
  connection: void 0,
84090
+ elicit: void 0,
84091
+ elicitDone: void 0,
83861
84092
  listeners: /* @__PURE__ */ new Set(),
83862
84093
  openToolCalls: /* @__PURE__ */ new Set(),
83863
84094
  pendingPermissions: /* @__PURE__ */ new Map(),
@@ -83980,7 +84211,50 @@ var AcpManager = class {
83980
84211
  }
83981
84212
  session2.pendingPermissions.delete(requestId);
83982
84213
  pending.resolve({ outcome: "selected", optionId });
83983
- this.emit(session2, { type: "permission_resolved", requestId, outcome: { optionId } });
84214
+ this.emit(session2, {
84215
+ type: "permission_resolved",
84216
+ requestId,
84217
+ outcome: { optionId },
84218
+ by: "user",
84219
+ reason: null
84220
+ });
84221
+ }
84222
+ /**
84223
+ * Switch the policy Lumem applies to permission requests (F1.1).
84224
+ *
84225
+ * Synchronous, and that is the whole difference from `setConfig`: nothing
84226
+ * leaves the daemon. `setConfig` forwards a value to the agent and waits for it
84227
+ * to answer; this changes what *we* answer, and the agent never learns a policy
84228
+ * exists.
84229
+ *
84230
+ * Two refusals, and both are named rather than silent:
84231
+ *
84232
+ * - **the agent owns the selector** (A1). Accepting the switch and doing
84233
+ * nothing with it would leave a stored value that never applies — the tell
84234
+ * would be a pill that changes and a behaviour that does not.
84235
+ * - **a turn is running** (F1.7). Same reason `setConfig` refuses, and
84236
+ * deliberately the same message: from where the user stands it is the same
84237
+ * act, and two wordings for one rule teach that there are two rules.
84238
+ */
84239
+ setLumemMode(id, mode) {
84240
+ const session2 = this.require(id);
84241
+ if (session2.info.state === "exited") {
84242
+ throw new DomainError("SESSION_EXITED", `session ${id} has exited`);
84243
+ }
84244
+ if (modeOwnerOf(session2.info) === "agent") {
84245
+ throw new DomainError(
84246
+ "BLOCKED",
84247
+ "este agente oferece modos pr\xF3prios: troque o modo dele, n\xE3o a pol\xEDtica do Lumem"
84248
+ );
84249
+ }
84250
+ if (session2.promptInFlight) {
84251
+ throw new DomainError(
84252
+ "BLOCKED",
84253
+ "n\xE3o d\xE1 para trocar de modo ou modelo no meio de um turno: interrompa ou espere ele acabar"
84254
+ );
84255
+ }
84256
+ session2.info.lumemMode = mode;
84257
+ this.emitConfig(session2);
83984
84258
  }
83985
84259
  /**
83986
84260
  * Everything an attaching client needs to catch up, in one frame.
@@ -84057,18 +84331,18 @@ var AcpManager = class {
84057
84331
  const running = [...this.sessions.values()].filter((s) => s.info.state === "running");
84058
84332
  await Promise.all(
84059
84333
  running.map(
84060
- (session2) => new Promise((resolve5) => {
84061
- const timer = setTimeout(resolve5, timeoutMs);
84334
+ (session2) => new Promise((resolve6) => {
84335
+ const timer = setTimeout(resolve6, timeoutMs);
84062
84336
  timer.unref?.();
84063
84337
  void session2.process.exited.then(() => {
84064
84338
  clearTimeout(timer);
84065
- resolve5();
84339
+ resolve6();
84066
84340
  });
84067
84341
  try {
84068
84342
  session2.process.kill();
84069
84343
  } catch {
84070
84344
  clearTimeout(timer);
84071
- resolve5();
84345
+ resolve6();
84072
84346
  }
84073
84347
  })
84074
84348
  )
@@ -84130,8 +84404,33 @@ var AcpManager = class {
84130
84404
  }).onRequest("terminal/release", ({ params }) => {
84131
84405
  this.requireTerminals(session2).release(params.terminalId);
84132
84406
  return {};
84407
+ }).onRequest("elicitation/create", ({ params }) => {
84408
+ const record2 = params;
84409
+ const url2 = record2["url"];
84410
+ if (session2.elicit === void 0 || params.mode !== "url" || typeof url2 !== "string") {
84411
+ return { action: "decline" };
84412
+ }
84413
+ session2.elicit({
84414
+ elicitationId: String(record2["elicitationId"] ?? ""),
84415
+ url: url2,
84416
+ message: params.message,
84417
+ code: codeIn(params.message)
84418
+ });
84419
+ return { action: "accept" };
84420
+ }).onNotification("elicitation/complete", ({ params }) => {
84421
+ session2.elicitDone?.(params.elicitationId);
84133
84422
  }).onRequest("session/request_permission", ({ params }) => {
84134
84423
  const requestId = newId();
84424
+ const options = params.options.map((option) => ({
84425
+ optionId: option.optionId,
84426
+ name: option.name,
84427
+ kind: option.kind
84428
+ }));
84429
+ const decision = modeOwnerOf(session2.info) === "lumem" ? decidePermission(session2.info.lumemMode, session2.info.cwd, {
84430
+ kind: kindOf(params.toolCall),
84431
+ locations: locationsOf(params.toolCall),
84432
+ options
84433
+ }) : { approve: false, reason: null };
84135
84434
  this.emit(session2, {
84136
84435
  type: "permission_request",
84137
84436
  requestId,
@@ -84139,15 +84438,24 @@ var AcpManager = class {
84139
84438
  title: params.toolCall.title ?? params.toolCall.toolCallId,
84140
84439
  command: commandOf(params.toolCall),
84141
84440
  cwd: session2.info.cwd,
84142
- options: params.options.map((option) => ({
84143
- optionId: option.optionId,
84144
- name: option.name,
84145
- kind: option.kind
84146
- }))
84441
+ options,
84442
+ policyReason: decision.approve ? null : decision.reason
84147
84443
  });
84148
- return new Promise((resolve5) => {
84444
+ if (decision.approve) {
84445
+ this.emit(session2, {
84446
+ type: "permission_resolved",
84447
+ requestId,
84448
+ outcome: { optionId: decision.optionId },
84449
+ by: "lumem",
84450
+ reason: decision.reason
84451
+ });
84452
+ return Promise.resolve({
84453
+ outcome: { outcome: "selected", optionId: decision.optionId }
84454
+ });
84455
+ }
84456
+ return new Promise((resolve6) => {
84149
84457
  session2.pendingPermissions.set(requestId, {
84150
- resolve: (outcome) => resolve5({ outcome })
84458
+ resolve: (outcome) => resolve6({ outcome })
84151
84459
  });
84152
84460
  });
84153
84461
  }).onNotification(
@@ -84262,7 +84570,20 @@ var AcpManager = class {
84262
84570
  * Gated on the `PtyManager` too: the methods it offers are commands, and
84263
84571
  * a client that cannot run one has no business being offered it.
84264
84572
  */
84265
- ...this.ptyManager ? { auth: { terminal: true } } : {}
84573
+ ...this.ptyManager ? { auth: { terminal: true } } : {},
84574
+ /*
84575
+ * "Eu sei mostrar uma URL", declarado porque os dois métodos existem.
84576
+ *
84577
+ * Medido na fase 0 da `second-agent` (§4.2): sem isto, o `codex-acp`
84578
+ * não oferece `chat-gpt-device-code` — o único método dele que **não**
84579
+ * abre um navegador na máquina do daemon. Declarar é o que faz o
84580
+ * método aparecer, e é por isso que ele não é opcional aqui.
84581
+ *
84582
+ * Não é gatilhado no `ptyManager` como o `auth.terminal`: mostrar uma
84583
+ * URL não precisa de terminal nenhum, e o que responde ao pedido é o
84584
+ * `elicitation/create` acima, que existe sempre.
84585
+ */
84586
+ elicitation: { url: {} }
84266
84587
  },
84267
84588
  ...this.ptyManager ? { _meta: { "terminal-auth": true } } : {},
84268
84589
  clientInfo: { name: "lumem", version: LUMEM_CLIENT_VERSION }
@@ -84362,6 +84683,9 @@ var AcpManager = class {
84362
84683
  modeId: value
84363
84684
  });
84364
84685
  session2.info.mode = value;
84686
+ session2.info.configOptions = session2.info.configOptions.map(
84687
+ (option) => option.id === MODE_OPTION ? { ...option, currentValue: value } : option
84688
+ );
84365
84689
  this.emitConfig(session2);
84366
84690
  return;
84367
84691
  }
@@ -84387,7 +84711,10 @@ var AcpManager = class {
84387
84711
  this.emit(session2, {
84388
84712
  type: "config",
84389
84713
  mode: session2.info.mode,
84390
- options: [...session2.info.configOptions]
84714
+ options: [...session2.info.configOptions],
84715
+ modeOwner: modeOwnerOf(session2.info),
84716
+ lumemMode: session2.info.lumemMode,
84717
+ lumemModeDefault: session2.info.lumemModeDefault
84391
84718
  });
84392
84719
  for (const watcher of [...this.configWatchers]) {
84393
84720
  try {
@@ -84551,6 +84878,18 @@ function flattenChoices(options) {
84551
84878
  ];
84552
84879
  });
84553
84880
  }
84881
+ function kindOf(toolCall) {
84882
+ const parsed = acpToolKindSchema.safeParse(toolCall.kind);
84883
+ return parsed.success ? parsed.data : "other";
84884
+ }
84885
+ function locationsOf(toolCall) {
84886
+ const locations = toolCall.locations;
84887
+ if (!Array.isArray(locations)) return [];
84888
+ return locations.flatMap((location) => {
84889
+ const path = location.path;
84890
+ return typeof path === "string" ? [{ path, line: null }] : [];
84891
+ });
84892
+ }
84554
84893
  function commandOf(toolCall) {
84555
84894
  const raw = toolCall.rawInput;
84556
84895
  if (typeof raw === "object" && raw !== null) {
@@ -84561,7 +84900,8 @@ function commandOf(toolCall) {
84561
84900
  }
84562
84901
  function notInstalled(command, adapterVersion) {
84563
84902
  const pinned = adapterVersion ?? "";
84564
- const remedy = pinned ? `npm i -g @agentclientprotocol/claude-agent-acp@${pinned}` : `instale o adaptador e deixe "${command}" no PATH`;
84903
+ const spec = adapterByCommand(basename3(command));
84904
+ const remedy = spec !== null && spec.package !== null ? `npm i -g ${spec.package}@${pinned || spec.pinnedVersion}` : `instale o adaptador e deixe "${command}" no PATH`;
84565
84905
  const version3 = pinned ? ` Esta sess\xE3o fixa a vers\xE3o ${pinned}.` : "";
84566
84906
  return new DomainError(
84567
84907
  "SPAWN_FAILED",
@@ -84577,6 +84917,10 @@ function launchFailure(command, adapterVersion, cause) {
84577
84917
  { cause }
84578
84918
  );
84579
84919
  }
84920
+ function codeIn(message) {
84921
+ const match = /\b([A-Z0-9]{4}-[A-Z0-9]{4}|[A-Z0-9]{6,12})\b/.exec(message);
84922
+ return match?.[1] ?? null;
84923
+ }
84580
84924
  function isAuthRequired(error40) {
84581
84925
  const code = error40.code;
84582
84926
  return code === ACP_AUTH_REQUIRED_CODE;
@@ -84711,7 +85055,7 @@ import { dirname as dirname6, join as join11 } from "node:path";
84711
85055
  // src/memory/catalog.ts
84712
85056
  import { createHash as createHash2 } from "node:crypto";
84713
85057
  import { readdir as readdir5, readFile as readFile3 } from "node:fs/promises";
84714
- import { join as join9, relative, sep as sep4 } from "node:path";
85058
+ import { join as join9, relative as relative2, sep as sep4 } from "node:path";
84715
85059
 
84716
85060
  // src/memory/entry.ts
84717
85061
  var import_yaml = __toESM(require_dist(), 1);
@@ -85010,7 +85354,7 @@ async function collectFrom(stateDir, dir, location, items) {
85010
85354
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
85011
85355
  if (dirent.name === "MEMORY.md") continue;
85012
85356
  const absolute = join9(dir, dirent.name);
85013
- const path = relative(stateDir, absolute).split(sep4).join("/");
85357
+ const path = relative2(stateDir, absolute).split(sep4).join("/");
85014
85358
  const text2 = await readFile3(absolute, "utf8").catch((error40) => error40);
85015
85359
  if (typeof text2 !== "string") {
85016
85360
  items.push({ path, reason: `n\xE3o foi poss\xEDvel ler: ${text2.message}` });
@@ -85201,7 +85545,7 @@ function listDecisions(db, { path, limit = 50 } = {}) {
85201
85545
  }
85202
85546
 
85203
85547
  // src/memory/paths.ts
85204
- import { join as join10, relative as relative2, sep as sep5 } from "node:path";
85548
+ import { join as join10, relative as relative3, sep as sep5 } from "node:path";
85205
85549
  var ID_SEGMENT = "[A-Za-z0-9_-]+";
85206
85550
  function memoryDirFor(stateDir, target) {
85207
85551
  switch (target.scope) {
@@ -85281,7 +85625,7 @@ function slugOf(path, type) {
85281
85625
  return file2.replace(new RegExp(`^${type}_`), "").replace(/\.md$/, "");
85282
85626
  }
85283
85627
  function repoRelative(stateDir, absolutePath) {
85284
- const rel = relative2(stateDir, absolutePath);
85628
+ const rel = relative3(stateDir, absolutePath);
85285
85629
  if (rel === "" || rel.startsWith("..")) {
85286
85630
  throw new DomainError("INVALID_ARGUMENT", `caminho fora do state dir: ${absolutePath}`);
85287
85631
  }
@@ -86395,8 +86739,72 @@ function parseWrite(requested) {
86395
86739
  throw new DomainError("INVALID_ARGUMENT", `${field} \u2014 ${issue2?.message ?? "inv\xE1lido"}`);
86396
86740
  }
86397
86741
 
86742
+ // src/repositories/agentConfig.ts
86743
+ function conflicts3(name) {
86744
+ return {
86745
+ "unique:agent_config.name": {
86746
+ code: "DUPLICATE",
86747
+ message: `j\xE1 existe uma configura\xE7\xE3o chamada "${name}"`
86748
+ },
86749
+ foreignKey: {
86750
+ code: "IN_USE",
86751
+ message: "a configura\xE7\xE3o ainda est\xE1 em uso por alguma sess\xE3o"
86752
+ },
86753
+ // Without this the CHECK surfaces as a raw SQLite error, which reads as a
86754
+ // daemon defect rather than as the one thing the caller got wrong.
86755
+ "check:agent_config_adapter_version": {
86756
+ code: "INVALID_ARGUMENT",
86757
+ message: "configura\xE7\xE3o ACP precisa de uma vers\xE3o de adaptador fixa, e configura\xE7\xE3o PTY n\xE3o pode ter uma"
86758
+ },
86759
+ "check:agent_config_transport": {
86760
+ code: "INVALID_ARGUMENT",
86761
+ message: "transporte precisa ser pty ou acp"
86762
+ }
86763
+ };
86764
+ }
86765
+ function createAgentConfigRepository(db) {
86766
+ async function require_(id) {
86767
+ const found = await db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
86768
+ if (!found) throw new DomainError("NOT_FOUND", `configura\xE7\xE3o ${id} n\xE3o existe`);
86769
+ return found;
86770
+ }
86771
+ return {
86772
+ async create({ name, command, args = [], env = {}, transport = "pty", adapterVersion = null }) {
86773
+ const [row] = await withConstraints(
86774
+ () => db.insert(agentConfig).values({ id: newId(), name, command, args, env, transport, adapterVersion }).returning(),
86775
+ conflicts3(name)
86776
+ );
86777
+ return row;
86778
+ },
86779
+ list() {
86780
+ return db.select().from(agentConfig).orderBy(asc(agentConfig.name));
86781
+ },
86782
+ findById(id) {
86783
+ return db.query.agentConfig.findFirst({ where: eq(agentConfig.id, id) });
86784
+ },
86785
+ findByName(name) {
86786
+ return db.query.agentConfig.findFirst({ where: eq(agentConfig.name, name) });
86787
+ },
86788
+ async update(id, input) {
86789
+ const current = await require_(id);
86790
+ const [row] = await withConstraints(
86791
+ () => db.update(agentConfig).set({ ...input, updatedAt: /* @__PURE__ */ new Date() }).where(eq(agentConfig.id, id)).returning(),
86792
+ conflicts3(input.name ?? current.name)
86793
+ );
86794
+ return row;
86795
+ },
86796
+ async remove(id) {
86797
+ await require_(id);
86798
+ await withConstraints(
86799
+ () => db.delete(agentConfig).where(eq(agentConfig.id, id)).returning(),
86800
+ conflicts3("")
86801
+ );
86802
+ }
86803
+ };
86804
+ }
86805
+
86398
86806
  // src/memory/projection.ts
86399
- import { relative as relative3, isAbsolute as isAbsolute4 } from "node:path";
86807
+ import { relative as relative4, isAbsolute as isAbsolute5 } from "node:path";
86400
86808
  var MAX_FILES = 12;
86401
86809
  var MAX_COMMANDS = 8;
86402
86810
  function isEmpty(projection) {
@@ -86444,8 +86852,8 @@ function projectSession(entries, { cwd }) {
86444
86852
  };
86445
86853
  }
86446
86854
  function toRelative(path, cwd) {
86447
- if (!isAbsolute4(path)) return path;
86448
- const rel = relative3(cwd, path);
86855
+ if (!isAbsolute5(path)) return path;
86856
+ const rel = relative4(cwd, path);
86449
86857
  if (rel === "" || rel.startsWith("..")) return null;
86450
86858
  return rel;
86451
86859
  }
@@ -86884,6 +87292,7 @@ function trackSessionUsage({ db, acpManager, log }) {
86884
87292
  sessionId,
86885
87293
  projectId: scope.projectId,
86886
87294
  worktreeId: scope.worktreeId,
87295
+ agentConfigId: scope.agentConfigId,
86887
87296
  tokens,
86888
87297
  ...cost === null ? {} : { cost: cost.amount, currency: cost.currency }
86889
87298
  }).run();
@@ -86899,10 +87308,95 @@ function trackSessionUsage({ db, acpManager, log }) {
86899
87308
  async function scopeOf(db, sessionId) {
86900
87309
  const row = await createSessionRepository(db).findById(sessionId);
86901
87310
  if (row === void 0) return null;
86902
- if (row.scopeType === "project") return { projectId: row.scopeId, worktreeId: "" };
87311
+ const agentConfigId = row.agentConfigId ?? null;
87312
+ if (row.scopeType === "project") {
87313
+ return { projectId: row.scopeId, worktreeId: "", agentConfigId };
87314
+ }
86903
87315
  const worktree2 = await createWorktreeRepository(db).findById(row.scopeId);
86904
87316
  if (worktree2 === void 0) return null;
86905
- return { projectId: worktree2.projectId, worktreeId: worktree2.id };
87317
+ return { projectId: worktree2.projectId, worktreeId: worktree2.id, agentConfigId };
87318
+ }
87319
+
87320
+ // src/setup/agent-auth.ts
87321
+ function redact(message, secret) {
87322
+ if (secret === void 0 || secret === "") return message;
87323
+ return message.split(secret).join("\u2022\u2022\u2022");
87324
+ }
87325
+ function createAgentAuthService({
87326
+ acpManager
87327
+ }) {
87328
+ const attempts = /* @__PURE__ */ new Map();
87329
+ function require_(id) {
87330
+ const attempt = attempts.get(id);
87331
+ if (attempt === void 0) {
87332
+ throw new DomainError("NOT_FOUND", `n\xE3o existe login em andamento com o id ${id}`);
87333
+ }
87334
+ return attempt;
87335
+ }
87336
+ function view({ id, state, elicitation, message }) {
87337
+ return { id, state, elicitation, message };
87338
+ }
87339
+ return {
87340
+ start(options) {
87341
+ const attempt = {
87342
+ id: newId(),
87343
+ state: "running",
87344
+ elicitation: null,
87345
+ message: null,
87346
+ controller: new AbortController()
87347
+ };
87348
+ attempts.set(attempt.id, attempt);
87349
+ void acpManager.authenticate({
87350
+ command: options.command,
87351
+ ...options.args ? { args: options.args } : {},
87352
+ cwd: options.cwd,
87353
+ methodId: options.methodId,
87354
+ ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
87355
+ ...options.adapterVersion === void 0 ? {} : { adapterVersion: options.adapterVersion },
87356
+ onElicitation: (elicitation) => {
87357
+ attempt.elicitation = elicitation;
87358
+ },
87359
+ onElicitationDone: (elicitationId) => {
87360
+ if (attempt.elicitation?.elicitationId === elicitationId) {
87361
+ attempt.elicitation = null;
87362
+ }
87363
+ },
87364
+ signal: attempt.controller.signal
87365
+ }).then(() => {
87366
+ if (attempt.state === "running") attempt.state = "ok";
87367
+ attempt.elicitation = null;
87368
+ }).catch((error40) => {
87369
+ if (attempt.state === "cancelled") return;
87370
+ attempt.state = "failed";
87371
+ attempt.elicitation = null;
87372
+ attempt.message = redact(
87373
+ error40 instanceof Error ? error40.message : "o adaptador recusou o login",
87374
+ options.apiKey
87375
+ );
87376
+ });
87377
+ return view(attempt);
87378
+ },
87379
+ status(id) {
87380
+ return view(require_(id));
87381
+ },
87382
+ cancel(id) {
87383
+ const attempt = require_(id);
87384
+ if (attempt.state === "running") {
87385
+ attempt.state = "cancelled";
87386
+ attempt.elicitation = null;
87387
+ attempt.controller.abort();
87388
+ }
87389
+ return view(attempt);
87390
+ },
87391
+ cancelAll() {
87392
+ for (const attempt of attempts.values()) {
87393
+ if (attempt.state === "running") {
87394
+ attempt.state = "cancelled";
87395
+ attempt.controller.abort();
87396
+ }
87397
+ }
87398
+ }
87399
+ };
86906
87400
  }
86907
87401
 
86908
87402
  // src/memory/skill.ts
@@ -87212,18 +87706,18 @@ var PtyManager = class {
87212
87706
  const running = [...this.sessions.values()].filter((s) => s.info.state === "running");
87213
87707
  await Promise.all(
87214
87708
  running.map(
87215
- (session2) => new Promise((resolve5) => {
87216
- const timer = setTimeout(resolve5, timeoutMs);
87709
+ (session2) => new Promise((resolve6) => {
87710
+ const timer = setTimeout(resolve6, timeoutMs);
87217
87711
  timer.unref?.();
87218
87712
  session2.exitListeners.add(() => {
87219
87713
  clearTimeout(timer);
87220
- resolve5();
87714
+ resolve6();
87221
87715
  });
87222
87716
  try {
87223
87717
  session2.pty.kill();
87224
87718
  } catch {
87225
87719
  clearTimeout(timer);
87226
- resolve5();
87720
+ resolve6();
87227
87721
  }
87228
87722
  })
87229
87723
  )
@@ -87333,10 +87827,10 @@ async function releasePort(db, scope) {
87333
87827
  );
87334
87828
  }
87335
87829
  function isPortFree(port) {
87336
- return new Promise((resolve5) => {
87830
+ return new Promise((resolve6) => {
87337
87831
  const probe = createServer();
87338
- probe.once("error", () => resolve5(false));
87339
- probe.once("listening", () => probe.close(() => resolve5(true)));
87832
+ probe.once("error", () => resolve6(false));
87833
+ probe.once("listening", () => probe.close(() => resolve6(true)));
87340
87834
  probe.listen(port, "0.0.0.0");
87341
87835
  });
87342
87836
  }
@@ -87999,7 +88493,7 @@ function parse3(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
87999
88493
  }
88000
88494
 
88001
88495
  // src/memory/project-identity.ts
88002
- import { dirname as dirname7, join as join13, resolve as resolve3 } from "node:path";
88496
+ import { dirname as dirname7, join as join13, resolve as resolve4 } from "node:path";
88003
88497
  var PROJECT_FILE = join13(".lumem", "project.toml");
88004
88498
 
88005
88499
  // src/scripts/project-scripts.ts
@@ -88131,7 +88625,7 @@ function createScriptRunner({
88131
88625
  log
88132
88626
  }) {
88133
88627
  const discovered = /* @__PURE__ */ new Map();
88134
- async function checkoutOf(scope) {
88628
+ async function checkoutOf2(scope) {
88135
88629
  const projects = createProjectRepository(db);
88136
88630
  if (scope.scopeType === "project") {
88137
88631
  const project3 = await projects.findById(scope.scopeId);
@@ -88184,7 +88678,7 @@ function createScriptRunner({
88184
88678
  }
88185
88679
  return {
88186
88680
  async status(scope) {
88187
- const { project: project2, cwd } = await checkoutOf(scope);
88681
+ const { project: project2, cwd } = await checkoutOf2(scope);
88188
88682
  const scripts = await readProjectScripts(cwd, {
88189
88683
  warn: (message) => log?.warn({ scope, message }, "scripts ignorados")
88190
88684
  });
@@ -88208,7 +88702,7 @@ function createScriptRunner({
88208
88702
  };
88209
88703
  },
88210
88704
  async start(scope, phase) {
88211
- const { project: project2, worktree: worktree2, cwd } = await checkoutOf(scope);
88705
+ const { project: project2, worktree: worktree2, cwd } = await checkoutOf2(scope);
88212
88706
  const scripts = await readProjectScripts(cwd);
88213
88707
  const command = scripts[phase];
88214
88708
  if (command === null) {
@@ -88274,15 +88768,15 @@ function createScriptRunner({
88274
88768
  },
88275
88769
  async runToCompletion(scope, phase, { timeoutMs = TEARDOWN_TIMEOUT_MS } = {}) {
88276
88770
  const { session: row } = await this.start(scope, phase);
88277
- return new Promise((resolve5) => {
88771
+ return new Promise((resolve6) => {
88278
88772
  const timer = setTimeout(() => {
88279
88773
  log?.warn({ scope, phase }, "script n\xE3o terminou no tempo \u2014 seguindo sem ele");
88280
88774
  ptyManager.kill(row.id);
88281
- resolve5(null);
88775
+ resolve6(null);
88282
88776
  }, timeoutMs);
88283
88777
  ptyManager.onExit(row.id, ({ exitCode }) => {
88284
88778
  clearTimeout(timer);
88285
- resolve5(exitCode);
88779
+ resolve6(exitCode);
88286
88780
  });
88287
88781
  });
88288
88782
  },
@@ -88450,6 +88944,19 @@ function createSessionStore({
88450
88944
  log?.warn({ session: row.id, err: error40 }, "falha ao varrer reverts do checkout");
88451
88945
  }
88452
88946
  }
88947
+ async function inheritedMode(scopeType, scopeId) {
88948
+ try {
88949
+ const projects = createProjectRepository(db);
88950
+ const projectId = scopeType === "project" ? scopeId : (await createWorktreeRepository(db).findById(scopeId))?.projectId;
88951
+ if (!projectId) return "ask";
88952
+ const project2 = await projects.findById(projectId);
88953
+ if (!project2) return "ask";
88954
+ const workspace2 = await createWorkspaceRepository(db).findById(project2.workspaceId);
88955
+ return workspace2?.defaultLumemMode === "auto" ? "auto" : "ask";
88956
+ } catch {
88957
+ return "ask";
88958
+ }
88959
+ }
88453
88960
  return {
88454
88961
  async start(input) {
88455
88962
  const { kind, agentConfigId = null, scopeType, scopeId, cwd, command } = input;
@@ -88462,12 +88969,17 @@ function createSessionStore({
88462
88969
  "esta inst\xE2ncia n\xE3o sabe iniciar sess\xE3o ACP: nenhum AcpManager foi ligado"
88463
88970
  );
88464
88971
  }
88972
+ const inherited = await inheritedMode(scopeType, scopeId);
88465
88973
  const agent = await acpManager.spawn({
88466
88974
  command,
88467
88975
  ...input.args ? { args: input.args } : {},
88468
88976
  cwd,
88469
88977
  ...input.env ? { env: input.env } : {},
88470
- ...input.adapterVersion ? { adapterVersion: input.adapterVersion } : {}
88978
+ ...input.adapterVersion ? { adapterVersion: input.adapterVersion } : {},
88979
+ // O modo herdado, e ele é o mesmo nos dois campos: uma sessão nova
88980
+ // começa no padrão do workspace, e o menu mostra de onde veio.
88981
+ lumemMode: inherited,
88982
+ lumemModeDefault: inherited
88471
88983
  });
88472
88984
  try {
88473
88985
  return await sessions.create({
@@ -88545,6 +89057,16 @@ function createSessionStore({
88545
89057
  ...config2?.env && Object.keys(config2.env).length > 0 ? { env: config2.env } : {},
88546
89058
  ...config2?.adapterVersion ? { adapterVersion: config2.adapterVersion } : {},
88547
89059
  acpSessionId: row.acpSessionId,
89060
+ /*
89061
+ * A política volta como estava (F1.4).
89062
+ *
89063
+ * `lumemMode` da linha morta, e não o padrão do workspace: retomar é
89064
+ * continuar a mesma conversa, e uma que estava em `auto` voltar
89065
+ * perguntando tudo seria a feature desfazendo a escolha em silêncio.
89066
+ * Inclusive `free` — o portão já foi atravessado por esta conversa.
89067
+ */
89068
+ lumemMode: row.lumemMode,
89069
+ lumemModeDefault: await inheritedMode(row.scopeType, row.scopeId),
88548
89070
  // What makes the new session's transcript self-contained (D15): the old
88549
89071
  // conversation is copied in front of it, and the separator recorded after.
88550
89072
  fromSessionId: row.id
@@ -88597,6 +89119,19 @@ function createSessionStore({
88597
89119
  * interface offering something it cannot do.
88598
89120
  */
88599
89121
  configOptions: [],
89122
+ /*
89123
+ * O modo em que a conversa **esteve** (F1.8).
89124
+ *
89125
+ * A autoria sai de `row.mode`, e não de `configOptions`: aqui a lista é
89126
+ * vazia por construção, e perguntar a ela quem era o dono responderia
89127
+ * "o Lumem" para toda conversa morta — inclusive as que rodaram com um
89128
+ * agente cheio de modos. O que a linha guarda é qual modo estava valendo,
89129
+ * e ter um é a prova de que o agente era o dono.
89130
+ */
89131
+ modeOwner: row.mode ? "agent" : "lumem",
89132
+ lumemMode: row.lumemMode,
89133
+ lumemModeDefault: "ask",
89134
+ cwd: row.cwd,
88600
89135
  transcript: [...acpManager.storedTranscript(row.id)]
88601
89136
  };
88602
89137
  },
@@ -88636,7 +89171,11 @@ function createSessionStore({
88636
89171
  const offPty = ptyManager.watchExits((info) => record2(info.id, info.exitCode));
88637
89172
  const offAcp = acpManager?.watchExits((info) => record2(info.id, info.exitCode));
88638
89173
  const offConfig = acpManager?.watchConfig((info) => {
88639
- void sessions.setConfig(info.id, { mode: info.mode, model: info.model }).catch((error40) => {
89174
+ void sessions.setConfig(info.id, {
89175
+ mode: info.mode,
89176
+ model: info.model,
89177
+ lumemMode: info.lumemMode
89178
+ }).catch((error40) => {
88640
89179
  log?.warn({ session: info.id, err: error40 }, "falha ao registrar troca de configura\xE7\xE3o");
88641
89180
  });
88642
89181
  });
@@ -89593,8 +90132,8 @@ var Unpromise = class Unpromise2 {
89593
90132
  status: "fulfilled",
89594
90133
  value
89595
90134
  };
89596
- subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve5 }) => {
89597
- resolve5(value);
90135
+ subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve6 }) => {
90136
+ resolve6(value);
89598
90137
  });
89599
90138
  });
89600
90139
  if ("catch" in thenReturn) thenReturn.catch((reason) => {
@@ -89735,15 +90274,15 @@ function resolveSelfTuple(promise2) {
89735
90274
  return Unpromise.proxy(promise2).then(() => [promise2]);
89736
90275
  }
89737
90276
  function withResolvers() {
89738
- let resolve5;
90277
+ let resolve6;
89739
90278
  let reject;
89740
90279
  const promise2 = new Promise((_resolve, _reject) => {
89741
- resolve5 = _resolve;
90280
+ resolve6 = _resolve;
89742
90281
  reject = _reject;
89743
90282
  });
89744
90283
  return {
89745
90284
  promise: promise2,
89746
- resolve: resolve5,
90285
+ resolve: resolve6,
89747
90286
  reject
89748
90287
  };
89749
90288
  }
@@ -89787,8 +90326,8 @@ function timerResource(ms) {
89787
90326
  let timer = null;
89788
90327
  return makeResource({ start() {
89789
90328
  if (timer) throw new Error("Timer already started");
89790
- const promise2 = new Promise((resolve5) => {
89791
- timer = setTimeout(() => resolve5(disposablePromiseTimerResult), ms);
90329
+ const promise2 = new Promise((resolve6) => {
90330
+ timer = setTimeout(() => resolve6(disposablePromiseTimerResult), ms);
89792
90331
  });
89793
90332
  return promise2;
89794
90333
  } }, () => {
@@ -89975,15 +90514,15 @@ function _takeWithGrace() {
89975
90514
  return _takeWithGrace.apply(this, arguments);
89976
90515
  }
89977
90516
  function createDeferred() {
89978
- let resolve5;
90517
+ let resolve6;
89979
90518
  let reject;
89980
90519
  const promise2 = new Promise((res, rej) => {
89981
- resolve5 = res;
90520
+ resolve6 = res;
89982
90521
  reject = rej;
89983
90522
  });
89984
90523
  return {
89985
90524
  promise: promise2,
89986
- resolve: resolve5,
90525
+ resolve: resolve6,
89987
90526
  reject
89988
90527
  };
89989
90528
  }
@@ -91558,8 +92097,8 @@ function getWSConnectionHandler(opts) {
91558
92097
  try {
91559
92098
  var _usingCtx = (0, import_usingCtx2.default)();
91560
92099
  const iterator = _usingCtx.a(iteratorResource(iterable));
91561
- const abortPromise = new Promise((resolve5) => {
91562
- abortController$1.signal.onabort = () => resolve5("abort");
92100
+ const abortPromise = new Promise((resolve6) => {
92101
+ abortController$1.signal.onabort = () => resolve6("abort");
91563
92102
  });
91564
92103
  let next;
91565
92104
  let result$1;
@@ -92039,8 +92578,8 @@ async function* snapshots(id, current, listeners, signal) {
92039
92578
  yield job;
92040
92579
  if (isTerminal(job.state)) return;
92041
92580
  }
92042
- await new Promise((resolve5) => {
92043
- wake = resolve5;
92581
+ await new Promise((resolve6) => {
92582
+ wake = resolve6;
92044
92583
  });
92045
92584
  wake = void 0;
92046
92585
  }
@@ -92051,6 +92590,652 @@ async function* snapshots(id, current, listeners, signal) {
92051
92590
  }
92052
92591
  }
92053
92592
 
92593
+ // src/pr/GhHost.ts
92594
+ import { mkdtemp, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
92595
+ import { tmpdir } from "node:os";
92596
+ import { join as join15 } from "node:path";
92597
+
92598
+ // src/git/git-url.ts
92599
+ var ALLOWED = /* @__PURE__ */ new Set(["https", "http", "ssh", "file"]);
92600
+ var HELPER = /^([A-Za-z][A-Za-z0-9+.-]*)::/;
92601
+ var SCHEME = /^([A-Za-z][A-Za-z0-9+.-]*):\/\//;
92602
+ var CONTROL = /[\u0000-\u001f\u007f]/;
92603
+ function refuse(rule, message) {
92604
+ return { ok: false, rule, message };
92605
+ }
92606
+ function refuseScheme(scheme) {
92607
+ if (scheme === "git") {
92608
+ return refuse(
92609
+ "scheme",
92610
+ "o transporte git:// n\xE3o \xE9 aceito: ele n\xE3o autentica nem verifica integridade \u2014 use a URL https ou ssh do mesmo reposit\xF3rio"
92611
+ );
92612
+ }
92613
+ return refuse(
92614
+ "scheme",
92615
+ `o transporte "${scheme}" n\xE3o est\xE1 na lista de transportes aceitos (https, http, ssh, file)`
92616
+ );
92617
+ }
92618
+ function parseGitUrl(input) {
92619
+ const raw = input.trim();
92620
+ if (raw === "") return refuse("empty", "informe a URL do reposit\xF3rio");
92621
+ if (CONTROL.test(raw)) {
92622
+ return refuse("control-chars", "a URL tem caracteres de controle");
92623
+ }
92624
+ if (raw.startsWith("-")) {
92625
+ return refuse("leading-dash", "a URL n\xE3o pode come\xE7ar com '-'");
92626
+ }
92627
+ const helper = HELPER.exec(raw);
92628
+ if (helper) return refuseScheme(helper[1].toLowerCase());
92629
+ const scheme = SCHEME.exec(raw);
92630
+ if (scheme) return parseWithScheme(raw, scheme[1].toLowerCase());
92631
+ if (isScp(raw)) return parseScp(raw);
92632
+ return refuse(
92633
+ "scheme",
92634
+ "n\xE3o reconheci isto como URL git: use https://, ssh://, file:// ou a forma git@host:org/repo.git"
92635
+ );
92636
+ }
92637
+ function scpColon(raw) {
92638
+ const at = raw.indexOf("@");
92639
+ return raw.indexOf(":", at === -1 ? 0 : at + 1);
92640
+ }
92641
+ function isScp(raw) {
92642
+ const colon = scpColon(raw);
92643
+ if (colon <= 0) return false;
92644
+ const slash = raw.indexOf("/");
92645
+ return slash === -1 || colon < slash;
92646
+ }
92647
+ function parseWithScheme(raw, scheme) {
92648
+ if (!ALLOWED.has(scheme)) return refuseScheme(scheme);
92649
+ let parsed;
92650
+ try {
92651
+ parsed = new URL(raw);
92652
+ } catch {
92653
+ return refuse("host", `n\xE3o consegui ler ${raw} como URL`);
92654
+ }
92655
+ if (scheme === "file") return parseFile(raw, parsed);
92656
+ if (parsed.hostname === "" || raw.slice(scheme.length + "://".length).startsWith("/")) {
92657
+ return refuse("host", "a URL n\xE3o tem host");
92658
+ }
92659
+ const path = parsed.pathname.replace(/^\/+/, "");
92660
+ if (path === "") {
92661
+ return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
92662
+ }
92663
+ const sanitized = new URL(raw);
92664
+ sanitized.username = "";
92665
+ sanitized.password = "";
92666
+ return {
92667
+ ok: true,
92668
+ raw,
92669
+ url: {
92670
+ scheme,
92671
+ href: sanitized.href,
92672
+ host: parsed.host,
92673
+ path,
92674
+ insecure: scheme === "http",
92675
+ scp: false
92676
+ }
92677
+ };
92678
+ }
92679
+ function parseFile(raw, parsed) {
92680
+ if (parsed.hostname !== "" && parsed.hostname !== "localhost") {
92681
+ return refuse("host", "file:// com host n\xE3o \xE9 aceito; use file:///caminho/absoluto");
92682
+ }
92683
+ const path = decodeURIComponent(parsed.pathname);
92684
+ if (!path.startsWith("/")) {
92685
+ return refuse("path", "o caminho de um file:// precisa ser absoluto");
92686
+ }
92687
+ return {
92688
+ ok: true,
92689
+ raw,
92690
+ url: { scheme: "file", href: `file://${parsed.pathname}`, host: "", path, insecure: false, scp: false }
92691
+ };
92692
+ }
92693
+ function parseScp(raw) {
92694
+ const colon = scpColon(raw);
92695
+ const authority = raw.slice(0, colon);
92696
+ const path = raw.slice(colon + 1);
92697
+ const at = authority.lastIndexOf("@");
92698
+ const user = at === -1 ? "" : authority.slice(0, at);
92699
+ const host = authority.slice(at + 1);
92700
+ if (host === "") return refuse("host", "a URL n\xE3o tem host");
92701
+ if (path === "") return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
92702
+ const cleanUser = user.includes(":") ? user.slice(0, user.indexOf(":")) : user;
92703
+ const prefix = cleanUser === "" ? "" : `${cleanUser}@`;
92704
+ return {
92705
+ ok: true,
92706
+ raw,
92707
+ url: {
92708
+ scheme: "ssh",
92709
+ href: `${prefix}${host}:${path}`,
92710
+ host,
92711
+ path: path.replace(/^\/+/, ""),
92712
+ insecure: false,
92713
+ scp: true
92714
+ }
92715
+ };
92716
+ }
92717
+ function repoNameOf(url2) {
92718
+ const segments = url2.path.split("/").filter((segment) => segment !== "");
92719
+ const last = segments.at(-1) ?? "";
92720
+ return last.endsWith(".git") ? last.slice(0, -".git".length) : last;
92721
+ }
92722
+
92723
+ // src/pr/exec.ts
92724
+ import { execFile as execFile2 } from "node:child_process";
92725
+ var DEFAULT_GH_TIMEOUT_MS = 2e4;
92726
+ var GH_MAX_BUFFER = 8 * 1024 * 1024;
92727
+ var execGh = (args, { cwd, timeoutMs = DEFAULT_GH_TIMEOUT_MS, bin = "gh" }) => new Promise((resolve6) => {
92728
+ execFile2(
92729
+ bin,
92730
+ [...args],
92731
+ {
92732
+ cwd,
92733
+ timeout: timeoutMs,
92734
+ maxBuffer: GH_MAX_BUFFER,
92735
+ env: {
92736
+ ...process.env,
92737
+ // Cor no meio de um JSON é o tipo de coisa que só quebra na máquina de
92738
+ // outra pessoa, que tem `GH_FORCE_TTY` no perfil.
92739
+ NO_COLOR: "1",
92740
+ GH_NO_UPDATE_NOTIFIER: "1",
92741
+ // O `gh` chama o `git`, e o `git` pode pedir credencial. Sem isto, um
92742
+ // remote mal configurado trava o daemon até o timeout.
92743
+ GIT_TERMINAL_PROMPT: "0"
92744
+ }
92745
+ },
92746
+ (error40, stdout, stderr) => {
92747
+ const failure = error40;
92748
+ resolve6({
92749
+ stdout,
92750
+ stderr,
92751
+ code: failure === null ? 0 : typeof failure.code === "number" ? failure.code : null,
92752
+ spawnError: typeof failure?.code === "string" ? failure.code : null,
92753
+ timedOut: failure?.killed === true
92754
+ });
92755
+ }
92756
+ );
92757
+ });
92758
+ var EXIT_AUTH_REQUIRED = 4;
92759
+ var OFFLINE = [
92760
+ "dial tcp",
92761
+ "no such host",
92762
+ "connection refused",
92763
+ "network is unreachable",
92764
+ "i/o timeout",
92765
+ "tls: ",
92766
+ "proxyconnect",
92767
+ "eof"
92768
+ ];
92769
+ function classify(result) {
92770
+ if (result.spawnError === "ENOENT") {
92771
+ return {
92772
+ kind: "no-binary",
92773
+ message: "o gh n\xE3o est\xE1 instalado ou n\xE3o est\xE1 no PATH"
92774
+ };
92775
+ }
92776
+ if (result.timedOut) {
92777
+ return { kind: "timeout", message: "o gh n\xE3o respondeu a tempo" };
92778
+ }
92779
+ if (result.spawnError !== null) {
92780
+ return { kind: "failed", message: "n\xE3o deu para executar o gh" };
92781
+ }
92782
+ if (result.code === 0) return null;
92783
+ const stderr = result.stderr.toLowerCase();
92784
+ if (result.code === EXIT_AUTH_REQUIRED || stderr.includes("gh auth login")) {
92785
+ if (stderr.includes("known github host")) {
92786
+ return {
92787
+ kind: "unsupported-host",
92788
+ message: "nenhum remote deste reposit\xF3rio aponta para um host GitHub conhecido"
92789
+ };
92790
+ }
92791
+ return { kind: "no-auth", message: "o gh n\xE3o est\xE1 autenticado" };
92792
+ }
92793
+ if (stderr.includes("rate limit")) {
92794
+ return {
92795
+ kind: "rate-limit",
92796
+ message: "o limite de consultas do GitHub foi atingido",
92797
+ retryAt: parseRetryAt(result.stderr)
92798
+ };
92799
+ }
92800
+ if (stderr.includes("not a git repository")) {
92801
+ return { kind: "not-a-repo", message: "este diret\xF3rio n\xE3o \xE9 um reposit\xF3rio git" };
92802
+ }
92803
+ if (OFFLINE.some((needle) => stderr.includes(needle))) {
92804
+ return { kind: "offline", message: "n\xE3o deu para falar com o GitHub" };
92805
+ }
92806
+ return { kind: "failed", message: "o gh recusou a consulta" };
92807
+ }
92808
+ function parseRetryAt(stderr) {
92809
+ const iso = /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)/.exec(stderr);
92810
+ if (iso) return iso[1];
92811
+ const seconds = /retry after (\d+) seconds?/i.exec(stderr);
92812
+ if (seconds) return new Date(Date.now() + Number(seconds[1]) * 1e3).toISOString();
92813
+ return null;
92814
+ }
92815
+
92816
+ // src/pr/url.ts
92817
+ function safeUrl(candidate, host) {
92818
+ if (candidate === null || candidate === void 0 || candidate.trim() === "") return null;
92819
+ if (host === null || host === "") return null;
92820
+ let parsed;
92821
+ try {
92822
+ parsed = new URL(candidate.trim());
92823
+ } catch {
92824
+ return null;
92825
+ }
92826
+ if (parsed.protocol !== "https:") return null;
92827
+ if (parsed.username !== "" || parsed.password !== "") return null;
92828
+ if (parsed.hostname.toLowerCase() !== host.toLowerCase()) return null;
92829
+ return parsed.href;
92830
+ }
92831
+ var NAME_WITH_OWNER = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/;
92832
+ function compareUrl(input) {
92833
+ const { host, repo, base, head } = input;
92834
+ if (host === null || host === "" || base === "" || head === "") return null;
92835
+ if (!NAME_WITH_OWNER.test(repo)) return null;
92836
+ if (base === head) return null;
92837
+ const url2 = `https://${host}/${repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}?expand=1`;
92838
+ return safeUrl(url2, host);
92839
+ }
92840
+
92841
+ // src/pr/GhHost.ts
92842
+ var DEFAULT_LIMIT = 50;
92843
+ var FIELDS = [
92844
+ "number",
92845
+ "url",
92846
+ "title",
92847
+ "state",
92848
+ "isDraft",
92849
+ "mergeable",
92850
+ "mergeStateStatus",
92851
+ "reviewDecision",
92852
+ "latestReviews",
92853
+ "headRefName",
92854
+ "baseRefName",
92855
+ "updatedAt",
92856
+ "mergedAt",
92857
+ "closedAt",
92858
+ "author",
92859
+ "statusCheckRollup"
92860
+ ].join(",");
92861
+ var PROJECTION = [
92862
+ "map({number, url, title, state, isDraft, mergeable, mergeStateStatus, reviewDecision,",
92863
+ "headRefName, baseRefName, updatedAt, mergedAt, closedAt,",
92864
+ 'author: (.author.login // ""),',
92865
+ 'reviews: [.latestReviews[]? | {author: (.author.login // ""), state: .state}],',
92866
+ "checks: [.statusCheckRollup[]? | {",
92867
+ 'name: (.name // .context // ""), app: (.workflowName // .context // ""),',
92868
+ 'status: (.status // "COMPLETED"), conclusion: (.conclusion // .state // ""),',
92869
+ 'url: (.detailsUrl // .targetUrl // ""),',
92870
+ "startedAt: (.startedAt // null), completedAt: (.completedAt // null)}]})"
92871
+ ].join(" ");
92872
+ var REPO_FIELDS = [
92873
+ "nameWithOwner",
92874
+ "mergeCommitAllowed",
92875
+ "squashMergeAllowed",
92876
+ "rebaseMergeAllowed",
92877
+ "deleteBranchOnMerge"
92878
+ ].join(",");
92879
+ var CONTROL2 = /[\u0000-\u001f\u007f]/;
92880
+ function createGhHost({ exec = execGh, limit = DEFAULT_LIMIT } = {}) {
92881
+ function hostOf(remoteUrl) {
92882
+ if (remoteUrl === null || remoteUrl.trim() === "") return null;
92883
+ const parsed = parseGitUrl(remoteUrl);
92884
+ if (!parsed.ok) return null;
92885
+ const host = parsed.url.host.split(":")[0] ?? "";
92886
+ return host === "" ? null : host.toLowerCase();
92887
+ }
92888
+ function isGitHub(host) {
92889
+ if (host === null) return false;
92890
+ return host === "github.com" || host.endsWith(".github.com") || host.startsWith("github.");
92891
+ }
92892
+ async function run3(args, cwd) {
92893
+ const result = await exec(args, { cwd });
92894
+ const failure = classify(result);
92895
+ return failure === null ? { ok: true, stdout: result.stdout } : { ok: false, failure };
92896
+ }
92897
+ function unsupported(host) {
92898
+ return {
92899
+ kind: "unsupported-host",
92900
+ message: host === null ? "este reposit\xF3rio n\xE3o tem um remote conhecido" : `o Lumem ainda n\xE3o fala com ${host}`
92901
+ };
92902
+ }
92903
+ function flag(name, value) {
92904
+ return `--${name}=${value}`;
92905
+ }
92906
+ function refuseControl(value, what) {
92907
+ return CONTROL2.test(value) ? { kind: "failed", message: `o ${what} tem caracteres de controle` } : null;
92908
+ }
92909
+ return {
92910
+ name: "GitHub",
92911
+ supports(remoteUrl) {
92912
+ return isGitHub(hostOf(remoteUrl));
92913
+ },
92914
+ async read({ repoPath, remoteUrl }) {
92915
+ const host = hostOf(remoteUrl);
92916
+ if (!isGitHub(host)) return { ok: false, failure: unsupported(host) };
92917
+ const [pulls, repo] = await Promise.all([
92918
+ run3(
92919
+ [
92920
+ "pr",
92921
+ "list",
92922
+ "--state",
92923
+ "all",
92924
+ "--limit",
92925
+ String(limit),
92926
+ "--json",
92927
+ FIELDS,
92928
+ "--jq",
92929
+ PROJECTION
92930
+ ],
92931
+ repoPath
92932
+ ),
92933
+ run3(["repo", "view", "--json", REPO_FIELDS], repoPath)
92934
+ ]);
92935
+ if (!pulls.ok) return pulls;
92936
+ if (!repo.ok) return repo;
92937
+ const parsed = parseJson(pulls.stdout, []);
92938
+ const settings = parseJson(repo.stdout, {});
92939
+ if (parsed === null || settings === null) {
92940
+ return {
92941
+ ok: false,
92942
+ failure: { kind: "failed", message: "o gh respondeu algo que n\xE3o \xE9 JSON" }
92943
+ };
92944
+ }
92945
+ return {
92946
+ ok: true,
92947
+ snapshot: {
92948
+ host: host ?? "",
92949
+ repo: typeof settings.nameWithOwner === "string" ? settings.nameWithOwner : "",
92950
+ pulls: parsed,
92951
+ merge: mergeOptionsOf(settings),
92952
+ readAt: (/* @__PURE__ */ new Date()).toISOString()
92953
+ }
92954
+ };
92955
+ },
92956
+ async create(input) {
92957
+ const host = hostOf(input.remoteUrl);
92958
+ if (!isGitHub(host)) return { ok: false, failure: unsupported(host) };
92959
+ const bad = refuseControl(input.title, "t\xEDtulo") ?? refuseControl(input.base, "nome da base") ?? refuseControl(input.head, "nome da branch");
92960
+ if (bad !== null) return { ok: false, failure: bad };
92961
+ if (input.title.trim() === "") {
92962
+ return { ok: false, failure: { kind: "failed", message: "a PR precisa de um t\xEDtulo" } };
92963
+ }
92964
+ const dir = await mkdtemp(join15(tmpdir(), "lumem-pr-"));
92965
+ const bodyFile = join15(dir, "body.md");
92966
+ try {
92967
+ await writeFile4(bodyFile, input.body, "utf8");
92968
+ const args = [
92969
+ "pr",
92970
+ "create",
92971
+ flag("base", input.base),
92972
+ flag("head", input.head),
92973
+ flag("title", input.title),
92974
+ flag("body-file", bodyFile)
92975
+ ];
92976
+ if (input.draft) args.push("--draft");
92977
+ const result = await run3(args, input.repoPath);
92978
+ if (!result.ok) return { ok: false, failure: createFailure(result.failure) };
92979
+ const printed = result.stdout.trim().split("\n").at(-1)?.trim() ?? "";
92980
+ return { ok: true, url: safeUrl(printed, host) ?? "" };
92981
+ } finally {
92982
+ await rm4(dir, { recursive: true, force: true });
92983
+ }
92984
+ },
92985
+ async merge(input) {
92986
+ const host = hostOf(input.remoteUrl);
92987
+ if (!isGitHub(host)) return { ok: false, failure: unsupported(host) };
92988
+ if (!Number.isInteger(input.number) || input.number <= 0) {
92989
+ return { ok: false, failure: { kind: "failed", message: "n\xFAmero de PR inv\xE1lido" } };
92990
+ }
92991
+ const args = ["pr", "merge", String(input.number), `--${input.strategy}`];
92992
+ if (input.deleteBranch) args.push("--delete-branch");
92993
+ const result = await run3(args, input.repoPath);
92994
+ if (!result.ok) return { ok: false, failure: mergeFailure(result.failure) };
92995
+ return { ok: true, url: "" };
92996
+ }
92997
+ };
92998
+ }
92999
+ function createFailure(failure) {
93000
+ if (failure.kind !== "failed") return failure;
93001
+ return { kind: "failed", message: "o GitHub recusou criar a pull request" };
93002
+ }
93003
+ function mergeFailure(failure) {
93004
+ if (failure.kind !== "failed") return failure;
93005
+ return { kind: "failed", message: "o GitHub recusou o merge" };
93006
+ }
93007
+ function mergeOptionsOf(settings) {
93008
+ const flagOf = (key) => settings[key] === true;
93009
+ const options = {
93010
+ merge: flagOf("mergeCommitAllowed"),
93011
+ squash: flagOf("squashMergeAllowed"),
93012
+ rebase: flagOf("rebaseMergeAllowed"),
93013
+ deleteBranchOnMerge: flagOf("deleteBranchOnMerge")
93014
+ };
93015
+ if (!options.merge && !options.squash && !options.rebase) {
93016
+ return { ...options, merge: true };
93017
+ }
93018
+ return options;
93019
+ }
93020
+ function parseJson(stdout, whenEmpty) {
93021
+ const text2 = stdout.trim();
93022
+ if (text2 === "") return whenEmpty;
93023
+ try {
93024
+ return JSON.parse(text2);
93025
+ } catch {
93026
+ return null;
93027
+ }
93028
+ }
93029
+
93030
+ // src/pr/verdict.ts
93031
+ var PASSED = /* @__PURE__ */ new Set(["SUCCESS", "NEUTRAL"]);
93032
+ var SKIPPED = /* @__PURE__ */ new Set(["SKIPPED", "STALE"]);
93033
+ var FAILED = /* @__PURE__ */ new Set([
93034
+ "FAILURE",
93035
+ "TIMED_OUT",
93036
+ "CANCELLED",
93037
+ "ACTION_REQUIRED",
93038
+ "STARTUP_FAILURE",
93039
+ "ERROR"
93040
+ ]);
93041
+ function groupOf(check3) {
93042
+ const status = check3.status.toUpperCase();
93043
+ if (status !== "COMPLETED" && status !== "SUCCESS" && status !== "FAILURE") {
93044
+ return "running";
93045
+ }
93046
+ const conclusion = check3.conclusion.toUpperCase();
93047
+ if (PASSED.has(conclusion)) return "passed";
93048
+ if (SKIPPED.has(conclusion)) return "skipped";
93049
+ if (FAILED.has(conclusion)) return "failed";
93050
+ return "running";
93051
+ }
93052
+ function countChecks(checks) {
93053
+ const counts = { failed: 0, running: 0, passed: 0, skipped: 0 };
93054
+ for (const check3 of checks) counts[groupOf(check3)] += 1;
93055
+ return counts;
93056
+ }
93057
+ function queuedCount(checks) {
93058
+ return checks.filter((check3) => {
93059
+ const status = check3.status.toUpperCase();
93060
+ return status === "QUEUED" || status === "WAITING" || status === "REQUESTED";
93061
+ }).length;
93062
+ }
93063
+ function namesOf(checks, group) {
93064
+ return checks.filter((check3) => groupOf(check3) === group).map((check3) => check3.name);
93065
+ }
93066
+ function changesRequestedBy(reviews) {
93067
+ return reviews.filter((review) => review.state.toUpperCase() === "CHANGES_REQUESTED").map((review) => review.author);
93068
+ }
93069
+ function approvedBy(reviews) {
93070
+ return reviews.filter((review) => review.state.toUpperCase() === "APPROVED").map((review) => review.author);
93071
+ }
93072
+ function decide2(pr) {
93073
+ const state = pr.state.toUpperCase();
93074
+ if (state === "MERGED") {
93075
+ return { verdict: "merged", reason: { kind: "merged", base: pr.baseRefName } };
93076
+ }
93077
+ if (state === "CLOSED") {
93078
+ return { verdict: "closed", reason: { kind: "closed" } };
93079
+ }
93080
+ const counts = countChecks(pr.checks);
93081
+ if (pr.isDraft) {
93082
+ return { verdict: "draft", reason: { kind: "draft", passed: counts.passed } };
93083
+ }
93084
+ const mergeable = pr.mergeable.toUpperCase();
93085
+ const mergeState = pr.mergeStateStatus.toUpperCase();
93086
+ if (mergeable === "CONFLICTING" || mergeState === "DIRTY") {
93087
+ return { verdict: "blocked", reason: { kind: "conflict", base: pr.baseRefName } };
93088
+ }
93089
+ if (counts.failed > 0) {
93090
+ return {
93091
+ verdict: "blocked",
93092
+ reason: { kind: "checks-failed", names: namesOf(pr.checks, "failed") }
93093
+ };
93094
+ }
93095
+ const requested = changesRequestedBy(pr.reviews);
93096
+ if (pr.reviewDecision.toUpperCase() === "CHANGES_REQUESTED" || requested.length > 0) {
93097
+ return { verdict: "blocked", reason: { kind: "changes-requested", by: requested } };
93098
+ }
93099
+ if (pr.reviewDecision.toUpperCase() === "REVIEW_REQUIRED") {
93100
+ return { verdict: "blocked", reason: { kind: "review-required" } };
93101
+ }
93102
+ if (mergeState === "BEHIND") {
93103
+ return { verdict: "blocked", reason: { kind: "behind", base: pr.baseRefName } };
93104
+ }
93105
+ if (counts.running > 0) {
93106
+ return {
93107
+ verdict: "pending",
93108
+ reason: {
93109
+ kind: "checks-running",
93110
+ names: namesOf(pr.checks, "running"),
93111
+ running: counts.running - queuedCount(pr.checks),
93112
+ queued: queuedCount(pr.checks)
93113
+ }
93114
+ };
93115
+ }
93116
+ if (mergeState === "BLOCKED") {
93117
+ return { verdict: "blocked", reason: { kind: "blocked-by-host" } };
93118
+ }
93119
+ if (mergeable !== "MERGEABLE") {
93120
+ return { verdict: "pending", reason: { kind: "mergeability-unknown" } };
93121
+ }
93122
+ if (mergeState !== "CLEAN" && mergeState !== "UNSTABLE" && mergeState !== "HAS_HOOKS") {
93123
+ return { verdict: "pending", reason: { kind: "mergeability-unknown" } };
93124
+ }
93125
+ return {
93126
+ verdict: "ready",
93127
+ reason: { kind: "ready", passed: counts.passed, approvedBy: approvedBy(pr.reviews) }
93128
+ };
93129
+ }
93130
+
93131
+ // src/pr/PrCache.ts
93132
+ var TTL_BUSY_MS = 15e3;
93133
+ var TTL_IDLE_MS = 6e4;
93134
+ var BACKOFF_START_MS = TTL_IDLE_MS;
93135
+ var BACKOFF_MAX_MS = 6e5;
93136
+ function createPrCache({
93137
+ host,
93138
+ now = () => Date.now(),
93139
+ onChange
93140
+ }) {
93141
+ const slots = /* @__PURE__ */ new Map();
93142
+ let reads = 0;
93143
+ function slotOf(projectId) {
93144
+ const existing = slots.get(projectId);
93145
+ if (existing) return existing;
93146
+ const fresh = {
93147
+ snapshot: null,
93148
+ failure: null,
93149
+ readAt: null,
93150
+ freshUntil: 0,
93151
+ failures: 0,
93152
+ inFlight: null,
93153
+ want: 0,
93154
+ served: 0
93155
+ };
93156
+ slots.set(projectId, fresh);
93157
+ return fresh;
93158
+ }
93159
+ function viewOf2(slot) {
93160
+ return { snapshot: slot.snapshot, failure: slot.failure, readAt: slot.readAt };
93161
+ }
93162
+ function ttlOf(slot) {
93163
+ if (slot.failures > 0) {
93164
+ return Math.min(BACKOFF_START_MS * 2 ** (slot.failures - 1), BACKOFF_MAX_MS);
93165
+ }
93166
+ const pulls = slot.snapshot?.pulls ?? [];
93167
+ const busy = pulls.some(
93168
+ (pull) => pull.state.toUpperCase() === "OPEN" && countChecks(pull.checks).running > 0
93169
+ );
93170
+ return busy ? TTL_BUSY_MS : TTL_IDLE_MS;
93171
+ }
93172
+ async function fetch(project2, slot, want) {
93173
+ reads += 1;
93174
+ try {
93175
+ const read = await host.read({ repoPath: project2.path, remoteUrl: project2.remoteUrl });
93176
+ if (read.ok) {
93177
+ const changed = digestOf(slot.snapshot) !== digestOf(read.snapshot);
93178
+ slot.snapshot = read.snapshot;
93179
+ slot.failure = null;
93180
+ slot.readAt = read.snapshot.readAt;
93181
+ slot.failures = 0;
93182
+ if (changed) onChange?.(project2.id);
93183
+ } else {
93184
+ slot.failure = read.failure;
93185
+ slot.failures += 1;
93186
+ }
93187
+ } catch {
93188
+ slot.failure = { kind: "failed", message: "n\xE3o deu para consultar o host" };
93189
+ slot.failures += 1;
93190
+ } finally {
93191
+ slot.freshUntil = now() + ttlOf(slot);
93192
+ slot.served = Math.max(slot.served, want);
93193
+ }
93194
+ return viewOf2(slot);
93195
+ }
93196
+ function start(project2, slot) {
93197
+ const current = slot.inFlight;
93198
+ if (current !== null) {
93199
+ if (current.want >= slot.want) return current.promise;
93200
+ return current.promise.then(() => start(project2, slot));
93201
+ }
93202
+ const holder = { want: slot.want, promise: Promise.resolve(viewOf2(slot)) };
93203
+ holder.promise = fetch(project2, slot, holder.want).finally(() => {
93204
+ if (slot.inFlight === holder) slot.inFlight = null;
93205
+ });
93206
+ slot.inFlight = holder;
93207
+ return holder.promise;
93208
+ }
93209
+ return {
93210
+ get(project2) {
93211
+ const slot = slotOf(project2.id);
93212
+ const known = slot.snapshot !== null || slot.failure !== null;
93213
+ if (!known || slot.want > slot.served) return start(project2, slot);
93214
+ if (now() < slot.freshUntil) return Promise.resolve(viewOf2(slot));
93215
+ void start(project2, slot).catch(() => void 0);
93216
+ return Promise.resolve(viewOf2(slot));
93217
+ },
93218
+ invalidate(projectId) {
93219
+ const slot = slots.get(projectId);
93220
+ if (!slot) return;
93221
+ slot.freshUntil = 0;
93222
+ slot.want += 1;
93223
+ slot.failures = 0;
93224
+ },
93225
+ forget(projectId) {
93226
+ slots.delete(projectId);
93227
+ },
93228
+ get reads() {
93229
+ return reads;
93230
+ }
93231
+ };
93232
+ }
93233
+ function digestOf(snapshot) {
93234
+ if (snapshot === null) return "";
93235
+ const { readAt: _ignored, ...rest } = snapshot;
93236
+ return JSON.stringify(rest);
93237
+ }
93238
+
92054
93239
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/wrapper.mjs
92055
93240
  var import_stream2 = __toESM(require_stream(), 1);
92056
93241
  var import_extension = __toESM(require_extension(), 1);
@@ -92143,6 +93328,13 @@ function registerAcpWebSocket({
92143
93328
  // with empty dropdowns until the agent happened to mention something would
92144
93329
  // look broken for as long as nothing did.
92145
93330
  configOptions: [...info.configOptions],
93331
+ // Who owns the mode selector, and what the policy is set to — on attach for
93332
+ // the same reason `configOptions` is: a composer whose mode pill appeared
93333
+ // only once something changed would look broken for as long as nothing did.
93334
+ modeOwner: modeOwnerOf(info),
93335
+ lumemMode: info.lumemMode,
93336
+ lumemModeDefault: info.lumemModeDefault,
93337
+ cwd: info.cwd,
92146
93338
  transcript: [...acpManager.transcript(sessionId)]
92147
93339
  });
92148
93340
  const offEvent = acpManager.onEvent(
@@ -92177,6 +93369,10 @@ function registerAcpWebSocket({
92177
93369
  });
92178
93370
  return;
92179
93371
  }
93372
+ if (message.type === "set_lumem_mode") {
93373
+ acpManager.setLumemMode(sessionId, message.mode);
93374
+ return;
93375
+ }
92180
93376
  acpManager.respondToPermission(sessionId, message.requestId, message.optionId);
92181
93377
  } catch (error40) {
92182
93378
  reportFailure(error40, message.type);
@@ -92205,8 +93401,8 @@ function registerAcpWebSocket({
92205
93401
  });
92206
93402
  app.addHook("onClose", async () => {
92207
93403
  for (const client2 of wss.clients) client2.close(CLOSE_GOING_AWAY, "daemon shutting down");
92208
- await new Promise((resolve5) => {
92209
- wss.close(() => resolve5());
93404
+ await new Promise((resolve6) => {
93405
+ wss.close(() => resolve6());
92210
93406
  });
92211
93407
  });
92212
93408
  }
@@ -92599,8 +93795,8 @@ function registerPtyWebSocket({
92599
93795
  });
92600
93796
  app.addHook("onClose", async () => {
92601
93797
  for (const client2 of wss.clients) client2.close(CLOSE_GOING_AWAY2, "daemon shutting down");
92602
- await new Promise((resolve5) => {
92603
- wss.close(() => resolve5());
93798
+ await new Promise((resolve6) => {
93799
+ wss.close(() => resolve6());
92604
93800
  });
92605
93801
  });
92606
93802
  }
@@ -92608,12 +93804,12 @@ function registerPtyWebSocket({
92608
93804
  // src/web/static.ts
92609
93805
  var import_static = __toESM(require_static(), 1);
92610
93806
  import { existsSync as existsSync4 } from "node:fs";
92611
- import { join as join15 } from "node:path";
93807
+ import { join as join16 } from "node:path";
92612
93808
  import { fileURLToPath as fileURLToPath2 } from "node:url";
92613
93809
  var DAEMON_PREFIXES = ["/trpc", "/pty", "/acp", "/memory"];
92614
93810
  function resolveWebRoot(override = null) {
92615
93811
  const candidate = override ?? fileURLToPath2(new URL("../web/", import.meta.url));
92616
- return existsSync4(join15(candidate, "index.html")) ? candidate : null;
93812
+ return existsSync4(join16(candidate, "index.html")) ? candidate : null;
92617
93813
  }
92618
93814
  var IMMUTABLE = "public, max-age=31536000, immutable";
92619
93815
  var REVALIDATE = "no-cache";
@@ -93276,14 +94472,305 @@ var memoryRouter = router({
93276
94472
  )
93277
94473
  });
93278
94474
 
94475
+ // src/pr/view.ts
94476
+ var GROUP_ORDER = { failed: 0, running: 1, passed: 2, skipped: 3 };
94477
+ function durationOf(check3) {
94478
+ if (check3.startedAt === null) return null;
94479
+ const start = Date.parse(check3.startedAt);
94480
+ const end = check3.completedAt === null ? Date.now() : Date.parse(check3.completedAt);
94481
+ if (Number.isNaN(start) || Number.isNaN(end)) return null;
94482
+ return Math.max(0, end - start);
94483
+ }
94484
+ function checkViewOf(check3, host) {
94485
+ return {
94486
+ name: check3.name,
94487
+ app: check3.app,
94488
+ group: groupOf(check3),
94489
+ url: safeUrl(check3.url, host),
94490
+ durationMs: durationOf(check3)
94491
+ };
94492
+ }
94493
+ function viewOf({ pull, host, alsoOpen }) {
94494
+ const { verdict, reason } = decide2(pull);
94495
+ const checks = pull.checks.map((check3) => checkViewOf(check3, host)).sort((a, b) => GROUP_ORDER[a.group] - GROUP_ORDER[b.group]);
94496
+ return {
94497
+ number: pull.number,
94498
+ url: safeUrl(pull.url, host),
94499
+ title: pull.title,
94500
+ verdict,
94501
+ reason,
94502
+ counts: countChecks(pull.checks),
94503
+ checks,
94504
+ base: pull.baseRefName,
94505
+ head: pull.headRefName,
94506
+ updatedAt: pull.updatedAt,
94507
+ author: pull.author,
94508
+ alsoOpen
94509
+ };
94510
+ }
94511
+ var CLOSED_PR_MAX_AGE_DAYS = 30;
94512
+ function pickForBranch(pulls, branch, now = Date.now()) {
94513
+ const mine = pulls.filter((pull) => pull.headRefName === branch);
94514
+ if (mine.length === 0) return null;
94515
+ const open = mine.filter((pull) => pull.state.toUpperCase() === "OPEN");
94516
+ const recent = (pull) => {
94517
+ const at = Date.parse(pull.updatedAt);
94518
+ if (Number.isNaN(at)) return false;
94519
+ return now - at <= CLOSED_PR_MAX_AGE_DAYS * 24 * 60 * 60 * 1e3;
94520
+ };
94521
+ const byUpdated = (a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt);
94522
+ if (open.length > 0) {
94523
+ const [first2] = [...open].sort(byUpdated);
94524
+ return { pull: first2, alsoOpen: open.length - 1 };
94525
+ }
94526
+ const closed = mine.filter(recent).sort(byUpdated);
94527
+ const [first] = closed;
94528
+ return first === void 0 ? null : { pull: first, alsoOpen: 0 };
94529
+ }
94530
+
94531
+ // src/routers/pr.ts
94532
+ var worktreeInput = external_exports.object({ worktreeId: external_exports.string().min(1) });
94533
+ var projectInput = external_exports.object({ projectId: external_exports.string().min(1) });
94534
+ var strategySchema = external_exports.enum(["merge", "squash", "rebase"]);
94535
+ async function remoteOf(ctx, project2) {
94536
+ return project2.remoteUrl ?? await ctx.git.getRemoteUrl(project2.path);
94537
+ }
94538
+ async function checkoutOf(ctx, worktreeId) {
94539
+ const worktree2 = await createWorktreeRepository(ctx.db).findById(worktreeId);
94540
+ if (!worktree2) throw new DomainError("NOT_FOUND", `worktree ${worktreeId} n\xE3o existe`);
94541
+ if (worktree2.state === "missing") {
94542
+ throw new DomainError("BLOCKED", `a worktree "${worktree2.name}" n\xE3o est\xE1 no disco`);
94543
+ }
94544
+ const project2 = await createProjectRepository(ctx.db).findById(worktree2.projectId);
94545
+ if (!project2) {
94546
+ throw new DomainError("NOT_FOUND", `projeto ${worktree2.projectId} n\xE3o existe`);
94547
+ }
94548
+ return {
94549
+ project: { id: project2.id, path: project2.path, remoteUrl: await remoteOf(ctx, project2) },
94550
+ branch: worktree2.branch,
94551
+ base: project2.defaultBranch,
94552
+ cwd: worktree2.path
94553
+ };
94554
+ }
94555
+ function failureViewOf(entry) {
94556
+ if (entry.failure === null) return null;
94557
+ return {
94558
+ kind: entry.failure.kind,
94559
+ message: entry.failure.message,
94560
+ retryAt: entry.failure.retryAt ?? null
94561
+ };
94562
+ }
94563
+ var NO_MERGE_OPTIONS = {
94564
+ merge: false,
94565
+ squash: false,
94566
+ rebase: false,
94567
+ deleteBranchOnMerge: false
94568
+ };
94569
+ var prRouter = router({
94570
+ /**
94571
+ * A barra, inteira, numa leitura só.
94572
+ *
94573
+ * Ela precisa de sete coisas que vêm de três lugares — o host, o git local e
94574
+ * o banco —, e pedi-las em três chamadas daria três respostas de instantes
94575
+ * diferentes na mesma barra.
94576
+ */
94577
+ getByWorktree: publicProcedure.input(worktreeInput).query(
94578
+ ({ ctx, input }) => domainSafeAsync(async () => {
94579
+ const checkout = await checkoutOf(ctx, input.worktreeId);
94580
+ const [entry, published] = await Promise.all([
94581
+ ctx.pr.get(checkout.project),
94582
+ // Barato e local: lê a referência de rastreamento do disco. É o que
94583
+ // separa "sem pull request" de "branch não publicada" — as duas neutras,
94584
+ // e dizer a errada manda a pessoa procurar uma PR que não podia existir.
94585
+ ctx.git.hasRemoteBranch(checkout.cwd, checkout.branch)
94586
+ ]);
94587
+ const snapshot = entry.snapshot;
94588
+ const host = snapshot?.host ?? null;
94589
+ const picked = snapshot === null ? null : pickForBranch(snapshot.pulls, checkout.branch);
94590
+ return {
94591
+ pull: picked === null ? null : viewOf({ pull: picked.pull, host, alsoOpen: picked.alsoOpen }),
94592
+ failure: failureViewOf(entry),
94593
+ readAt: entry.readAt,
94594
+ host,
94595
+ branch: checkout.branch,
94596
+ base: checkout.base,
94597
+ published,
94598
+ compareUrl: snapshot === null ? null : compareUrl({
94599
+ host,
94600
+ repo: snapshot.repo,
94601
+ base: checkout.base,
94602
+ head: checkout.branch
94603
+ }),
94604
+ merge: snapshot?.merge ?? NO_MERGE_OPTIONS
94605
+ };
94606
+ })
94607
+ ),
94608
+ /**
94609
+ * Os marcadores da sidebar, num pedido só.
94610
+ *
94611
+ * F3.1 e F3.3: N worktrees não fazem N consultas, e a cor sai do **mesmo**
94612
+ * veredito da barra. Worktree sem PR não entra na lista — marcador cinza em
94613
+ * cinco linhas ensina o olho a ignorar a coluna inteira (Q9).
94614
+ */
94615
+ listByProject: publicProcedure.input(projectInput).query(
94616
+ ({ ctx, input }) => domainSafeAsync(async () => {
94617
+ const project2 = await createProjectRepository(ctx.db).findById(input.projectId);
94618
+ if (!project2) throw new DomainError("NOT_FOUND", `projeto ${input.projectId} n\xE3o existe`);
94619
+ const entry = await ctx.pr.get({
94620
+ id: project2.id,
94621
+ path: project2.path,
94622
+ remoteUrl: await remoteOf(ctx, project2)
94623
+ });
94624
+ const snapshot = entry.snapshot;
94625
+ if (snapshot === null) return [];
94626
+ const worktrees = await createWorktreeRepository(ctx.db).listByProject(project2.id);
94627
+ return worktrees.flatMap((worktree2) => {
94628
+ const picked = pickForBranch(snapshot.pulls, worktree2.branch);
94629
+ if (picked === null) return [];
94630
+ const view = viewOf({ pull: picked.pull, host: snapshot.host, alsoOpen: picked.alsoOpen });
94631
+ return [{ worktreeId: worktree2.id, number: view.number, verdict: view.verdict }];
94632
+ });
94633
+ })
94634
+ ),
94635
+ /**
94636
+ * F7 — mesclar.
94637
+ *
94638
+ * O portão é o veredito, e ele é **relido aqui** (F7.2). Recusar só na tela
94639
+ * seria conforto: quem garante é a procedure, e o teste que a prova chama
94640
+ * daqui, sem passar pelo botão.
94641
+ */
94642
+ merge: publicProcedure.input(worktreeInput.extend({ strategy: strategySchema, deleteBranch: external_exports.boolean() })).mutation(
94643
+ ({ ctx, input }) => domainSafeAsync(async () => {
94644
+ const checkout = await checkoutOf(ctx, input.worktreeId);
94645
+ ctx.pr.invalidate(checkout.project.id);
94646
+ const entry = await ctx.pr.get(checkout.project);
94647
+ const snapshot = entry.snapshot;
94648
+ if (entry.failure !== null) {
94649
+ throw new DomainError(
94650
+ "BLOCKED",
94651
+ `n\xE3o deu para confirmar o estado da pull request: ${entry.failure.message}`
94652
+ );
94653
+ }
94654
+ if (snapshot === null) {
94655
+ throw new DomainError("BLOCKED", "n\xE3o deu para ler o estado da pull request");
94656
+ }
94657
+ const picked = pickForBranch(snapshot.pulls, checkout.branch);
94658
+ if (picked === null) {
94659
+ throw new DomainError("NOT_FOUND", "esta worktree n\xE3o tem pull request");
94660
+ }
94661
+ const view = viewOf({ pull: picked.pull, host: snapshot.host, alsoOpen: picked.alsoOpen });
94662
+ if (view.verdict !== "ready") {
94663
+ throw new DomainError(
94664
+ "BLOCKED",
94665
+ `a pull request #${String(view.number)} n\xE3o est\xE1 pronta para merge`
94666
+ );
94667
+ }
94668
+ if (!allows(snapshot.merge, input.strategy)) {
94669
+ throw new DomainError(
94670
+ "BLOCKED",
94671
+ `o reposit\xF3rio n\xE3o permite merge por ${input.strategy}`
94672
+ );
94673
+ }
94674
+ const write = await ctx.prHost.merge({
94675
+ repoPath: checkout.cwd,
94676
+ remoteUrl: checkout.project.remoteUrl,
94677
+ // Do cache do daemon, **nunca** do cliente (§4.2.12 do PRD).
94678
+ number: view.number,
94679
+ strategy: input.strategy,
94680
+ deleteBranch: input.deleteBranch
94681
+ });
94682
+ if (!write.ok) throw new DomainError("GIT_FAILED", write.failure.message);
94683
+ ctx.pr.invalidate(checkout.project.id);
94684
+ ctx.events.emit({ type: "worktree.changed", projectId: checkout.project.id });
94685
+ return { number: view.number };
94686
+ })
94687
+ ),
94688
+ /**
94689
+ * O que o formulário propõe, e por que ele não é um campo do `PrStatus`.
94690
+ *
94691
+ * A [Q4](../../../../docs/prd/pull-request-status/open-questions.md) e a F7.6
94692
+ * pedem título vindo do assunto do último commit. Isso custa um `git log`, e
94693
+ * só interessa quando o formulário abre — dentro do `PrStatus` seria um
94694
+ * processo git a cada ciclo de poll, por worktree aberta.
94695
+ */
94696
+ draft: publicProcedure.input(worktreeInput).query(
94697
+ ({ ctx, input }) => domainSafeAsync(async () => {
94698
+ const checkout = await checkoutOf(ctx, input.worktreeId);
94699
+ return {
94700
+ title: await ctx.git.getSubject(checkout.cwd) ?? "",
94701
+ base: checkout.base,
94702
+ head: checkout.branch
94703
+ };
94704
+ })
94705
+ ),
94706
+ /**
94707
+ * F7 — criar.
94708
+ *
94709
+ * Título e corpo vêm da tela, que é o que o §4.2 do PRD existe para aguentar.
94710
+ * A base e a head **não** vêm: elas saem do banco e do git local.
94711
+ */
94712
+ create: publicProcedure.input(
94713
+ worktreeInput.extend({
94714
+ title: external_exports.string().min(1).max(256),
94715
+ body: external_exports.string().max(6e4),
94716
+ draft: external_exports.boolean()
94717
+ })
94718
+ ).mutation(
94719
+ ({ ctx, input }) => domainSafeAsync(async () => {
94720
+ const checkout = await checkoutOf(ctx, input.worktreeId);
94721
+ const published = await ctx.git.hasRemoteBranch(checkout.cwd, checkout.branch);
94722
+ if (!published) {
94723
+ throw new DomainError(
94724
+ "BLOCKED",
94725
+ `a branch "${checkout.branch}" ainda n\xE3o foi publicada`
94726
+ );
94727
+ }
94728
+ const write = await ctx.prHost.create({
94729
+ repoPath: checkout.cwd,
94730
+ remoteUrl: checkout.project.remoteUrl,
94731
+ base: checkout.base,
94732
+ head: checkout.branch,
94733
+ title: input.title,
94734
+ body: input.body,
94735
+ draft: input.draft
94736
+ });
94737
+ if (!write.ok) throw new DomainError("GIT_FAILED", write.failure.message);
94738
+ ctx.pr.invalidate(checkout.project.id);
94739
+ ctx.events.emit({ type: "worktree.changed", projectId: checkout.project.id });
94740
+ return { url: write.url };
94741
+ })
94742
+ ),
94743
+ /**
94744
+ * "Tentar de novo", e o `⟳` da coluna. Não escreve nada; só invalida.
94745
+ *
94746
+ * A entrada é o **escopo**, e não o id do projeto, porque o escopo é o que o
94747
+ * cliente sabe **na hora do clique** — o projeto de uma worktree chega uma
94748
+ * consulta depois. Pedir o projeto fazia o botão não fazer nada quando o
94749
+ * clique vinha cedo, que é exatamente quando alguém clica em recarregar.
94750
+ */
94751
+ refresh: publicProcedure.input(external_exports.object({ scopeType: external_exports.enum(["project", "worktree"]), scopeId: external_exports.string().min(1) })).mutation(
94752
+ ({ ctx, input }) => domainSafeAsync(async () => {
94753
+ const projectId = input.scopeType === "project" ? input.scopeId : (await createWorktreeRepository(ctx.db).findById(input.scopeId))?.projectId ?? null;
94754
+ if (projectId === null) {
94755
+ throw new DomainError("NOT_FOUND", `worktree ${input.scopeId} n\xE3o existe`);
94756
+ }
94757
+ ctx.pr.invalidate(projectId);
94758
+ return { ok: true };
94759
+ })
94760
+ )
94761
+ });
94762
+ function allows(options, strategy) {
94763
+ return options[strategy];
94764
+ }
94765
+
93279
94766
  // src/routers/project.ts
93280
94767
  import { existsSync as existsSync5 } from "node:fs";
93281
- import { rename as rename4, rm as rm6 } from "node:fs/promises";
93282
- import { basename as basename3, dirname as dirname11, isAbsolute as isAbsolute5, normalize as normalize4 } from "node:path";
94768
+ import { rename as rename4, rm as rm7 } from "node:fs/promises";
94769
+ import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute6, normalize as normalize4 } from "node:path";
93283
94770
 
93284
94771
  // src/git/clone.ts
93285
94772
  import { spawn as spawn2 } from "node:child_process";
93286
- import { rename as rename3, rm as rm4 } from "node:fs/promises";
94773
+ import { rename as rename3, rm as rm5 } from "node:fs/promises";
93287
94774
  var CloneError = class extends DomainError {
93288
94775
  constructor(failure, message, options) {
93289
94776
  super(failure === "internal" ? "BLOCKED" : "GIT_FAILED", message, options);
@@ -93304,7 +94791,7 @@ async function cloneRepository(options) {
93304
94791
  await rename3(tempPath, finalTarget);
93305
94792
  return finalTarget;
93306
94793
  } catch (error40) {
93307
- await rm4(tempPath, { recursive: true, force: true }).catch(() => {
94794
+ await rm5(tempPath, { recursive: true, force: true }).catch(() => {
93308
94795
  });
93309
94796
  throw error40;
93310
94797
  }
@@ -93348,7 +94835,7 @@ function runClone({
93348
94835
  silenceMs = DEFAULT_SILENCE_MS,
93349
94836
  env
93350
94837
  }) {
93351
- return new Promise((resolve5, reject) => {
94838
+ return new Promise((resolve6, reject) => {
93352
94839
  const child = spawn2("git", cloneArgs(rawUrl, tempPath), {
93353
94840
  env: cloneEnv(env),
93354
94841
  stdio: ["ignore", "ignore", "pipe"]
@@ -93371,7 +94858,7 @@ function runClone({
93371
94858
  silence.stop();
93372
94859
  signal?.removeEventListener("abort", onAbort);
93373
94860
  if (error40) reject(error40);
93374
- else resolve5();
94861
+ else resolve6();
93375
94862
  }
93376
94863
  function onAbort() {
93377
94864
  finish(new CloneError("internal", "clone cancelado"));
@@ -93402,7 +94889,7 @@ function runClone({
93402
94889
  if (killTimer) clearTimeout(killTimer);
93403
94890
  if (code === 0) return finish();
93404
94891
  const stderr = errors.text();
93405
- finish(new CloneError(classify(stderr), lastMeaningfulLine(stderr) || `git saiu com ${code}`));
94892
+ finish(new CloneError(classify2(stderr), lastMeaningfulLine(stderr) || `git saiu com ${code}`));
93406
94893
  });
93407
94894
  });
93408
94895
  }
@@ -93410,7 +94897,7 @@ async function rewriteOrigin({ rawUrl, url: url2, tempPath }) {
93410
94897
  if (rawUrl === url2) return;
93411
94898
  await execGit(["remote", "set-url", "origin", url2], { cwd: tempPath });
93412
94899
  }
93413
- function classify(stderr) {
94900
+ function classify2(stderr) {
93414
94901
  if (/authentication failed|could not read username|could not read password|invalid username or password|permission denied \(publickey|terminal prompts disabled|host key verification failed/i.test(
93415
94902
  stderr
93416
94903
  )) {
@@ -93509,134 +94996,7 @@ var SilenceTimer = class {
93509
94996
 
93510
94997
  // src/git/clone-plan.ts
93511
94998
  import { homedir } from "node:os";
93512
- import { dirname as dirname9, join as join16, normalize as normalize3 } from "node:path";
93513
-
93514
- // src/git/git-url.ts
93515
- var ALLOWED = /* @__PURE__ */ new Set(["https", "http", "ssh", "file"]);
93516
- var HELPER = /^([A-Za-z][A-Za-z0-9+.-]*)::/;
93517
- var SCHEME = /^([A-Za-z][A-Za-z0-9+.-]*):\/\//;
93518
- var CONTROL = /[\u0000-\u001f\u007f]/;
93519
- function refuse(rule, message) {
93520
- return { ok: false, rule, message };
93521
- }
93522
- function refuseScheme(scheme) {
93523
- if (scheme === "git") {
93524
- return refuse(
93525
- "scheme",
93526
- "o transporte git:// n\xE3o \xE9 aceito: ele n\xE3o autentica nem verifica integridade \u2014 use a URL https ou ssh do mesmo reposit\xF3rio"
93527
- );
93528
- }
93529
- return refuse(
93530
- "scheme",
93531
- `o transporte "${scheme}" n\xE3o est\xE1 na lista de transportes aceitos (https, http, ssh, file)`
93532
- );
93533
- }
93534
- function parseGitUrl(input) {
93535
- const raw = input.trim();
93536
- if (raw === "") return refuse("empty", "informe a URL do reposit\xF3rio");
93537
- if (CONTROL.test(raw)) {
93538
- return refuse("control-chars", "a URL tem caracteres de controle");
93539
- }
93540
- if (raw.startsWith("-")) {
93541
- return refuse("leading-dash", "a URL n\xE3o pode come\xE7ar com '-'");
93542
- }
93543
- const helper = HELPER.exec(raw);
93544
- if (helper) return refuseScheme(helper[1].toLowerCase());
93545
- const scheme = SCHEME.exec(raw);
93546
- if (scheme) return parseWithScheme(raw, scheme[1].toLowerCase());
93547
- if (isScp(raw)) return parseScp(raw);
93548
- return refuse(
93549
- "scheme",
93550
- "n\xE3o reconheci isto como URL git: use https://, ssh://, file:// ou a forma git@host:org/repo.git"
93551
- );
93552
- }
93553
- function scpColon(raw) {
93554
- const at = raw.indexOf("@");
93555
- return raw.indexOf(":", at === -1 ? 0 : at + 1);
93556
- }
93557
- function isScp(raw) {
93558
- const colon = scpColon(raw);
93559
- if (colon <= 0) return false;
93560
- const slash = raw.indexOf("/");
93561
- return slash === -1 || colon < slash;
93562
- }
93563
- function parseWithScheme(raw, scheme) {
93564
- if (!ALLOWED.has(scheme)) return refuseScheme(scheme);
93565
- let parsed;
93566
- try {
93567
- parsed = new URL(raw);
93568
- } catch {
93569
- return refuse("host", `n\xE3o consegui ler ${raw} como URL`);
93570
- }
93571
- if (scheme === "file") return parseFile(raw, parsed);
93572
- if (parsed.hostname === "" || raw.slice(scheme.length + "://".length).startsWith("/")) {
93573
- return refuse("host", "a URL n\xE3o tem host");
93574
- }
93575
- const path = parsed.pathname.replace(/^\/+/, "");
93576
- if (path === "") {
93577
- return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
93578
- }
93579
- const sanitized = new URL(raw);
93580
- sanitized.username = "";
93581
- sanitized.password = "";
93582
- return {
93583
- ok: true,
93584
- raw,
93585
- url: {
93586
- scheme,
93587
- href: sanitized.href,
93588
- host: parsed.host,
93589
- path,
93590
- insecure: scheme === "http",
93591
- scp: false
93592
- }
93593
- };
93594
- }
93595
- function parseFile(raw, parsed) {
93596
- if (parsed.hostname !== "" && parsed.hostname !== "localhost") {
93597
- return refuse("host", "file:// com host n\xE3o \xE9 aceito; use file:///caminho/absoluto");
93598
- }
93599
- const path = decodeURIComponent(parsed.pathname);
93600
- if (!path.startsWith("/")) {
93601
- return refuse("path", "o caminho de um file:// precisa ser absoluto");
93602
- }
93603
- return {
93604
- ok: true,
93605
- raw,
93606
- url: { scheme: "file", href: `file://${parsed.pathname}`, host: "", path, insecure: false, scp: false }
93607
- };
93608
- }
93609
- function parseScp(raw) {
93610
- const colon = scpColon(raw);
93611
- const authority = raw.slice(0, colon);
93612
- const path = raw.slice(colon + 1);
93613
- const at = authority.lastIndexOf("@");
93614
- const user = at === -1 ? "" : authority.slice(0, at);
93615
- const host = authority.slice(at + 1);
93616
- if (host === "") return refuse("host", "a URL n\xE3o tem host");
93617
- if (path === "") return refuse("path", "a URL n\xE3o diz qual reposit\xF3rio");
93618
- const cleanUser = user.includes(":") ? user.slice(0, user.indexOf(":")) : user;
93619
- const prefix = cleanUser === "" ? "" : `${cleanUser}@`;
93620
- return {
93621
- ok: true,
93622
- raw,
93623
- url: {
93624
- scheme: "ssh",
93625
- href: `${prefix}${host}:${path}`,
93626
- host,
93627
- path: path.replace(/^\/+/, ""),
93628
- insecure: false,
93629
- scp: true
93630
- }
93631
- };
93632
- }
93633
- function repoNameOf(url2) {
93634
- const segments = url2.path.split("/").filter((segment) => segment !== "");
93635
- const last = segments.at(-1) ?? "";
93636
- return last.endsWith(".git") ? last.slice(0, -".git".length) : last;
93637
- }
93638
-
93639
- // src/git/clone-plan.ts
94999
+ import { dirname as dirname9, join as join17, normalize as normalize3 } from "node:path";
93640
95000
  function planClone({
93641
95001
  source,
93642
95002
  workspacesDir,
@@ -93667,11 +95027,11 @@ function rawUrlOf(source) {
93667
95027
  return parsed.ok ? parsed.raw : null;
93668
95028
  }
93669
95029
  function tempCloneDir(target, jobId) {
93670
- return join16(dirname9(target), `.lumem-clone-${jobId}`);
95030
+ return join17(dirname9(target), `.lumem-clone-${jobId}`);
93671
95031
  }
93672
95032
 
93673
95033
  // src/git/managed-dir.ts
93674
- import { lstat as lstat4, readdir as readdir6, realpath as realpath5, rm as rm5, rmdir as rmdir2 } from "node:fs/promises";
95034
+ import { lstat as lstat4, readdir as readdir6, realpath as realpath5, rm as rm6, rmdir as rmdir2 } from "node:fs/promises";
93675
95035
  import { dirname as dirname10 } from "node:path";
93676
95036
  async function deleteManagedRepo({ path, workspacesDir }) {
93677
95037
  let info;
@@ -93688,7 +95048,7 @@ async function deleteManagedRepo({ path, workspacesDir }) {
93688
95048
  );
93689
95049
  }
93690
95050
  await requireInsideTree(path, workspacesDir);
93691
- await rm5(path, { recursive: true, force: true });
95051
+ await rm6(path, { recursive: true, force: true });
93692
95052
  }
93693
95053
  async function collectEmptyProjectHome(home, workspacesDir) {
93694
95054
  await requireInsideTree(home, workspacesDir).catch(() => {
@@ -93752,7 +95112,7 @@ async function resolveAvailableName(ctx, workspaceId, desired) {
93752
95112
  if (!taken.has(candidate)) return candidate;
93753
95113
  }
93754
95114
  }
93755
- var pathSchema = external_exports.string().trim().min(1, "informe o caminho do reposit\xF3rio").refine((value) => isAbsolute5(value), "o caminho precisa ser absoluto").transform((value) => normalize4(value));
95115
+ var pathSchema = external_exports.string().trim().min(1, "informe o caminho do reposit\xF3rio").refine((value) => isAbsolute6(value), "o caminho precisa ser absoluto").transform((value) => normalize4(value));
93756
95116
  var projectRouter = router({
93757
95117
  listByWorkspace: publicProcedure.input(external_exports.object({ workspaceId: external_exports.string().min(1) })).query(async ({ ctx, input }) => {
93758
95118
  const rows = await createProjectRepository(ctx.db).listByWorkspace(input.workspaceId);
@@ -93813,7 +95173,7 @@ var projectRouter = router({
93813
95173
  registerProject(ctx, {
93814
95174
  workspaceId: input.workspaceId,
93815
95175
  path: input.path,
93816
- name: input.name ?? basename3(input.path)
95176
+ name: input.name ?? basename4(input.path)
93817
95177
  })
93818
95178
  )
93819
95179
  )
@@ -93827,23 +95187,27 @@ var projectRouter = router({
93827
95187
  ),
93828
95188
  remove: publicProcedure.input(external_exports.object({ id: external_exports.string().min(1) })).mutation(
93829
95189
  ({ ctx, input }) => domainSafeAsync(async () => {
93830
- const running = await ctx.sessionStore.listRunningInScope("project", input.id);
93831
- if (running.length > 0) {
95190
+ const projects = createProjectRepository(ctx.db);
95191
+ const worktrees = createWorktreeRepository(ctx.db);
95192
+ const row = await projects.findById(input.id);
95193
+ const owned = await worktrees.listByProject(input.id);
95194
+ const vanishing = row?.managed === true ? [] : owned;
95195
+ const runningLists = await Promise.all([
95196
+ ctx.sessionStore.listRunningInScope("project", input.id),
95197
+ ...vanishing.map((wt) => ctx.sessionStore.listRunningInScope("worktree", wt.id))
95198
+ ]);
95199
+ const running = runningLists.reduce((total, list) => total + list.length, 0);
95200
+ if (running > 0) {
93832
95201
  throw new DomainError(
93833
95202
  "BLOCKED",
93834
- `o projeto tem ${running.length} sess\xE3o(\xF5es) rodando; encerre-as antes de remover`
95203
+ `o projeto tem ${running} sess\xE3o(\xF5es) rodando; encerre-as antes de remover`
93835
95204
  );
93836
95205
  }
93837
- const projects = createProjectRepository(ctx.db);
93838
- const row = await projects.findById(input.id);
93839
- if (row) {
93840
- const worktrees = await createWorktreeRepository(ctx.db).listByProject(row.id);
93841
- if (worktrees.length > 0) {
93842
- throw new DomainError(
93843
- "IN_USE",
93844
- `o projeto ainda tem worktrees registradas (${worktrees.length}); remova-as antes`
93845
- );
93846
- }
95206
+ if (row?.managed && owned.length > 0) {
95207
+ throw new DomainError(
95208
+ "IN_USE",
95209
+ `o projeto ainda tem worktrees registradas (${owned.length}); remova-as antes`
95210
+ );
93847
95211
  }
93848
95212
  if (row) {
93849
95213
  if (row.managed) {
@@ -93855,7 +95219,11 @@ var projectRouter = router({
93855
95219
  ).catch(() => false);
93856
95220
  }
93857
95221
  await projects.remove(input.id);
93858
- if (row) ctx.events.emit({ type: "project.changed", workspaceId: row.workspaceId });
95222
+ if (row) {
95223
+ ctx.events.emit({ type: "project.changed", workspaceId: row.workspaceId });
95224
+ if (vanishing.length > 0)
95225
+ ctx.events.emit({ type: "worktree.changed", projectId: input.id });
95226
+ }
93859
95227
  return { ok: true };
93860
95228
  })
93861
95229
  ),
@@ -93983,7 +95351,7 @@ async function runCloneJob(ctx, job, { rawUrl, url: url2, name }) {
93983
95351
  finalName === name ? void 0 : `o nome ${name} j\xE1 existia; registrado como ${finalName}`
93984
95352
  );
93985
95353
  } catch (error40) {
93986
- if (landed !== null) await rm6(landed, { recursive: true, force: true }).catch(() => {
95354
+ if (landed !== null) await rm7(landed, { recursive: true, force: true }).catch(() => {
93987
95355
  });
93988
95356
  await sweep();
93989
95357
  if (ctx.clones.get(job.id)?.state === "cancelled") return;
@@ -94248,24 +95616,24 @@ var sessionRouter = router({
94248
95616
 
94249
95617
  // src/routers/setup.ts
94250
95618
  import { mkdirSync as mkdirSync4 } from "node:fs";
94251
- import { join as join18 } from "node:path";
95619
+ import { join as join19 } from "node:path";
94252
95620
 
94253
95621
  // src/setup/run-command.ts
94254
- import { execFile as execFile2 } from "node:child_process";
95622
+ import { execFile as execFile3 } from "node:child_process";
94255
95623
  var DEFAULT_VERSION_TIMEOUT_MS = 3e3;
94256
- var runCommand = (command, args, { timeoutMs = DEFAULT_VERSION_TIMEOUT_MS } = {}) => new Promise((resolve5) => {
94257
- execFile2(
95624
+ var runCommand = (command, args, { timeoutMs = DEFAULT_VERSION_TIMEOUT_MS } = {}) => new Promise((resolve6) => {
95625
+ execFile3(
94258
95626
  command,
94259
95627
  [...args],
94260
95628
  { timeout: timeoutMs, maxBuffer: 1024 * 1024 },
94261
95629
  (error40, stdout, stderr) => {
94262
95630
  const output = (stdout.trim() !== "" ? stdout : stderr).trim();
94263
95631
  if (error40 === null) {
94264
- resolve5({ ok: true, output, failure: null });
95632
+ resolve6({ ok: true, output, failure: null });
94265
95633
  return;
94266
95634
  }
94267
95635
  const killed = error40.killed === true;
94268
- resolve5({
95636
+ resolve6({
94269
95637
  ok: false,
94270
95638
  output,
94271
95639
  failure: killed ? `n\xE3o respondeu em ${timeoutMs} ms` : error40.message
@@ -94303,42 +95671,86 @@ async function detectAgents({
94303
95671
  path = process.env["PATH"],
94304
95672
  env = process.env,
94305
95673
  run: run3 = runCommand,
95674
+ specs = ADAPTERS,
94306
95675
  installedAt
94307
95676
  } = {}) {
94308
- const [claude, adapter] = await Promise.all([
94309
- inspect(CLAUDE_CLI_COMMAND, null, { path, run: run3 }),
94310
- inspect(ACP_ADAPTER_COMMAND, ACP_ADAPTER_INSTALL, { path, run: run3, preferred: installedAt })
95677
+ const adapters = await Promise.all(
95678
+ specs.map(async (spec) => reportFor(spec, { path, env, run: run3, installedAt }))
95679
+ );
95680
+ return { adapters };
95681
+ }
95682
+ async function reportFor(spec, {
95683
+ path,
95684
+ env,
95685
+ run: run3,
95686
+ installedAt
95687
+ }) {
95688
+ const [adapter, cli] = await Promise.all([
95689
+ inspect(spec.command, adapterInstallCommand(spec), {
95690
+ path,
95691
+ run: run3,
95692
+ ...installedAt === void 0 ? {} : { preferred: installedAt(spec) }
95693
+ }),
95694
+ spec.cli === null ? Promise.resolve(null) : inspect(spec.cli.command, spec.cli.install, { path, run: run3 })
94311
95695
  ]);
94312
- const key = env[ANTHROPIC_API_KEY_ENV];
94313
- return { claude, adapter, apiKeyInEnv: key !== void 0 && key.trim() !== "" };
95696
+ return {
95697
+ id: spec.id,
95698
+ label: spec.label,
95699
+ adapter,
95700
+ cli,
95701
+ apiKeyEnv: spec.apiKeyEnv.find((name) => (env[name] ?? "").trim() !== "") ?? null
95702
+ };
94314
95703
  }
94315
95704
 
94316
95705
  // src/setup/install-adapter.ts
94317
95706
  import { existsSync as existsSync6 } from "node:fs";
94318
95707
  import { mkdir as mkdir8 } from "node:fs/promises";
94319
- import { join as join17 } from "node:path";
94320
- function adapterBinaryPath(dir) {
94321
- return join17(dir, "node_modules", ".bin", ACP_ADAPTER_COMMAND);
95708
+ import { join as join18 } from "node:path";
95709
+ function adapterDir(dir, spec = CLAUDE_ADAPTER) {
95710
+ return join18(dir, spec.id);
95711
+ }
95712
+ function adapterBinaryPath(dir, spec = CLAUDE_ADAPTER) {
95713
+ return join18(adapterDir(dir, spec), "node_modules", ".bin", spec.command);
95714
+ }
95715
+ function legacyAdapterBinaryPath(dir, spec) {
95716
+ return join18(dir, "node_modules", ".bin", spec.command);
94322
95717
  }
94323
95718
  async function installAdapter({
95719
+ spec = CLAUDE_ADAPTER,
94324
95720
  dir,
94325
95721
  run: run3 = runCommand,
94326
- timeoutMs = 12e4
95722
+ timeoutMs = 12e4,
95723
+ resolve: resolve6 = resolveCommandPath
94327
95724
  }) {
94328
- const binary = adapterBinaryPath(dir);
95725
+ if (spec.package === null) {
95726
+ const found = resolve6(spec.command);
95727
+ if (found === null) {
95728
+ throw new DomainError(
95729
+ "NOT_FOUND",
95730
+ `${spec.label} n\xE3o tem adaptador para instalar: o bin\xE1rio ${spec.command} tem que estar no PATH, e n\xE3o est\xE1`
95731
+ );
95732
+ }
95733
+ return { path: found, version: spec.pinnedVersion, alreadyInstalled: true };
95734
+ }
95735
+ const target = adapterDir(dir, spec);
95736
+ const binary = adapterBinaryPath(dir, spec);
94329
95737
  if (existsSync6(binary)) {
94330
- return { path: binary, version: ACP_ADAPTER_PINNED_VERSION, alreadyInstalled: true };
95738
+ return { path: binary, version: spec.pinnedVersion, alreadyInstalled: true };
95739
+ }
95740
+ const legacy = legacyAdapterBinaryPath(dir, spec);
95741
+ if (existsSync6(legacy)) {
95742
+ return { path: legacy, version: spec.pinnedVersion, alreadyInstalled: true };
94331
95743
  }
94332
- await mkdir8(dir, { recursive: true });
95744
+ await mkdir8(target, { recursive: true });
94333
95745
  const outcome = await run3(
94334
95746
  "npm",
94335
95747
  [
94336
95748
  "install",
94337
95749
  "--prefix",
94338
- dir,
95750
+ target,
94339
95751
  "--no-fund",
94340
95752
  "--no-audit",
94341
- `${ACP_ADAPTER_PACKAGE}@${ACP_ADAPTER_PINNED_VERSION}`
95753
+ `${spec.package}@${spec.pinnedVersion}`
94342
95754
  ],
94343
95755
  { timeoutMs }
94344
95756
  );
@@ -94355,10 +95767,10 @@ ${outcome.output}`.trim()
94355
95767
  if (!existsSync6(binary)) {
94356
95768
  throw new DomainError(
94357
95769
  "SPAWN_FAILED",
94358
- `o npm terminou sem erro mas ${binary} n\xE3o existe \u2014 o pacote ${ACP_ADAPTER_PACKAGE} pode ter mudado de layout`
95770
+ `o npm terminou sem erro mas ${binary} n\xE3o existe \u2014 o pacote ${spec.package} pode ter mudado de layout`
94359
95771
  );
94360
95772
  }
94361
- return { path: binary, version: ACP_ADAPTER_PINNED_VERSION, alreadyInstalled: false };
95773
+ return { path: binary, version: spec.pinnedVersion, alreadyInstalled: false };
94362
95774
  }
94363
95775
 
94364
95776
  // src/setup/login.ts
@@ -94553,6 +95965,17 @@ function diskCheck(config2, freeBytes) {
94553
95965
  }
94554
95966
 
94555
95967
  // src/routers/setup.ts
95968
+ function adaptersDir(stateDir) {
95969
+ return join19(stateDir, ADAPTERS_DIR_NAME);
95970
+ }
95971
+ function specOf(id) {
95972
+ const spec = adapterById(id ?? DEFAULT_ADAPTER_ID);
95973
+ if (spec === null) {
95974
+ throw new DomainError("INVALID_ARGUMENT", `n\xE3o existe adaptador "${id}" no cat\xE1logo`);
95975
+ }
95976
+ return spec;
95977
+ }
95978
+ var adapterInput = external_exports.object({ adapterId: external_exports.string().trim().min(1).optional() }).optional();
94556
95979
  var setupRouter = router({
94557
95980
  /** The five checks, each one able to fail without the others. */
94558
95981
  preflight: publicProcedure.query(({ ctx }) => preflight({ config: ctx.config })),
@@ -94564,7 +95987,9 @@ var setupRouter = router({
94564
95987
  * have it globally, and the flow must not ask twice for the same thing.
94565
95988
  */
94566
95989
  agents: publicProcedure.query(
94567
- ({ ctx }) => detectAgents({ installedAt: adapterBinaryPath(join18(ctx.config.stateDir, ADAPTERS_DIR_NAME)) })
95990
+ ({ ctx }) => detectAgents({
95991
+ installedAt: (spec) => adapterBinaryPath(adaptersDir(ctx.config.stateDir), spec)
95992
+ })
94568
95993
  ),
94569
95994
  /**
94570
95995
  * Installs the adapter into the daemon's own directory, at the pinned version.
@@ -94573,9 +95998,9 @@ var setupRouter = router({
94573
95998
  * costs is named in `install-adapter.ts` — the daemon runs a package manager and
94574
95999
  * then executes what it downloaded.
94575
96000
  */
94576
- installAdapter: publicProcedure.mutation(
94577
- ({ ctx }) => domainSafeAsync(
94578
- () => installAdapter({ dir: join18(ctx.config.stateDir, ADAPTERS_DIR_NAME) })
96001
+ installAdapter: publicProcedure.input(adapterInput).mutation(
96002
+ ({ ctx, input }) => domainSafeAsync(
96003
+ () => installAdapter({ spec: specOf(input?.adapterId), dir: adaptersDir(ctx.config.stateDir) })
94579
96004
  )
94580
96005
  ),
94581
96006
  /**
@@ -94591,6 +96016,8 @@ var setupRouter = router({
94591
96016
  methodId: external_exports.string().trim().min(1),
94592
96017
  /** Which adapter to ask. Defaults to what the flow installed or found. */
94593
96018
  command: external_exports.string().trim().min(1).optional(),
96019
+ /** Which catalogued adapter, when no explicit command is given. */
96020
+ adapterId: external_exports.string().trim().min(1).optional(),
94594
96021
  /**
94595
96022
  * And its arguments, because a command without them is a different program.
94596
96023
  *
@@ -94603,8 +96030,8 @@ var setupRouter = router({
94603
96030
  })
94604
96031
  ).mutation(
94605
96032
  ({ ctx, input }) => domainSafeAsync(async () => {
94606
- const command = input.command ?? ACP_ADAPTER_COMMAND;
94607
- const cwd = join18(ctx.config.stateDir, "probe");
96033
+ const command = input.command ?? specOf(input.adapterId).command;
96034
+ const cwd = join19(ctx.config.stateDir, "probe");
94608
96035
  const report = await ctx.acpManager.probe({
94609
96036
  command,
94610
96037
  ...input.args === void 0 ? {} : { args: input.args },
@@ -94633,6 +96060,60 @@ var setupRouter = router({
94633
96060
  });
94634
96061
  })
94635
96062
  ),
96063
+ /**
96064
+ * Entrar no agente, pela chamada que o próprio agente ofereceu (T10, T11).
96065
+ *
96066
+ * Três procedimentos e nenhum bloqueante, porque um deles espera **uma
96067
+ * pessoa**: o `authenticate` de um método de navegador fica pendurado até
96068
+ * alguém autorizar noutro lugar, e o pedido de mostrar uma URL chega no meio
96069
+ * dessa espera. Começar e perguntar é o mesmo desenho do login por comando, que
96070
+ * devolvia um `ptySessionId` para o cliente acompanhar.
96071
+ *
96072
+ * O `command` continua sendo conferido contra o handshake — o cliente manda um
96073
+ * `methodId`, e o que roda é o que o adaptador declarou para aquele id.
96074
+ */
96075
+ authenticate: publicProcedure.input(
96076
+ external_exports.object({
96077
+ methodId: external_exports.string().trim().min(1),
96078
+ /** Qual adaptador, quando não vem um comando explícito. */
96079
+ adapterId: external_exports.string().trim().min(1).optional(),
96080
+ command: external_exports.string().trim().min(1).optional(),
96081
+ args: external_exports.array(external_exports.string()).optional(),
96082
+ /**
96083
+ * A chave, quando o método pede uma.
96084
+ *
96085
+ * Ela entra por aqui, atravessa o daemon e vai para o adaptador. Não é
96086
+ * gravada, não é logada e **não volta** em nenhuma resposta — o que volta
96087
+ * é o estado da tentativa.
96088
+ */
96089
+ apiKey: external_exports.string().min(1).optional()
96090
+ })
96091
+ ).mutation(
96092
+ ({ ctx, input }) => domainSafeAsync(() => {
96093
+ const spec = specOf(input.adapterId);
96094
+ const cwd = join19(ctx.config.stateDir, "probe");
96095
+ mkdirSync4(cwd, { recursive: true });
96096
+ return Promise.resolve(
96097
+ ctx.agentAuth.start({
96098
+ command: input.command ?? spec.command,
96099
+ ...input.args === void 0 ? {} : { args: input.args },
96100
+ cwd,
96101
+ methodId: input.methodId,
96102
+ ...input.apiKey === void 0 ? {} : { apiKey: input.apiKey },
96103
+ adapterVersion: spec.pinnedVersion
96104
+ })
96105
+ );
96106
+ })
96107
+ ),
96108
+ /** O estado de uma tentativa de login. É o que a tela pergunta enquanto espera. */
96109
+ authState: publicProcedure.input(external_exports.object({ loginId: external_exports.string().trim().min(1) })).query(({ ctx, input }) => domainSafe(() => ctx.agentAuth.status(input.loginId))),
96110
+ /**
96111
+ * Desistir: mata o adaptador.
96112
+ *
96113
+ * Não há "cancelar" no protocolo — o `authenticate` está esperando uma pessoa,
96114
+ * e a única forma de parar de esperar é o processo acabar. Ele é do daemon.
96115
+ */
96116
+ cancelAuth: publicProcedure.input(external_exports.object({ loginId: external_exports.string().trim().min(1) })).mutation(({ ctx, input }) => domainSafe(() => ctx.agentAuth.cancel(input.loginId))),
94636
96117
  /**
94637
96118
  * One handshake, then the process dies.
94638
96119
  *
@@ -94644,12 +96125,14 @@ var setupRouter = router({
94644
96125
  external_exports.object({
94645
96126
  /** Defaults to the adapter the flow installs. */
94646
96127
  command: external_exports.string().trim().min(1).optional(),
96128
+ /** Which catalogued adapter, when no explicit command is given. */
96129
+ adapterId: external_exports.string().trim().min(1).optional(),
94647
96130
  args: external_exports.array(external_exports.string()).optional()
94648
96131
  }).optional()
94649
96132
  ).query(
94650
96133
  ({ ctx, input }) => domainSafeAsync(async () => {
94651
- const command = input?.command ?? ACP_ADAPTER_COMMAND;
94652
- const cwd = join18(ctx.config.stateDir, "probe");
96134
+ const command = input?.command ?? specOf(input?.adapterId).command;
96135
+ const cwd = join19(ctx.config.stateDir, "probe");
94653
96136
  try {
94654
96137
  mkdirSync4(cwd, { recursive: true });
94655
96138
  } catch (error40) {
@@ -94726,6 +96209,32 @@ function usageByWorktree(db, { projectId, period: period2, now }) {
94726
96209
  and(eq(sessionUsage.worktreeId, worktree.id), gte(sessionUsage.createdAt, since))
94727
96210
  ).where(eq(worktree.projectId, projectId)).groupBy(worktree.id).orderBy(sql`${SUM.tokens} desc`, worktree.name).all();
94728
96211
  }
96212
+ function usageByProjectAndAgent(db, { workspaceId, period: period2, now }) {
96213
+ const since = windowStart(period2, now);
96214
+ return db.select({
96215
+ projectId: sessionUsage.projectId,
96216
+ agentConfigId: sessionUsage.agentConfigId,
96217
+ // `max` e não `min`: dá no mesmo dentro de um grupo — o join é por id — e
96218
+ // é o mesmo truque que a moeda já usa para atravessar um `GROUP BY`.
96219
+ name: sql`max(${agentConfig.name})`,
96220
+ tokens: SUM.tokens,
96221
+ cost: SUM.cost,
96222
+ currency: SUM.currency,
96223
+ turns: SUM.turns
96224
+ }).from(sessionUsage).innerJoin(project, eq(project.id, sessionUsage.projectId)).leftJoin(agentConfig, eq(agentConfig.id, sessionUsage.agentConfigId)).where(and(eq(project.workspaceId, workspaceId), gte(sessionUsage.createdAt, since))).groupBy(sessionUsage.projectId, sessionUsage.agentConfigId).orderBy(sql`${SUM.tokens} desc`).all();
96225
+ }
96226
+ function usageByWorktreeAndAgent(db, { projectId, period: period2, now }) {
96227
+ const since = windowStart(period2, now);
96228
+ return db.select({
96229
+ worktreeId: sessionUsage.worktreeId,
96230
+ agentConfigId: sessionUsage.agentConfigId,
96231
+ name: sql`max(${agentConfig.name})`,
96232
+ tokens: SUM.tokens,
96233
+ cost: SUM.cost,
96234
+ currency: SUM.currency,
96235
+ turns: SUM.turns
96236
+ }).from(sessionUsage).leftJoin(agentConfig, eq(agentConfig.id, sessionUsage.agentConfigId)).where(and(eq(sessionUsage.projectId, projectId), gte(sessionUsage.createdAt, since))).groupBy(sessionUsage.worktreeId, sessionUsage.agentConfigId).orderBy(sql`${SUM.tokens} desc`).all();
96237
+ }
94729
96238
  function usageOutsideWorktrees(db, { projectId, period: period2, now }) {
94730
96239
  const since = windowStart(period2, now);
94731
96240
  const [row] = db.select({ tokens: SUM.tokens, cost: SUM.cost, currency: SUM.currency, turns: SUM.turns }).from(sessionUsage).where(
@@ -94762,7 +96271,21 @@ var usageRouter = router({
94762
96271
  projectId: input.projectId,
94763
96272
  period: input.period
94764
96273
  })
94765
- }))
96274
+ })),
96275
+ /*
96276
+ * O mesmo consumo por agente (`second-agent`, F5).
96277
+ *
96278
+ * Procedimentos separados, e não um `groupBy` nos dois de cima: a resposta
96279
+ * agrupada tem uma linha por par, e enfiá-la na mesma chamada mudaria a forma
96280
+ * do que a tela do workspace já lê. A tela só pede isto quando há **mais de um
96281
+ * agente** — com um, a coluna não existe e a chamada não acontece (C5).
96282
+ */
96283
+ byProjectAndAgent: publicProcedure.input(external_exports.object({ workspaceId: external_exports.string().min(1), period })).query(
96284
+ ({ ctx, input }) => usageByProjectAndAgent(ctx.db, { workspaceId: input.workspaceId, period: input.period })
96285
+ ),
96286
+ byWorktreeAndAgent: publicProcedure.input(external_exports.object({ projectId: external_exports.string().min(1), period })).query(
96287
+ ({ ctx, input }) => usageByWorktreeAndAgent(ctx.db, { projectId: input.projectId, period: input.period })
96288
+ )
94766
96289
  });
94767
96290
 
94768
96291
  // src/routers/workspace.ts
@@ -94959,6 +96482,7 @@ var appRouter = router({
94959
96482
  events: eventsRouter,
94960
96483
  files: filesRouter,
94961
96484
  memory: memoryRouter,
96485
+ pr: prRouter,
94962
96486
  project: projectRouter,
94963
96487
  scripts: scriptsRouter,
94964
96488
  session: sessionRouter,
@@ -94989,6 +96513,14 @@ async function createServer2({
94989
96513
  }),
94990
96514
  git = createGitService(),
94991
96515
  clones = createCloneJobStore(),
96516
+ prHost = createGhHost(),
96517
+ pr = createPrCache({
96518
+ host: prHost,
96519
+ // F6.3: a barra não depende só do relógio dela. É o que faz um merge feito
96520
+ // noutra aba aparecer nesta sem esperar o próximo ciclo.
96521
+ onChange: (projectId) => events.emit({ type: "pr.changed", projectId })
96522
+ }),
96523
+ agentAuth = createAgentAuthService({ acpManager }),
94992
96524
  logger = false
94993
96525
  }) {
94994
96526
  const app = (0, import_fastify2.default)({
@@ -95007,6 +96539,9 @@ async function createServer2({
95007
96539
  scripts,
95008
96540
  git,
95009
96541
  clones,
96542
+ pr,
96543
+ prHost,
96544
+ agentAuth,
95010
96545
  events
95011
96546
  });
95012
96547
  await app.register(fastifyTRPCPlugin, {
@@ -95142,6 +96677,7 @@ async function bootstrap({
95142
96677
  askUrl: `http://${config2.host}:${String(config2.port)}/memory/ask`
95143
96678
  })
95144
96679
  });
96680
+ const agentAuth = createAgentAuthService({ acpManager: acp });
95145
96681
  const sessionStore = createSessionStore({
95146
96682
  db: openedDatabase.db,
95147
96683
  ptyManager,
@@ -95204,6 +96740,7 @@ async function bootstrap({
95204
96740
  }
95205
96741
  }),
95206
96742
  events,
96743
+ agentAuth,
95207
96744
  logger
95208
96745
  });
95209
96746
  bootedApp = app;
@@ -95215,6 +96752,7 @@ async function bootstrap({
95215
96752
  stopUsageTracking();
95216
96753
  await ptyManager.killAll();
95217
96754
  await acp.killAll();
96755
+ agentAuth.cancelAll();
95218
96756
  if (beforeClose) await beforeClose();
95219
96757
  await app.close();
95220
96758
  if (owned) openedDatabase.close();
@@ -95253,7 +96791,7 @@ async function bootstrap({
95253
96791
 
95254
96792
  // src/config.ts
95255
96793
  import { homedir as homedir2 } from "node:os";
95256
- import { isAbsolute as isAbsolute6, join as join19, resolve as resolve4 } from "node:path";
96794
+ import { isAbsolute as isAbsolute7, join as join20, resolve as resolve5 } from "node:path";
95257
96795
  function readPort(raw) {
95258
96796
  if (raw === void 0 || raw === "") return DEFAULT_SERVER_PORT;
95259
96797
  const trimmed2 = raw.trim();
@@ -95267,22 +96805,22 @@ function readPort(raw) {
95267
96805
  return parsed;
95268
96806
  }
95269
96807
  function absoluteDir(raw) {
95270
- const expanded = raw === "~" || raw.startsWith("~/") ? join19(homedir2(), raw.slice(1)) : raw;
95271
- return isAbsolute6(expanded) ? join19(expanded) : resolve4(expanded);
96808
+ const expanded = raw === "~" || raw.startsWith("~/") ? join20(homedir2(), raw.slice(1)) : raw;
96809
+ return isAbsolute7(expanded) ? join20(expanded) : resolve5(expanded);
95272
96810
  }
95273
96811
  function readBudget(raw) {
95274
96812
  if (raw === void 0 || !/^\d+$/.test(raw.trim())) return 3;
95275
96813
  return Number.parseInt(raw.trim(), 10);
95276
96814
  }
95277
96815
  function loadConfig(env = process.env) {
95278
- const stateDir = absoluteDir(env.LUMEM_STATE_DIR ?? join19(homedir2(), ".lumem"));
96816
+ const stateDir = absoluteDir(env.LUMEM_STATE_DIR ?? join20(homedir2(), ".lumem"));
95279
96817
  return {
95280
96818
  port: readPort(env.LUMEM_PORT),
95281
96819
  host: env.LUMEM_HOST ?? "127.0.0.1",
95282
96820
  stateDir,
95283
- databasePath: env.LUMEM_DB_PATH ?? join19(stateDir, "lumem.db"),
95284
- workspacesDir: join19(stateDir, "workspaces"),
95285
- transcriptsDir: join19(stateDir, "transcripts"),
96821
+ databasePath: env.LUMEM_DB_PATH ?? join20(stateDir, "lumem.db"),
96822
+ workspacesDir: join20(stateDir, "workspaces"),
96823
+ transcriptsDir: join20(stateDir, "transcripts"),
95286
96824
  // /bin/sh exists on every POSIX system this daemon can run on; SHELL is
95287
96825
  // unset under launchd and in some containers.
95288
96826
  shell: env.SHELL === void 0 || env.SHELL === "" ? "/bin/sh" : env.SHELL,