codex-to-im 0.1.2 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daemon.mjs CHANGED
@@ -9999,9 +9999,9 @@ var require_form_data = __commonJS({
9999
9999
  var http = __require("http");
10000
10000
  var https = __require("https");
10001
10001
  var parseUrl = __require("url").parse;
10002
- var fs10 = __require("fs");
10002
+ var fs11 = __require("fs");
10003
10003
  var Stream = __require("stream").Stream;
10004
- var crypto10 = __require("crypto");
10004
+ var crypto11 = __require("crypto");
10005
10005
  var mime = require_mime_types();
10006
10006
  var asynckit = require_asynckit();
10007
10007
  var setToStringTag = require_es_set_tostringtag();
@@ -10066,7 +10066,7 @@ var require_form_data = __commonJS({
10066
10066
  if (value.end != void 0 && value.end != Infinity && value.start != void 0) {
10067
10067
  callback(null, value.end + 1 - (value.start ? value.start : 0));
10068
10068
  } else {
10069
- fs10.stat(value.path, function(err, stat) {
10069
+ fs11.stat(value.path, function(err, stat) {
10070
10070
  if (err) {
10071
10071
  callback(err);
10072
10072
  return;
@@ -10207,7 +10207,7 @@ var require_form_data = __commonJS({
10207
10207
  return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]);
10208
10208
  };
10209
10209
  FormData2.prototype._generateBoundary = function() {
10210
- this._boundary = "--------------------------" + crypto10.randomBytes(12).toString("hex");
10210
+ this._boundary = "--------------------------" + crypto11.randomBytes(12).toString("hex");
10211
10211
  };
10212
10212
  FormData2.prototype.getLengthSync = function() {
10213
10213
  var knownLength = this._overheadLength + this._valueLength;
@@ -10899,7 +10899,7 @@ var require_axios = __commonJS({
10899
10899
  "node_modules/axios/dist/node/axios.cjs"(exports2, module2) {
10900
10900
  "use strict";
10901
10901
  var FormData$1 = require_form_data();
10902
- var crypto10 = __require("crypto");
10902
+ var crypto11 = __require("crypto");
10903
10903
  var url = __require("url");
10904
10904
  var proxyFromEnv = require_proxy_from_env();
10905
10905
  var http = __require("http");
@@ -10914,7 +10914,7 @@ var require_axios = __commonJS({
10914
10914
  return e && typeof e === "object" && "default" in e ? e : { "default": e };
10915
10915
  }
10916
10916
  var FormData__default = /* @__PURE__ */ _interopDefaultLegacy(FormData$1);
10917
- var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto10);
10917
+ var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto11);
10918
10918
  var url__default = /* @__PURE__ */ _interopDefaultLegacy(url);
10919
10919
  var proxyFromEnv__default = /* @__PURE__ */ _interopDefaultLegacy(proxyFromEnv);
10920
10920
  var http__default = /* @__PURE__ */ _interopDefaultLegacy(http);
@@ -22889,11 +22889,11 @@ var require_lib2 = __commonJS({
22889
22889
  "use strict";
22890
22890
  Object.defineProperty(exports2, "__esModule", { value: true });
22891
22891
  var axios = require_axios();
22892
- var crypto10 = __require("crypto");
22892
+ var crypto11 = __require("crypto");
22893
22893
  var qs = require_lib();
22894
22894
  var identity = require_lodash();
22895
22895
  var pickBy = require_lodash2();
22896
- var fs10 = __require("fs");
22896
+ var fs11 = __require("fs");
22897
22897
  var merge = require_lodash3();
22898
22898
  var qs$1 = __require("querystring");
22899
22899
  var WebSocket2 = require_ws();
@@ -22901,10 +22901,10 @@ var require_lib2 = __commonJS({
22901
22901
  return e && typeof e === "object" && "default" in e ? e : { "default": e };
22902
22902
  }
22903
22903
  var axios__default = /* @__PURE__ */ _interopDefaultLegacy(axios);
22904
- var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto10);
22904
+ var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto11);
22905
22905
  var identity__default = /* @__PURE__ */ _interopDefaultLegacy(identity);
22906
22906
  var pickBy__default = /* @__PURE__ */ _interopDefaultLegacy(pickBy);
22907
- var fs__default = /* @__PURE__ */ _interopDefaultLegacy(fs10);
22907
+ var fs__default = /* @__PURE__ */ _interopDefaultLegacy(fs11);
22908
22908
  var merge__default = /* @__PURE__ */ _interopDefaultLegacy(merge);
22909
22909
  var qs__default = /* @__PURE__ */ _interopDefaultLegacy(qs$1);
22910
22910
  var WebSocket__default = /* @__PURE__ */ _interopDefaultLegacy(WebSocket2);
@@ -106854,11 +106854,11 @@ var require_util2 = __commonJS({
106854
106854
  var { isUint8Array } = __require("node:util/types");
106855
106855
  var { webidl } = require_webidl();
106856
106856
  var supportedHashes = [];
106857
- var crypto10;
106857
+ var crypto11;
106858
106858
  try {
106859
- crypto10 = __require("node:crypto");
106859
+ crypto11 = __require("node:crypto");
106860
106860
  const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
106861
- supportedHashes = crypto10.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
106861
+ supportedHashes = crypto11.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
106862
106862
  } catch {
106863
106863
  }
106864
106864
  function responseURL(response) {
@@ -107131,7 +107131,7 @@ var require_util2 = __commonJS({
107131
107131
  }
107132
107132
  }
107133
107133
  function bytesMatch(bytes, metadataList) {
107134
- if (crypto10 === void 0) {
107134
+ if (crypto11 === void 0) {
107135
107135
  return true;
107136
107136
  }
107137
107137
  const parsedMetadata = parseMetadata(metadataList);
@@ -107146,7 +107146,7 @@ var require_util2 = __commonJS({
107146
107146
  for (const item of metadata) {
107147
107147
  const algorithm = item.algo;
107148
107148
  const expectedValue = item.hash;
107149
- let actualValue = crypto10.createHash(algorithm).update(bytes).digest("base64");
107149
+ let actualValue = crypto11.createHash(algorithm).update(bytes).digest("base64");
107150
107150
  if (actualValue[actualValue.length - 1] === "=") {
107151
107151
  if (actualValue[actualValue.length - 2] === "=") {
107152
107152
  actualValue = actualValue.slice(0, -2);
@@ -108210,8 +108210,8 @@ var require_body = __commonJS({
108210
108210
  var { multipartFormDataParser } = require_formdata_parser();
108211
108211
  var random;
108212
108212
  try {
108213
- const crypto10 = __require("node:crypto");
108214
- random = (max) => crypto10.randomInt(0, max);
108213
+ const crypto11 = __require("node:crypto");
108214
+ random = (max) => crypto11.randomInt(0, max);
108215
108215
  } catch {
108216
108216
  random = (max) => Math.floor(Math.random(max));
108217
108217
  }
@@ -119619,13 +119619,13 @@ var require_frame = __commonJS({
119619
119619
  "use strict";
119620
119620
  var { maxUnsigned16Bit } = require_constants6();
119621
119621
  var BUFFER_SIZE = 16386;
119622
- var crypto10;
119622
+ var crypto11;
119623
119623
  var buffer = null;
119624
119624
  var bufIdx = BUFFER_SIZE;
119625
119625
  try {
119626
- crypto10 = __require("node:crypto");
119626
+ crypto11 = __require("node:crypto");
119627
119627
  } catch {
119628
- crypto10 = {
119628
+ crypto11 = {
119629
119629
  // not full compatibility, but minimum.
119630
119630
  randomFillSync: function randomFillSync(buffer2, _offset, _size) {
119631
119631
  for (let i = 0; i < buffer2.length; ++i) {
@@ -119638,7 +119638,7 @@ var require_frame = __commonJS({
119638
119638
  function generateMask() {
119639
119639
  if (bufIdx === BUFFER_SIZE) {
119640
119640
  bufIdx = 0;
119641
- crypto10.randomFillSync(buffer ??= Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE);
119641
+ crypto11.randomFillSync(buffer ??= Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE);
119642
119642
  }
119643
119643
  return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]];
119644
119644
  }
@@ -119710,9 +119710,9 @@ var require_connection = __commonJS({
119710
119710
  var { Headers, getHeadersList } = require_headers();
119711
119711
  var { getDecodeSplit } = require_util2();
119712
119712
  var { WebsocketFrameSend } = require_frame();
119713
- var crypto10;
119713
+ var crypto11;
119714
119714
  try {
119715
- crypto10 = __require("node:crypto");
119715
+ crypto11 = __require("node:crypto");
119716
119716
  } catch {
119717
119717
  }
119718
119718
  function establishWebSocketConnection(url, protocols, client, ws, onEstablish, options) {
@@ -119732,7 +119732,7 @@ var require_connection = __commonJS({
119732
119732
  const headersList = getHeadersList(new Headers(options.headers));
119733
119733
  request.headersList = headersList;
119734
119734
  }
119735
- const keyValue = crypto10.randomBytes(16).toString("base64");
119735
+ const keyValue = crypto11.randomBytes(16).toString("base64");
119736
119736
  request.headersList.append("sec-websocket-key", keyValue);
119737
119737
  request.headersList.append("sec-websocket-version", "13");
119738
119738
  for (const protocol of protocols) {
@@ -119762,7 +119762,7 @@ var require_connection = __commonJS({
119762
119762
  return;
119763
119763
  }
119764
119764
  const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
119765
- const digest = crypto10.createHash("sha1").update(keyValue + uid).digest("base64");
119765
+ const digest = crypto11.createHash("sha1").update(keyValue + uid).digest("base64");
119766
119766
  if (secWSAccept !== digest) {
119767
119767
  failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
119768
119768
  return;
@@ -136304,11 +136304,11 @@ var require_util10 = __commonJS({
136304
136304
  var { isUint8Array } = __require("node:util/types");
136305
136305
  var { webidl } = require_webidl2();
136306
136306
  var supportedHashes = [];
136307
- var crypto10;
136307
+ var crypto11;
136308
136308
  try {
136309
- crypto10 = __require("node:crypto");
136309
+ crypto11 = __require("node:crypto");
136310
136310
  const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
136311
- supportedHashes = crypto10.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
136311
+ supportedHashes = crypto11.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
136312
136312
  } catch {
136313
136313
  }
136314
136314
  function responseURL(response) {
@@ -136581,7 +136581,7 @@ var require_util10 = __commonJS({
136581
136581
  }
136582
136582
  }
136583
136583
  function bytesMatch(bytes, metadataList) {
136584
- if (crypto10 === void 0) {
136584
+ if (crypto11 === void 0) {
136585
136585
  return true;
136586
136586
  }
136587
136587
  const parsedMetadata = parseMetadata(metadataList);
@@ -136596,7 +136596,7 @@ var require_util10 = __commonJS({
136596
136596
  for (const item of metadata) {
136597
136597
  const algorithm = item.algo;
136598
136598
  const expectedValue = item.hash;
136599
- let actualValue = crypto10.createHash(algorithm).update(bytes).digest("base64");
136599
+ let actualValue = crypto11.createHash(algorithm).update(bytes).digest("base64");
136600
136600
  if (actualValue[actualValue.length - 1] === "=") {
136601
136601
  if (actualValue[actualValue.length - 2] === "=") {
136602
136602
  actualValue = actualValue.slice(0, -2);
@@ -137660,8 +137660,8 @@ var require_body2 = __commonJS({
137660
137660
  var { multipartFormDataParser } = require_formdata_parser2();
137661
137661
  var random;
137662
137662
  try {
137663
- const crypto10 = __require("node:crypto");
137664
- random = (max) => crypto10.randomInt(0, max);
137663
+ const crypto11 = __require("node:crypto");
137664
+ random = (max) => crypto11.randomInt(0, max);
137665
137665
  } catch {
137666
137666
  random = (max) => Math.floor(Math.random(max));
137667
137667
  }
@@ -148990,13 +148990,13 @@ var require_frame2 = __commonJS({
148990
148990
  "use strict";
148991
148991
  var { maxUnsigned16Bit } = require_constants11();
148992
148992
  var BUFFER_SIZE = 16386;
148993
- var crypto10;
148993
+ var crypto11;
148994
148994
  var buffer = null;
148995
148995
  var bufIdx = BUFFER_SIZE;
148996
148996
  try {
148997
- crypto10 = __require("node:crypto");
148997
+ crypto11 = __require("node:crypto");
148998
148998
  } catch {
148999
- crypto10 = {
148999
+ crypto11 = {
149000
149000
  // not full compatibility, but minimum.
149001
149001
  randomFillSync: function randomFillSync(buffer2, _offset, _size) {
149002
149002
  for (let i = 0; i < buffer2.length; ++i) {
@@ -149009,7 +149009,7 @@ var require_frame2 = __commonJS({
149009
149009
  function generateMask() {
149010
149010
  if (bufIdx === BUFFER_SIZE) {
149011
149011
  bufIdx = 0;
149012
- crypto10.randomFillSync(buffer ??= Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE);
149012
+ crypto11.randomFillSync(buffer ??= Buffer.allocUnsafe(BUFFER_SIZE), 0, BUFFER_SIZE);
149013
149013
  }
149014
149014
  return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]];
149015
149015
  }
@@ -149081,9 +149081,9 @@ var require_connection2 = __commonJS({
149081
149081
  var { Headers, getHeadersList } = require_headers2();
149082
149082
  var { getDecodeSplit } = require_util10();
149083
149083
  var { WebsocketFrameSend } = require_frame2();
149084
- var crypto10;
149084
+ var crypto11;
149085
149085
  try {
149086
- crypto10 = __require("node:crypto");
149086
+ crypto11 = __require("node:crypto");
149087
149087
  } catch {
149088
149088
  }
149089
149089
  function establishWebSocketConnection(url, protocols, client, ws, onEstablish, options) {
@@ -149103,7 +149103,7 @@ var require_connection2 = __commonJS({
149103
149103
  const headersList = getHeadersList(new Headers(options.headers));
149104
149104
  request.headersList = headersList;
149105
149105
  }
149106
- const keyValue = crypto10.randomBytes(16).toString("base64");
149106
+ const keyValue = crypto11.randomBytes(16).toString("base64");
149107
149107
  request.headersList.append("sec-websocket-key", keyValue);
149108
149108
  request.headersList.append("sec-websocket-version", "13");
149109
149109
  for (const protocol of protocols) {
@@ -149133,7 +149133,7 @@ var require_connection2 = __commonJS({
149133
149133
  return;
149134
149134
  }
149135
149135
  const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
149136
- const digest = crypto10.createHash("sha1").update(keyValue + uid).digest("base64");
149136
+ const digest = crypto11.createHash("sha1").update(keyValue + uid).digest("base64");
149137
149137
  if (secWSAccept !== digest) {
149138
149138
  failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
149139
149139
  return;
@@ -155919,7 +155919,7 @@ var require_DataResolver = __commonJS({
155919
155919
  "node_modules/discord.js/src/util/DataResolver.js"(exports2, module2) {
155920
155920
  "use strict";
155921
155921
  var { Buffer: Buffer2 } = __require("node:buffer");
155922
- var fs10 = __require("node:fs/promises");
155922
+ var fs11 = __require("node:fs/promises");
155923
155923
  var path12 = __require("node:path");
155924
155924
  var { fetch: fetch2 } = require_undici2();
155925
155925
  var { DiscordjsError: DiscordjsError2, DiscordjsTypeError: DiscordjsTypeError2, ErrorCodes: ErrorCodes2 } = require_errors2();
@@ -155947,9 +155947,9 @@ var require_DataResolver = __commonJS({
155947
155947
  return { data: Buffer2.from(await res.arrayBuffer()), contentType: res.headers.get("content-type") };
155948
155948
  }
155949
155949
  const file = path12.resolve(resource);
155950
- const stats = await fs10.stat(file);
155950
+ const stats = await fs11.stat(file);
155951
155951
  if (!stats.isFile()) throw new DiscordjsError2(ErrorCodes2.FileNotFound, file);
155952
- return { data: await fs10.readFile(file) };
155952
+ return { data: await fs11.readFile(file) };
155953
155953
  }
155954
155954
  throw new DiscordjsTypeError2(ErrorCodes2.ReqResourceType);
155955
155955
  }
@@ -191204,7 +191204,7 @@ var require_ShardingManager = __commonJS({
191204
191204
  "node_modules/discord.js/src/sharding/ShardingManager.js"(exports2, module2) {
191205
191205
  "use strict";
191206
191206
  var EventEmitter = __require("node:events");
191207
- var fs10 = __require("node:fs");
191207
+ var fs11 = __require("node:fs");
191208
191208
  var path12 = __require("node:path");
191209
191209
  var process2 = __require("node:process");
191210
191210
  var { setTimeout: sleep2 } = __require("node:timers/promises");
@@ -191251,7 +191251,7 @@ var require_ShardingManager = __commonJS({
191251
191251
  this.file = file;
191252
191252
  if (!file) throw new DiscordjsError2(ErrorCodes2.ClientInvalidOption, "File", "specified.");
191253
191253
  if (!path12.isAbsolute(file)) this.file = path12.resolve(process2.cwd(), file);
191254
- const stats = fs10.statSync(this.file);
191254
+ const stats = fs11.statSync(this.file);
191255
191255
  if (!stats.isFile()) throw new DiscordjsError2(ErrorCodes2.ClientInvalidOption, "File", "a file");
191256
191256
  this.shardList = _options.shardList ?? "auto";
191257
191257
  if (this.shardList !== "auto") {
@@ -192469,11 +192469,13 @@ var codex_provider_exports = {};
192469
192469
  __export(codex_provider_exports, {
192470
192470
  CodexProvider: () => CodexProvider
192471
192471
  });
192472
- import fs8 from "node:fs";
192472
+ import fs9 from "node:fs";
192473
192473
  import os3 from "node:os";
192474
192474
  import path10 from "node:path";
192475
192475
  function toApprovalPolicy(permissionMode) {
192476
192476
  switch (permissionMode) {
192477
+ case "never":
192478
+ return "never";
192477
192479
  case "acceptEdits":
192478
192480
  return "on-failure";
192479
192481
  case "plan":
@@ -192490,6 +192492,18 @@ function shouldPassModelToCodex() {
192490
192492
  function shouldSkipGitRepoCheck() {
192491
192493
  return process.env.CTI_CODEX_SKIP_GIT_REPO_CHECK === "true";
192492
192494
  }
192495
+ function normalizeSandboxMode(mode) {
192496
+ if (mode === "read-only" || mode === "workspace-write" || mode === "danger-full-access") {
192497
+ return mode;
192498
+ }
192499
+ return "workspace-write";
192500
+ }
192501
+ function normalizeReasoningEffort2(value) {
192502
+ if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
192503
+ return value;
192504
+ }
192505
+ return void 0;
192506
+ }
192493
192507
  function shouldRetryFreshThread(message) {
192494
192508
  const lower = message.toLowerCase();
192495
192509
  return lower.includes("resuming session with different model") || lower.includes("no such session") || lower.includes("resume") && lower.includes("session");
@@ -192549,10 +192563,14 @@ var init_codex_provider = __esm({
192549
192563
  let savedThreadId = inMemoryThreadId || params.sdkSessionId || void 0;
192550
192564
  const approvalPolicy = toApprovalPolicy(params.permissionMode);
192551
192565
  const passModel = shouldPassModelToCodex();
192566
+ const sandboxMode = normalizeSandboxMode(params.sandboxMode);
192567
+ const modelReasoningEffort = normalizeReasoningEffort2(params.modelReasoningEffort);
192552
192568
  const threadOptions = {
192553
192569
  ...passModel && params.model ? { model: params.model } : {},
192554
192570
  ...params.workingDirectory ? { workingDirectory: params.workingDirectory } : {},
192555
192571
  ...shouldSkipGitRepoCheck() ? { skipGitRepoCheck: true } : {},
192572
+ sandboxMode,
192573
+ ...modelReasoningEffort ? { modelReasoningEffort } : {},
192556
192574
  approvalPolicy
192557
192575
  };
192558
192576
  const imageFiles = params.files?.filter(
@@ -192564,13 +192582,13 @@ var init_codex_provider = __esm({
192564
192582
  { type: "text", text: params.prompt }
192565
192583
  ];
192566
192584
  for (const file of imageFiles) {
192567
- if (file.filePath && fs8.existsSync(file.filePath)) {
192585
+ if (file.filePath && fs9.existsSync(file.filePath)) {
192568
192586
  parts.push({ type: "local_image", path: file.filePath });
192569
192587
  continue;
192570
192588
  }
192571
192589
  const ext = MIME_EXT[file.type] || ".png";
192572
192590
  const tmpPath = path10.join(os3.tmpdir(), `cti-img-${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`);
192573
- fs8.writeFileSync(tmpPath, Buffer.from(file.data, "base64"));
192591
+ fs9.writeFileSync(tmpPath, Buffer.from(file.data, "base64"));
192574
192592
  tempFiles.push(tmpPath);
192575
192593
  parts.push({ type: "local_image", path: tmpPath });
192576
192594
  }
@@ -192661,7 +192679,7 @@ var init_codex_provider = __esm({
192661
192679
  } finally {
192662
192680
  for (const tmp of tempFiles) {
192663
192681
  try {
192664
- fs8.unlinkSync(tmp);
192682
+ fs9.unlinkSync(tmp);
192665
192683
  } catch {
192666
192684
  }
192667
192685
  }
@@ -192753,9 +192771,9 @@ var init_codex_provider = __esm({
192753
192771
  });
192754
192772
 
192755
192773
  // src/main.ts
192756
- import fs9 from "node:fs";
192774
+ import fs10 from "node:fs";
192757
192775
  import path11 from "node:path";
192758
- import crypto9 from "node:crypto";
192776
+ import crypto10 from "node:crypto";
192759
192777
 
192760
192778
  // src/lib/bridge/context.ts
192761
192779
  var CONTEXT_KEY = "__bridge_context__";
@@ -192794,6 +192812,7 @@ function getRegisteredTypes() {
192794
192812
  }
192795
192813
 
192796
192814
  // src/lib/bridge/bridge-manager.ts
192815
+ import fs5 from "node:fs";
192797
192816
  import path7 from "node:path";
192798
192817
 
192799
192818
  // src/lib/bridge/adapters/telegram-adapter.ts
@@ -194740,20 +194759,20 @@ var FeishuAdapter = class extends BaseChannelAdapter {
194740
194759
  buffer = Buffer.concat(chunks);
194741
194760
  } catch (streamErr) {
194742
194761
  console.warn("[feishu-adapter] Stream read failed, falling back to writeFile:", streamErr instanceof Error ? streamErr.message : streamErr);
194743
- const fs10 = await import("fs");
194762
+ const fs11 = await import("fs");
194744
194763
  const os4 = await import("os");
194745
194764
  const path12 = await import("path");
194746
194765
  const tmpPath = path12.join(os4.tmpdir(), `feishu-dl-${crypto2.randomUUID()}`);
194747
194766
  try {
194748
194767
  await res.writeFile(tmpPath);
194749
- buffer = fs10.readFileSync(tmpPath);
194768
+ buffer = fs11.readFileSync(tmpPath);
194750
194769
  if (buffer.length > MAX_FILE_SIZE) {
194751
194770
  console.warn(`[feishu-adapter] Resource too large (>${MAX_FILE_SIZE} bytes), key: ${fileKey}`);
194752
194771
  return null;
194753
194772
  }
194754
194773
  } finally {
194755
194774
  try {
194756
- fs10.unlinkSync(tmpPath);
194775
+ fs11.unlinkSync(tmpPath);
194757
194776
  } catch {
194758
194777
  }
194759
194778
  }
@@ -195797,6 +195816,7 @@ import os from "node:os";
195797
195816
  import path from "node:path";
195798
195817
  var LEGACY_CTI_HOME = path.join(os.homedir(), ".claude-to-im");
195799
195818
  var DEFAULT_CTI_HOME = path.join(os.homedir(), ".codex-to-im");
195819
+ var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
195800
195820
  function resolveDefaultCtiHome() {
195801
195821
  if (fs.existsSync(DEFAULT_CTI_HOME)) return DEFAULT_CTI_HOME;
195802
195822
  if (fs.existsSync(LEGACY_CTI_HOME)) return LEGACY_CTI_HOME;
@@ -195804,6 +195824,14 @@ function resolveDefaultCtiHome() {
195804
195824
  }
195805
195825
  var CTI_HOME = process.env.CTI_HOME || resolveDefaultCtiHome();
195806
195826
  var CONFIG_PATH = path.join(CTI_HOME, "config.env");
195827
+ function expandHomePath(value) {
195828
+ if (!value) return value;
195829
+ if (value === "~") return os.homedir();
195830
+ if (value.startsWith("~/") || value.startsWith("~\\")) {
195831
+ return path.join(os.homedir(), value.slice(2));
195832
+ }
195833
+ return value;
195834
+ }
195807
195835
  function parseEnvFile(content) {
195808
195836
  const entries = /* @__PURE__ */ new Map();
195809
195837
  for (const line of content.split("\n")) {
@@ -195837,6 +195865,18 @@ function parsePositiveInt(value) {
195837
195865
  if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
195838
195866
  return Math.floor(parsed);
195839
195867
  }
195868
+ function parseSandboxMode(value) {
195869
+ if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") {
195870
+ return value;
195871
+ }
195872
+ return void 0;
195873
+ }
195874
+ function parseReasoningEffort(value) {
195875
+ if (value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh") {
195876
+ return value;
195877
+ }
195878
+ return void 0;
195879
+ }
195840
195880
  function loadConfig() {
195841
195881
  const env = loadRawConfigEnv();
195842
195882
  const rawRuntime = env.get("CTI_RUNTIME") || "codex";
@@ -195844,11 +195884,16 @@ function loadConfig() {
195844
195884
  return {
195845
195885
  runtime,
195846
195886
  enabledChannels: splitCsv(env.get("CTI_ENABLED_CHANNELS")) ?? ["feishu"],
195847
- defaultWorkDir: env.get("CTI_DEFAULT_WORKDIR") || process.cwd(),
195887
+ defaultWorkDir: expandHomePath(env.get("CTI_DEFAULT_WORKDIR")) || process.cwd(),
195888
+ defaultWorkspaceRoot: expandHomePath(env.get("CTI_DEFAULT_WORKSPACE_ROOT")) || DEFAULT_WORKSPACE_ROOT,
195848
195889
  defaultModel: env.get("CTI_DEFAULT_MODEL") || void 0,
195849
195890
  defaultMode: env.get("CTI_DEFAULT_MODE") || "code",
195850
195891
  historyMessageLimit: parsePositiveInt(env.get("CTI_HISTORY_MESSAGE_LIMIT")) ?? 8,
195851
195892
  codexSkipGitRepoCheck: env.has("CTI_CODEX_SKIP_GIT_REPO_CHECK") ? env.get("CTI_CODEX_SKIP_GIT_REPO_CHECK") === "true" : true,
195893
+ codexSandboxMode: parseSandboxMode(env.get("CTI_CODEX_SANDBOX_MODE")) ?? "workspace-write",
195894
+ codexReasoningEffort: parseReasoningEffort(env.get("CTI_CODEX_REASONING_EFFORT")) ?? "medium",
195895
+ uiAllowLan: env.get("CTI_UI_ALLOW_LAN") === "true",
195896
+ uiAccessToken: env.get("CTI_UI_ACCESS_TOKEN") || void 0,
195852
195897
  tgBotToken: env.get("CTI_TG_BOT_TOKEN") || void 0,
195853
195898
  tgChatId: env.get("CTI_TG_CHAT_ID") || void 0,
195854
195899
  tgAllowedUsers: splitCsv(env.get("CTI_TG_ALLOWED_USERS")),
@@ -195857,6 +195902,7 @@ function loadConfig() {
195857
195902
  feishuDomain: env.get("CTI_FEISHU_DOMAIN") || void 0,
195858
195903
  feishuAllowedUsers: splitCsv(env.get("CTI_FEISHU_ALLOWED_USERS")),
195859
195904
  feishuStreamingEnabled: env.has("CTI_FEISHU_STREAMING_ENABLED") ? env.get("CTI_FEISHU_STREAMING_ENABLED") === "true" : true,
195905
+ feishuCommandMarkdownEnabled: env.has("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_FEISHU_COMMAND_MARKDOWN_ENABLED") === "true" : true,
195860
195906
  discordBotToken: env.get("CTI_DISCORD_BOT_TOKEN") || void 0,
195861
195907
  discordAllowedUsers: splitCsv(env.get("CTI_DISCORD_ALLOWED_USERS")),
195862
195908
  discordAllowedChannels: splitCsv(
@@ -195871,6 +195917,7 @@ function loadConfig() {
195871
195917
  weixinBaseUrl: env.get("CTI_WEIXIN_BASE_URL") || void 0,
195872
195918
  weixinCdnBaseUrl: env.get("CTI_WEIXIN_CDN_BASE_URL") || void 0,
195873
195919
  weixinMediaEnabled: env.has("CTI_WEIXIN_MEDIA_ENABLED") ? env.get("CTI_WEIXIN_MEDIA_ENABLED") === "true" : void 0,
195920
+ weixinCommandMarkdownEnabled: env.has("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") ? env.get("CTI_WEIXIN_COMMAND_MARKDOWN_ENABLED") === "true" : false,
195874
195921
  autoApprove: env.get("CTI_AUTO_APPROVE") === "true"
195875
195922
  };
195876
195923
  }
@@ -195917,6 +195964,10 @@ function configToSettings(config2) {
195917
195964
  "bridge_feishu_streaming_enabled",
195918
195965
  config2.feishuStreamingEnabled === false ? "false" : "true"
195919
195966
  );
195967
+ m.set(
195968
+ "bridge_feishu_command_markdown_enabled",
195969
+ config2.feishuCommandMarkdownEnabled === false ? "false" : "true"
195970
+ );
195920
195971
  m.set(
195921
195972
  "bridge_qq_enabled",
195922
195973
  config2.enabledChannels.includes("qq") ? "true" : "false"
@@ -195935,11 +195986,18 @@ function configToSettings(config2) {
195935
195986
  );
195936
195987
  if (config2.weixinMediaEnabled !== void 0)
195937
195988
  m.set("bridge_weixin_media_enabled", String(config2.weixinMediaEnabled));
195989
+ m.set(
195990
+ "bridge_weixin_command_markdown_enabled",
195991
+ config2.weixinCommandMarkdownEnabled === true ? "true" : "false"
195992
+ );
195938
195993
  if (config2.weixinBaseUrl)
195939
195994
  m.set("bridge_weixin_base_url", config2.weixinBaseUrl);
195940
195995
  if (config2.weixinCdnBaseUrl)
195941
195996
  m.set("bridge_weixin_cdn_base_url", config2.weixinCdnBaseUrl);
195942
195997
  m.set("bridge_default_work_dir", config2.defaultWorkDir);
195998
+ if (config2.defaultWorkspaceRoot) {
195999
+ m.set("bridge_default_workspace_root", config2.defaultWorkspaceRoot);
196000
+ }
195943
196001
  if (config2.defaultModel) {
195944
196002
  m.set("bridge_default_model", config2.defaultModel);
195945
196003
  m.set("default_model", config2.defaultModel);
@@ -195953,6 +196011,14 @@ function configToSettings(config2) {
195953
196011
  "bridge_codex_skip_git_repo_check",
195954
196012
  config2.codexSkipGitRepoCheck === true ? "true" : "false"
195955
196013
  );
196014
+ m.set(
196015
+ "bridge_codex_sandbox_mode",
196016
+ config2.codexSandboxMode || "workspace-write"
196017
+ );
196018
+ m.set(
196019
+ "bridge_codex_reasoning_effort",
196020
+ config2.codexReasoningEffort || "medium"
196021
+ );
195956
196022
  return m;
195957
196023
  }
195958
196024
 
@@ -196773,8 +196839,10 @@ import path4 from "node:path";
196773
196839
  import fs3 from "node:fs";
196774
196840
  import os2 from "node:os";
196775
196841
  import path3 from "node:path";
196842
+ import crypto7 from "node:crypto";
196776
196843
  var ACTIVE_WINDOW_MS = 15 * 60 * 1e3;
196777
196844
  var MAX_SESSION_META_BYTES = 4 * 1024 * 1024;
196845
+ var MAX_SESSION_TITLE_SCAN_BYTES = 512 * 1024;
196778
196846
  var TITLE_MAX_CHARS = 72;
196779
196847
  function getCodexHome() {
196780
196848
  return process.env.CODEX_HOME || path3.join(os2.homedir(), ".codex");
@@ -196832,6 +196900,24 @@ function readFirstLine(filePath, maxBytes = MAX_SESSION_META_BYTES) {
196832
196900
  fs3.closeSync(fd);
196833
196901
  }
196834
196902
  }
196903
+ function readFilePrefix(filePath, maxBytes = MAX_SESSION_TITLE_SCAN_BYTES) {
196904
+ const fd = fs3.openSync(filePath, "r");
196905
+ try {
196906
+ const buffer = Buffer.alloc(Math.min(maxBytes, 64 * 1024));
196907
+ const chunks = [];
196908
+ let offset = 0;
196909
+ while (offset < maxBytes) {
196910
+ const bytesToRead = Math.min(buffer.length, maxBytes - offset);
196911
+ const bytesRead = fs3.readSync(fd, buffer, 0, bytesToRead, offset);
196912
+ if (bytesRead <= 0) break;
196913
+ chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
196914
+ offset += bytesRead;
196915
+ }
196916
+ return Buffer.concat(chunks).toString("utf-8");
196917
+ } finally {
196918
+ fs3.closeSync(fd);
196919
+ }
196920
+ }
196835
196921
  function walkSessionFiles(dirPath, target) {
196836
196922
  let entries;
196837
196923
  try {
@@ -196853,6 +196939,7 @@ function walkSessionFiles(dirPath, target) {
196853
196939
  function isDesktopLike(meta) {
196854
196940
  const originator = meta?.originator?.toLowerCase() || "";
196855
196941
  const source = meta?.source?.toLowerCase() || "";
196942
+ if (source === "exec") return false;
196856
196943
  return originator.includes("desktop") || source === "vscode" || source === "desktop";
196857
196944
  }
196858
196945
  function loadThreadNameIndex(archivedThreadIds) {
@@ -196884,6 +196971,27 @@ function loadThreadNameIndex(archivedThreadIds) {
196884
196971
  }
196885
196972
  return new Map(Array.from(titles.entries()).map(([threadId, entry]) => [threadId, entry.title]));
196886
196973
  }
196974
+ function buildFallbackTitle(threadId, filePath, cwd) {
196975
+ try {
196976
+ const content = readFilePrefix(filePath);
196977
+ for (const line of content.split(/\r?\n/)) {
196978
+ if (!line.trim()) continue;
196979
+ let parsed;
196980
+ try {
196981
+ parsed = JSON.parse(line);
196982
+ } catch {
196983
+ continue;
196984
+ }
196985
+ if (!isSessionEventLine(parsed) || parsed.payload?.type !== "user_message") continue;
196986
+ const firstUserMessage = trimTitle(normalizeFreeText(parsed.payload.message || ""));
196987
+ if (firstUserMessage) return firstUserMessage;
196988
+ }
196989
+ } catch {
196990
+ }
196991
+ const dirName = trimTitle(path3.basename(cwd || ""));
196992
+ if (dirName) return dirName;
196993
+ return `Session ${threadId.slice(0, 8)}`;
196994
+ }
196887
196995
  function parseDesktopSession(filePath, threadNames, archivedThreadIds) {
196888
196996
  const firstLine = readFirstLine(filePath);
196889
196997
  if (!firstLine) return null;
@@ -196909,8 +197017,7 @@ function parseDesktopSession(filePath, threadNames, archivedThreadIds) {
196909
197017
  const lastEventAt = stat.mtime.toISOString();
196910
197018
  const firstSeenAt = parsed.payload.timestamp || parsed.timestamp || stat.birthtime.toISOString();
196911
197019
  const threadId = parsed.payload.id;
196912
- const title = threadNames.get(threadId);
196913
- if (!title) return null;
197020
+ const title = threadNames.get(threadId) || buildFallbackTitle(threadId, filePath, cwd);
196914
197021
  return {
196915
197022
  threadId,
196916
197023
  filePath,
@@ -196933,6 +197040,28 @@ function trimTitle(text2) {
196933
197040
  function normalizeFreeText(text2) {
196934
197041
  return text2.replace(/\s+/g, " ").trim();
196935
197042
  }
197043
+ function createDesktopEventSignature(rawLine) {
197044
+ return crypto7.createHash("sha1").update(rawLine).digest("hex");
197045
+ }
197046
+ function readFileUtf8Range(filePath, startOffset, endOffset) {
197047
+ const safeStart = Math.max(0, startOffset);
197048
+ const safeEnd = Math.max(safeStart, endOffset);
197049
+ const bytesToRead = safeEnd - safeStart;
197050
+ if (bytesToRead <= 0) return "";
197051
+ const fd = fs3.openSync(filePath, "r");
197052
+ try {
197053
+ const buffer = Buffer.alloc(bytesToRead);
197054
+ let totalRead = 0;
197055
+ while (totalRead < bytesToRead) {
197056
+ const bytesRead = fs3.readSync(fd, buffer, totalRead, bytesToRead - totalRead, safeStart + totalRead);
197057
+ if (bytesRead <= 0) break;
197058
+ totalRead += bytesRead;
197059
+ }
197060
+ return buffer.subarray(0, totalRead).toString("utf-8");
197061
+ } finally {
197062
+ fs3.closeSync(fd);
197063
+ }
197064
+ }
196936
197065
  function isSessionEventLine(line) {
196937
197066
  return line.type === "event_msg";
196938
197067
  }
@@ -196944,7 +197073,6 @@ function listDesktopSessions(limit = 12) {
196944
197073
  if (!fs3.existsSync(root)) return [];
196945
197074
  const archivedThreadIds = loadArchivedThreadIds();
196946
197075
  const threadNames = loadThreadNameIndex(archivedThreadIds);
196947
- if (threadNames.size === 0) return [];
196948
197076
  const files = [];
196949
197077
  walkSessionFiles(root, files);
196950
197078
  const sessions = [];
@@ -196968,47 +197096,124 @@ ${text2}`;
196968
197096
  }
196969
197097
  return text2;
196970
197098
  }
196971
- function readDesktopSessionMessages(threadId, limit = 8) {
196972
- const session = getDesktopSessionByThreadId(threadId);
196973
- if (!session) return [];
196974
- let content = "";
196975
- try {
196976
- content = fs3.readFileSync(session.filePath, "utf-8");
196977
- } catch {
196978
- return [];
197099
+ function pushDesktopSessionEvent(events, parsed, rawLine) {
197100
+ if (isSessionEventLine(parsed) && parsed.payload?.type === "user_message") {
197101
+ const text2 = normalizeFreeText(parsed.payload.message || "");
197102
+ if (!text2) return;
197103
+ events.push({
197104
+ signature: createDesktopEventSignature(rawLine),
197105
+ role: "user",
197106
+ content: text2,
197107
+ timestamp: parsed.timestamp || ""
197108
+ });
197109
+ return;
196979
197110
  }
196980
- const messages = [];
196981
- for (const line of content.split(/\r?\n/)) {
196982
- if (!line.trim()) continue;
197111
+ if (isSessionEventLine(parsed) && parsed.payload?.type === "task_complete") {
197112
+ const text2 = normalizeFreeText(parsed.payload.last_agent_message || "");
197113
+ if (!text2) return;
197114
+ const lastEvent = events[events.length - 1];
197115
+ if (lastEvent?.role === "assistant" && lastEvent.content === text2) {
197116
+ return;
197117
+ }
197118
+ events.push({
197119
+ signature: createDesktopEventSignature(rawLine),
197120
+ role: "assistant",
197121
+ content: text2,
197122
+ timestamp: parsed.timestamp || ""
197123
+ });
197124
+ return;
197125
+ }
197126
+ if (isSessionMessageLine(parsed) && parsed.payload?.type === "message" && parsed.payload.role === "assistant") {
197127
+ const text2 = extractDesktopMessageText(parsed);
197128
+ if (!text2) return;
197129
+ events.push({
197130
+ signature: createDesktopEventSignature(rawLine),
197131
+ role: parsed.payload.phase === "commentary" ? "commentary" : "assistant",
197132
+ content: parsed.payload.phase === "commentary" ? text2.replace(/^\[commentary\]\n/, "") : text2,
197133
+ timestamp: parsed.timestamp || ""
197134
+ });
197135
+ }
197136
+ }
197137
+ function parseDesktopSessionEventText(content, leadingText = "", flushTrailingText = false) {
197138
+ const combined = `${leadingText}${content}`;
197139
+ if (!combined) {
197140
+ return {
197141
+ events: [],
197142
+ nextOffset: 0,
197143
+ trailingText: ""
197144
+ };
197145
+ }
197146
+ const hasTrailingNewline = combined.endsWith("\n") || combined.endsWith("\r");
197147
+ const rawLines = combined.split(/\r?\n/);
197148
+ let trailingText = hasTrailingNewline ? "" : rawLines.pop() || "";
197149
+ if (flushTrailingText && trailingText) {
197150
+ rawLines.push(trailingText);
197151
+ trailingText = "";
197152
+ }
197153
+ const events = [];
197154
+ for (const line of rawLines) {
197155
+ const trimmed = line.trim();
197156
+ if (!trimmed) continue;
196983
197157
  let parsed;
196984
197158
  try {
196985
- parsed = JSON.parse(line);
197159
+ parsed = JSON.parse(trimmed);
196986
197160
  } catch {
196987
197161
  continue;
196988
197162
  }
196989
- if (isSessionEventLine(parsed) && parsed.payload?.type === "user_message") {
196990
- const text2 = normalizeFreeText(parsed.payload.message || "");
196991
- if (!text2) continue;
196992
- messages.push({
196993
- role: "user",
196994
- content: text2
196995
- });
196996
- continue;
196997
- }
196998
- if (isSessionMessageLine(parsed) && parsed.payload?.type === "message" && parsed.payload.role === "assistant") {
196999
- const text2 = extractDesktopMessageText(parsed);
197000
- if (!text2) continue;
197001
- messages.push({
197002
- role: parsed.payload.role,
197003
- content: text2
197004
- });
197005
- }
197163
+ pushDesktopSessionEvent(events, parsed, trimmed);
197006
197164
  }
197165
+ return {
197166
+ events,
197167
+ nextOffset: 0,
197168
+ trailingText
197169
+ };
197170
+ }
197171
+ function readDesktopSessionMessages(threadId, limit = 8) {
197172
+ const messages = readDesktopSessionEventStream(threadId).map((event) => ({
197173
+ role: event.role === "commentary" ? "assistant" : event.role,
197174
+ content: event.role === "commentary" ? `[commentary]
197175
+ ${event.content}` : event.content
197176
+ }));
197007
197177
  const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 8;
197008
197178
  return messages.slice(-safeLimit);
197009
197179
  }
197180
+ function readDesktopSessionEventStreamByFilePath(filePath) {
197181
+ let content = "";
197182
+ try {
197183
+ content = fs3.readFileSync(filePath, "utf-8");
197184
+ } catch {
197185
+ return [];
197186
+ }
197187
+ return parseDesktopSessionEventText(content, "", true).events;
197188
+ }
197189
+ function readDesktopSessionEventDeltaByFilePath(filePath, startOffset, endOffset, trailingText = "") {
197190
+ let content = "";
197191
+ try {
197192
+ content = readFileUtf8Range(filePath, startOffset, endOffset);
197193
+ } catch {
197194
+ return {
197195
+ events: [],
197196
+ nextOffset: startOffset,
197197
+ trailingText
197198
+ };
197199
+ }
197200
+ const parsed = parseDesktopSessionEventText(content, trailingText);
197201
+ return {
197202
+ events: parsed.events,
197203
+ nextOffset: Math.max(startOffset, endOffset),
197204
+ trailingText: parsed.trailingText
197205
+ };
197206
+ }
197207
+ function readDesktopSessionEventStream(threadId) {
197208
+ const session = getDesktopSessionByThreadId(threadId);
197209
+ if (!session) return [];
197210
+ return readDesktopSessionEventStreamByFilePath(session.filePath);
197211
+ }
197010
197212
 
197011
197213
  // src/session-bindings.ts
197214
+ function getSessionMode(store, session) {
197215
+ return session.preferred_mode || store.getSetting("bridge_default_mode") || "code";
197216
+ }
197012
197217
  function bindStoreToSession(store, channelType, chatId, sessionId) {
197013
197218
  const session = store.getSession(sessionId);
197014
197219
  if (!session) return null;
@@ -197018,7 +197223,8 @@ function bindStoreToSession(store, channelType, chatId, sessionId) {
197018
197223
  codepilotSessionId: session.id,
197019
197224
  sdkSessionId: session.sdk_session_id || "",
197020
197225
  workingDirectory: session.working_directory,
197021
- model: session.model
197226
+ model: session.model,
197227
+ mode: getSessionMode(store, session)
197022
197228
  });
197023
197229
  }
197024
197230
  function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
@@ -197030,7 +197236,8 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
197030
197236
  codepilotSessionId: existing.id,
197031
197237
  sdkSessionId,
197032
197238
  workingDirectory: opts?.workingDirectory || existing.working_directory,
197033
- model: opts?.model || existing.model
197239
+ model: opts?.model || existing.model,
197240
+ mode: getSessionMode(store, existing)
197034
197241
  });
197035
197242
  }
197036
197243
  const workingDirectory = opts?.workingDirectory || store.getSetting("bridge_default_work_dir") || process.env.HOME || "";
@@ -197050,7 +197257,8 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
197050
197257
  codepilotSessionId: session.id,
197051
197258
  sdkSessionId,
197052
197259
  workingDirectory: workingDirectory || session.working_directory,
197053
- model: model || session.model
197260
+ model: model || session.model,
197261
+ mode: getSessionMode(store, session)
197054
197262
  });
197055
197263
  }
197056
197264
 
@@ -197088,7 +197296,7 @@ function createBinding(address, workingDirectory) {
197088
197296
  sdkSessionId: "",
197089
197297
  workingDirectory: defaultCwd,
197090
197298
  model: defaultModel,
197091
- mode: "code"
197299
+ mode: session.preferred_mode || "code"
197092
197300
  });
197093
197301
  }
197094
197302
  function bindToSession(address, codepilotSessionId) {
@@ -197104,7 +197312,21 @@ function updateBinding(id, updates) {
197104
197312
  // src/lib/bridge/conversation-engine.ts
197105
197313
  import fs4 from "fs";
197106
197314
  import path5 from "path";
197107
- import crypto7 from "crypto";
197315
+ import crypto8 from "crypto";
197316
+ function resolveSandboxMode(store) {
197317
+ const configured = store.getSetting("bridge_codex_sandbox_mode");
197318
+ if (configured === "read-only" || configured === "workspace-write" || configured === "danger-full-access") {
197319
+ return configured;
197320
+ }
197321
+ return "workspace-write";
197322
+ }
197323
+ function resolveReasoningEffort(store, session) {
197324
+ const configured = session?.reasoning_effort || store.getSetting("bridge_codex_reasoning_effort");
197325
+ if (configured === "minimal" || configured === "low" || configured === "medium" || configured === "high" || configured === "xhigh") {
197326
+ return configured;
197327
+ }
197328
+ return "medium";
197329
+ }
197108
197330
  function formatAttachmentSize(size) {
197109
197331
  if (!Number.isFinite(size) || size < 1024) return `${size} B`;
197110
197332
  if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
@@ -197135,7 +197357,7 @@ function buildLocalAttachmentPromptSupplement(files) {
197135
197357
  async function processMessage(binding, text2, onPermissionRequest, abortSignal, files, onPartialText, onToolEvent) {
197136
197358
  const { store, llm } = getBridgeContext();
197137
197359
  const sessionId = binding.codepilotSessionId;
197138
- const lockId = crypto7.randomBytes(8).toString("hex");
197360
+ const lockId = crypto8.randomBytes(8).toString("hex");
197139
197361
  const lockAcquired = store.acquireSessionLock(sessionId, lockId, `bridge-${binding.channelType}`, 600);
197140
197362
  if (!lockAcquired) {
197141
197363
  return {
@@ -197157,6 +197379,8 @@ async function processMessage(binding, text2, onPermissionRequest, abortSignal,
197157
197379
  try {
197158
197380
  const session = store.getSession(sessionId);
197159
197381
  const workDir = binding.workingDirectory || session?.working_directory || "";
197382
+ const sandboxMode = resolveSandboxMode(store);
197383
+ const modelReasoningEffort = resolveReasoningEffort(store, session);
197160
197384
  let savedContent = text2;
197161
197385
  let llmFiles = files;
197162
197386
  let persistedFileMeta = [];
@@ -197233,6 +197457,8 @@ ${attachmentSupplement}` : attachmentSupplement : text2;
197233
197457
  sessionId,
197234
197458
  sdkSessionId: binding.sdkSessionId || void 0,
197235
197459
  model: effectiveModel,
197460
+ sandboxMode,
197461
+ modelReasoningEffort,
197236
197462
  systemPrompt: session?.system_prompt || void 0,
197237
197463
  workingDirectory: workDir || void 0,
197238
197464
  abortController,
@@ -197851,6 +198077,7 @@ async function forwardPermissionRequest(adapter, address, permissionRequestId, t
197851
198077
  channelType: adapter.channelType,
197852
198078
  chatId: address.chatId,
197853
198079
  messageId: result.messageId,
198080
+ sessionId,
197854
198081
  toolName,
197855
198082
  suggestions: suggestions ? JSON.stringify(suggestions) : ""
197856
198083
  });
@@ -204152,6 +204379,112 @@ function markdownToDiscordChunks(markdown, limit = 2e3) {
204152
204379
  return result;
204153
204380
  }
204154
204381
 
204382
+ // src/desktop-session-mirror.ts
204383
+ function makeCursor(events) {
204384
+ const lastEvent = events.length > 0 ? events[events.length - 1] : void 0;
204385
+ return {
204386
+ initialized: true,
204387
+ lastEventSignature: lastEvent?.signature,
204388
+ lastEventTimestamp: lastEvent?.timestamp,
204389
+ lastEventRole: lastEvent?.role,
204390
+ lastEventContent: lastEvent?.content,
204391
+ lastEventCount: events.length
204392
+ };
204393
+ }
204394
+ function advanceDesktopMirrorCursor(cursor, appendedEvents) {
204395
+ if (appendedEvents.length === 0) {
204396
+ return cursor ? { ...cursor } : {
204397
+ initialized: false,
204398
+ lastEventCount: 0
204399
+ };
204400
+ }
204401
+ if (!cursor?.initialized) {
204402
+ return makeCursor(appendedEvents);
204403
+ }
204404
+ const lastEvent = appendedEvents[appendedEvents.length - 1];
204405
+ return {
204406
+ initialized: true,
204407
+ lastEventSignature: lastEvent?.signature,
204408
+ lastEventTimestamp: lastEvent?.timestamp,
204409
+ lastEventRole: lastEvent?.role,
204410
+ lastEventContent: lastEvent?.content,
204411
+ lastEventCount: cursor.lastEventCount + appendedEvents.length
204412
+ };
204413
+ }
204414
+ function filterDuplicateAssistantEvents(cursor, events) {
204415
+ if (events.length === 0) return events;
204416
+ let startIndex = 0;
204417
+ while (startIndex < events.length && cursor?.lastEventRole === "assistant" && events[startIndex]?.role === "assistant" && cursor.lastEventContent === events[startIndex]?.content) {
204418
+ startIndex += 1;
204419
+ }
204420
+ return startIndex === 0 ? events : events.slice(startIndex);
204421
+ }
204422
+ function findLastEventIndex(events, signature) {
204423
+ for (let index = events.length - 1; index >= 0; index -= 1) {
204424
+ if (events[index]?.signature === signature) {
204425
+ return index;
204426
+ }
204427
+ }
204428
+ return -1;
204429
+ }
204430
+ function collectEventsAfterTimestamp(events, timestamp) {
204431
+ if (!timestamp) return [];
204432
+ return events.filter((event) => Boolean(event.timestamp) && event.timestamp > timestamp);
204433
+ }
204434
+ function reconcileDesktopMirrorCursor(cursor, events) {
204435
+ const nextCursor = makeCursor(events);
204436
+ if (!cursor?.initialized) {
204437
+ return {
204438
+ nextCursor,
204439
+ deliverableEvents: [],
204440
+ reset: false
204441
+ };
204442
+ }
204443
+ if (events.length === 0) {
204444
+ return {
204445
+ nextCursor,
204446
+ deliverableEvents: [],
204447
+ reset: cursor.lastEventCount > 0
204448
+ };
204449
+ }
204450
+ if (cursor.lastEventSignature) {
204451
+ const lastSeenIndex = findLastEventIndex(events, cursor.lastEventSignature);
204452
+ if (lastSeenIndex === -1) {
204453
+ const recoveredEvents = collectEventsAfterTimestamp(events, cursor.lastEventTimestamp);
204454
+ return {
204455
+ nextCursor,
204456
+ deliverableEvents: recoveredEvents,
204457
+ reset: true
204458
+ };
204459
+ }
204460
+ return {
204461
+ nextCursor,
204462
+ deliverableEvents: events.slice(lastSeenIndex + 1),
204463
+ reset: false
204464
+ };
204465
+ }
204466
+ if (cursor.lastEventCount === 0) {
204467
+ return {
204468
+ nextCursor,
204469
+ deliverableEvents: events,
204470
+ reset: false
204471
+ };
204472
+ }
204473
+ if (events.length < cursor.lastEventCount) {
204474
+ const recoveredEvents = collectEventsAfterTimestamp(events, cursor.lastEventTimestamp);
204475
+ return {
204476
+ nextCursor,
204477
+ deliverableEvents: recoveredEvents,
204478
+ reset: true
204479
+ };
204480
+ }
204481
+ return {
204482
+ nextCursor,
204483
+ deliverableEvents: events.slice(cursor.lastEventCount),
204484
+ reset: false
204485
+ };
204486
+ }
204487
+
204155
204488
  // src/lib/bridge/security/validators.ts
204156
204489
  import * as path6 from "path";
204157
204490
  var MAX_INPUT_LENGTH = 32e3;
@@ -204212,6 +204545,19 @@ function validateMode(mode) {
204212
204545
 
204213
204546
  // src/lib/bridge/bridge-manager.ts
204214
204547
  var GLOBAL_KEY = "__bridge_manager__";
204548
+ var DRAFT_TTL_MS = 24 * 60 * 60 * 1e3;
204549
+ var HISTORY_SUMMARY_TTL_MS = 24 * 60 * 60 * 1e3;
204550
+ var MAX_HIDDEN_DRAFT_SESSIONS = 20;
204551
+ var INTERNAL_SESSION_ROOT = path7.join(CTI_HOME, "runtime", "internal-sessions");
204552
+ var DRAFT_SESSION_PREFIX = "Draft";
204553
+ var HISTORY_SESSION_PREFIX = "History Summary";
204554
+ var REASONING_LEVELS = ["minimal", "low", "medium", "high", "xhigh"];
204555
+ var MODE_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`code`\uFF08\u76F4\u63A5\u6267\u884C\uFF0C\u9ED8\u8BA4\uFF09 `plan`\uFF08\u5148\u5206\u6790\u518D\u884C\u52A8\uFF09 `ask`\uFF08\u8F7B\u5BF9\u8BDD / \u8349\u7A3F\uFF09";
204556
+ var REASONING_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`1=minimal` `2=low` `3=medium` `4=high` `5=xhigh`";
204557
+ var MIRROR_POLL_INTERVAL_MS = 2500;
204558
+ var MIRROR_WATCH_DEBOUNCE_MS = 350;
204559
+ var MIRROR_EVENT_BATCH_LIMIT = 8;
204560
+ var MIRROR_SUPPRESSION_WINDOW_MS = 4e3;
204215
204561
  function generateDraftId() {
204216
204562
  return Math.floor(Math.random() * 2147483646) + 1;
204217
204563
  }
@@ -204235,6 +204581,28 @@ function parseListIndex(raw) {
204235
204581
  if (!Number.isInteger(parsed) || parsed < 1) return null;
204236
204582
  return parsed;
204237
204583
  }
204584
+ function resolveCommandAlias(rawCommand, args) {
204585
+ switch (rawCommand) {
204586
+ case "/":
204587
+ return "/status";
204588
+ case "/h":
204589
+ return "/help";
204590
+ case "/t":
204591
+ return args ? "/thread" : "/threads";
204592
+ case "/s":
204593
+ return args ? "/use" : "/sessions";
204594
+ case "/n":
204595
+ return "/new";
204596
+ case "/m":
204597
+ return "/mode";
204598
+ case "/r":
204599
+ return "/reasoning";
204600
+ case "/his":
204601
+ return "/history";
204602
+ default:
204603
+ return rawCommand;
204604
+ }
204605
+ }
204238
204606
  function resolveByIndexOrPrefix(raw, items, getId) {
204239
204607
  const token = raw.trim().toLowerCase();
204240
204608
  if (!token) return { match: null, ambiguous: false };
@@ -204258,7 +204626,7 @@ function getDisplayedDesktopThreads(limit = 10) {
204258
204626
  }
204259
204627
  function getDisplayedBridgeSessions(currentSessionId) {
204260
204628
  const { store } = getBridgeContext();
204261
- const sessions = store.listSessions().toReversed();
204629
+ const sessions = store.listSessions().filter((session) => session.hidden !== true).toReversed();
204262
204630
  return sessions.sort((a, b) => {
204263
204631
  if (a.id === currentSessionId && b.id !== currentSessionId) return -1;
204264
204632
  if (b.id === currentSessionId && a.id !== currentSessionId) return 1;
@@ -204275,38 +204643,368 @@ function getSessionDisplayName(session, fallbackDirectory) {
204275
204643
  if (session?.id) return session.id.slice(0, 8);
204276
204644
  return "\u672A\u547D\u540D\u4F1A\u8BDD";
204277
204645
  }
204278
- function getDesktopThreadTitle(threadId) {
204279
- if (!threadId) return null;
204280
- return getDesktopSessionByThreadId(threadId)?.title || null;
204646
+ function nowIso() {
204647
+ return (/* @__PURE__ */ new Date()).toISOString();
204281
204648
  }
204282
- function formatCommandMessageId(id) {
204283
- if (!id) return "\u672A\u5171\u4EAB";
204284
- return id;
204649
+ function ensureDirectory(dirPath) {
204650
+ fs5.mkdirSync(dirPath, { recursive: true });
204285
204651
  }
204286
- function formatCommandPath(cwd) {
204287
- return cwd?.trim() || "~";
204652
+ function getWorkspaceRoot() {
204653
+ const { store } = getBridgeContext();
204654
+ return store.getSetting("bridge_default_workspace_root") || DEFAULT_WORKSPACE_ROOT;
204655
+ }
204656
+ function normalizeReasoningEffort(raw) {
204657
+ const token = raw.trim().toLowerCase();
204658
+ if (!token) return null;
204659
+ if (REASONING_LEVELS.includes(token)) {
204660
+ return token;
204661
+ }
204662
+ switch (token) {
204663
+ case "1":
204664
+ return "minimal";
204665
+ case "2":
204666
+ return "low";
204667
+ case "3":
204668
+ return "medium";
204669
+ case "4":
204670
+ return "high";
204671
+ case "5":
204672
+ return "xhigh";
204673
+ default:
204674
+ return null;
204675
+ }
204676
+ }
204677
+ function formatReasoningEffort(reasoning) {
204678
+ switch (reasoning) {
204679
+ case "minimal":
204680
+ return "minimal (1)";
204681
+ case "low":
204682
+ return "low (2)";
204683
+ case "medium":
204684
+ return "medium (3)";
204685
+ case "high":
204686
+ return "high (4)";
204687
+ case "xhigh":
204688
+ return "xhigh (5)";
204689
+ default:
204690
+ return reasoning;
204691
+ }
204288
204692
  }
204289
- function buildDesktopSessionLines(session, index) {
204693
+ function buildCommandFields(title, fields, notes = [], markdown = false) {
204694
+ const normalizedFields = fields.filter(([, value]) => value !== null && value !== void 0 && String(value).trim() !== "");
204695
+ const normalizedNotes = notes.filter((note) => note.trim().length > 0);
204696
+ if (markdown) {
204697
+ const lines = [`**${title}**`, ""];
204698
+ for (const [label, value] of normalizedFields) {
204699
+ lines.push(`- **${label}**\uFF1A${value}`);
204700
+ }
204701
+ if (normalizedNotes.length > 0) {
204702
+ lines.push("", "**\u8BF4\u660E**");
204703
+ for (const note of normalizedNotes) {
204704
+ lines.push(`- ${note}`);
204705
+ }
204706
+ }
204707
+ return lines.join("\n").trim();
204708
+ }
204290
204709
  return [
204291
- `${index + 1}. ${session.title || "\u672A\u547D\u540D\u7EBF\u7A0B"}`,
204292
- ` Thread: ${session.threadId}`,
204293
- ` \u76EE\u5F55: ${formatCommandPath(session.cwd)}`,
204294
- ` \u6765\u6E90: ${session.originator || "Codex Desktop"}`
204295
- ];
204710
+ title,
204711
+ "",
204712
+ ...normalizedFields.map(([label, value]) => `${label}: ${value}`),
204713
+ ...normalizedNotes.length > 0 ? ["", ...normalizedNotes] : []
204714
+ ].join("\n").trim();
204296
204715
  }
204297
- function buildBridgeSessionLines(session, index, isCurrent) {
204298
- const title = getSessionDisplayName(session, session.working_directory);
204299
- const currentSuffix = isCurrent ? " [\u5F53\u524D]" : "";
204300
- const lines = [
204301
- `${index + 1}. ${title}${currentSuffix}`,
204302
- ` Session: ${session.id}`,
204303
- ` \u76EE\u5F55: ${formatCommandPath(session.working_directory)}`
204304
- ];
204305
- if (session.sdk_session_id) {
204306
- const threadTitle = getDesktopThreadTitle(session.sdk_session_id);
204307
- lines.push(` Thread: ${session.sdk_session_id}${threadTitle ? ` \xB7 ${threadTitle}` : ""}`);
204716
+ function buildIndexedCommandList(title, items, footer = [], markdown = false) {
204717
+ if (markdown) {
204718
+ const lines2 = [`**${title}**`, ""];
204719
+ items.forEach((item, index) => {
204720
+ const marker = `${index + 1}.`;
204721
+ const childIndent = " ".repeat(marker.length + 1);
204722
+ lines2.push(`${marker} **${item.heading}**`);
204723
+ item.details.filter(Boolean).forEach((detail) => lines2.push(`${childIndent}- ${detail}`));
204724
+ lines2.push("");
204725
+ });
204726
+ footer.filter(Boolean).forEach((line) => lines2.push(`- ${line}`));
204727
+ return lines2.join("\n").trim();
204728
+ }
204729
+ const lines = [title, ""];
204730
+ items.forEach((item, index) => {
204731
+ lines.push(`${index + 1}. ${item.heading}`);
204732
+ item.details.filter(Boolean).forEach((detail) => lines.push(` ${detail}`));
204733
+ lines.push("");
204734
+ });
204735
+ footer.filter(Boolean).forEach((line) => lines.push(line));
204736
+ return lines.join("\n").trim();
204737
+ }
204738
+ function isCommandMarkdownEnabled(channelType) {
204739
+ const { store } = getBridgeContext();
204740
+ if (channelType === "feishu") {
204741
+ return store.getSetting("bridge_feishu_command_markdown_enabled") !== "false";
204742
+ }
204743
+ if (channelType === "weixin") {
204744
+ return store.getSetting("bridge_weixin_command_markdown_enabled") === "true";
204745
+ }
204746
+ return false;
204747
+ }
204748
+ function getCommandResponseParseMode(channelType) {
204749
+ return isCommandMarkdownEnabled(channelType) ? "Markdown" : "plain";
204750
+ }
204751
+ function resolveEffectiveReasoningEffort(session) {
204752
+ const { store } = getBridgeContext();
204753
+ const configured = session?.reasoning_effort || store.getSetting("bridge_codex_reasoning_effort");
204754
+ if (configured === "minimal" || configured === "low" || configured === "medium" || configured === "high" || configured === "xhigh") {
204755
+ return configured;
204756
+ }
204757
+ return "medium";
204758
+ }
204759
+ function resolveEffectiveSandboxMode() {
204760
+ const { store } = getBridgeContext();
204761
+ const configured = store.getSetting("bridge_codex_sandbox_mode");
204762
+ if (configured === "read-only" || configured === "workspace-write" || configured === "danger-full-access") {
204763
+ return configured;
204764
+ }
204765
+ return "workspace-write";
204766
+ }
204767
+ function isSessionExpired(session) {
204768
+ if (!session?.expires_at) return false;
204769
+ const expiresAt = Date.parse(session.expires_at);
204770
+ return Number.isFinite(expiresAt) && expiresAt <= Date.now();
204771
+ }
204772
+ function sanitizePathSlug(raw) {
204773
+ return raw.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "scratch";
204774
+ }
204775
+ function getInternalScratchDir(kind, key) {
204776
+ const dir = path7.join(INTERNAL_SESSION_ROOT, kind, sanitizePathSlug(key));
204777
+ ensureDirectory(dir);
204778
+ return dir;
204779
+ }
204780
+ function resolveNewWorkingDirectory(rawArgs) {
204781
+ const trimmed = rawArgs.trim();
204782
+ if (!trimmed) {
204783
+ return { ok: false, message: "\u7F3A\u5C11\u8DEF\u5F84\u53C2\u6570\u3002" };
204784
+ }
204785
+ if (path7.isAbsolute(trimmed)) {
204786
+ const validated2 = validateWorkingDirectory(trimmed);
204787
+ if (!validated2) {
204788
+ return { ok: false, message: "\u8DEF\u5F84\u65E0\u6548\u3002\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4E14\u4E0D\u80FD\u5305\u542B\u76EE\u5F55\u7A7F\u8D8A\u6216\u7279\u6B8A\u5B57\u7B26\u3002" };
204789
+ }
204790
+ return { ok: true, workDir: validated2 };
204791
+ }
204792
+ const workspaceRoot = getWorkspaceRoot();
204793
+ if (trimmed.includes("\0") || /[$`;|&><(){}\x00-\x1f]/.test(trimmed)) {
204794
+ return { ok: false, message: "\u9879\u76EE\u540D\u65E0\u6548\u3002" };
204795
+ }
204796
+ const normalizedRelative = path7.normalize(trimmed);
204797
+ if (!normalizedRelative || normalizedRelative === "." || normalizedRelative.split(/[\\/]/).some((segment) => segment === "..")) {
204798
+ return { ok: false, message: "\u9879\u76EE\u540D\u65E0\u6548\u3002\u4E0D\u80FD\u4F7F\u7528 .. \u6216\u7A7A\u8DEF\u5F84\u3002" };
204799
+ }
204800
+ const resolvedRoot = path7.resolve(workspaceRoot);
204801
+ const resolvedPath = path7.resolve(resolvedRoot, normalizedRelative);
204802
+ const relative = path7.relative(resolvedRoot, resolvedPath);
204803
+ if (relative.startsWith("..") || path7.isAbsolute(relative)) {
204804
+ return { ok: false, message: "\u9879\u76EE\u8DEF\u5F84\u8D8A\u754C\u3002\u65B0\u9879\u76EE\u5FC5\u987B\u521B\u5EFA\u5728\u9ED8\u8BA4\u5DE5\u4F5C\u7A7A\u95F4\u5185\u3002" };
204805
+ }
204806
+ const validated = validateWorkingDirectory(resolvedPath);
204807
+ if (!validated) {
204808
+ return { ok: false, message: "\u89E3\u6790\u540E\u7684\u5DE5\u4F5C\u76EE\u5F55\u65E0\u6548\u3002" };
204308
204809
  }
204309
- return lines;
204810
+ return { ok: true, workDir: validated };
204811
+ }
204812
+ function ensureWorkingDirectoryExists(workDir) {
204813
+ ensureDirectory(workDir);
204814
+ }
204815
+ function makeDraftSessionName(address) {
204816
+ return `${DRAFT_SESSION_PREFIX}:${address.channelType}:${address.chatId}`;
204817
+ }
204818
+ function makeHistorySummarySessionName(parentSessionId) {
204819
+ return `${HISTORY_SESSION_PREFIX}:${parentSessionId}`;
204820
+ }
204821
+ function cleanupHiddenSessions() {
204822
+ const { store } = getBridgeContext();
204823
+ const bindings = store.listChannelBindings();
204824
+ const boundSessionIds = new Set(bindings.map((binding) => binding.codepilotSessionId));
204825
+ const hiddenSessions = store.listSessions().filter((session) => session.hidden === true);
204826
+ for (const session of hiddenSessions) {
204827
+ if (isSessionExpired(session) && !boundSessionIds.has(session.id)) {
204828
+ store.deleteSession(session.id);
204829
+ }
204830
+ }
204831
+ const draftSessions = store.listSessions().filter((session) => session.hidden === true && session.session_type === "draft" && !boundSessionIds.has(session.id)).sort((a, b) => Date.parse(b.updated_at || b.created_at || "") - Date.parse(a.updated_at || a.created_at || ""));
204832
+ for (const session of draftSessions.slice(MAX_HIDDEN_DRAFT_SESSIONS)) {
204833
+ store.deleteSession(session.id);
204834
+ }
204835
+ }
204836
+ function getOrCreateDraftSession(address) {
204837
+ const { store } = getBridgeContext();
204838
+ cleanupHiddenSessions();
204839
+ const expectedName = makeDraftSessionName(address);
204840
+ const existing = store.listSessions().find(
204841
+ (session) => session.hidden === true && session.session_type === "draft" && session.name === expectedName && !isSessionExpired(session)
204842
+ );
204843
+ if (existing) {
204844
+ store.updateSession(existing.id, {
204845
+ preferred_mode: "ask",
204846
+ expires_at: new Date(Date.now() + DRAFT_TTL_MS).toISOString()
204847
+ });
204848
+ return store.getSession(existing.id) || existing;
204849
+ }
204850
+ const scratchDir = getInternalScratchDir("draft", `${address.channelType}-${address.chatId}`);
204851
+ return store.createSession(
204852
+ expectedName,
204853
+ store.getSetting("bridge_default_model") || "",
204854
+ void 0,
204855
+ scratchDir,
204856
+ "ask",
204857
+ {
204858
+ hidden: true,
204859
+ sessionType: "draft",
204860
+ expiresAt: new Date(Date.now() + DRAFT_TTL_MS).toISOString(),
204861
+ reasoningEffort: "low"
204862
+ }
204863
+ );
204864
+ }
204865
+ function getPendingPermissionLinksForCurrentSession(chatId, sessionId) {
204866
+ const { store } = getBridgeContext();
204867
+ const pending = store.listPendingPermissionLinksByChat(chatId);
204868
+ if (!sessionId) return pending;
204869
+ return pending.filter((link2) => !link2.sessionId || link2.sessionId === sessionId);
204870
+ }
204871
+ function resetDraftSession(address) {
204872
+ const { store } = getBridgeContext();
204873
+ const expectedName = makeDraftSessionName(address);
204874
+ for (const session of store.listSessions()) {
204875
+ if (session.hidden === true && session.session_type === "draft" && session.name === expectedName) {
204876
+ store.deleteSession(session.id);
204877
+ }
204878
+ }
204879
+ return getOrCreateDraftSession(address);
204880
+ }
204881
+ function getOrCreateHistorySummarySession(parentSession) {
204882
+ const { store } = getBridgeContext();
204883
+ cleanupHiddenSessions();
204884
+ const existing = store.listSessions().find(
204885
+ (session) => session.hidden === true && session.session_type === "history_summary" && session.parent_session_id === parentSession.id && !isSessionExpired(session)
204886
+ );
204887
+ if (existing) {
204888
+ store.updateSession(existing.id, {
204889
+ expires_at: new Date(Date.now() + HISTORY_SUMMARY_TTL_MS).toISOString()
204890
+ });
204891
+ return store.getSession(existing.id) || existing;
204892
+ }
204893
+ const scratchDir = getInternalScratchDir("history_summary", parentSession.id);
204894
+ return store.createSession(
204895
+ makeHistorySummarySessionName(parentSession.id),
204896
+ parentSession.model,
204897
+ void 0,
204898
+ scratchDir,
204899
+ "ask",
204900
+ {
204901
+ hidden: true,
204902
+ sessionType: "history_summary",
204903
+ parentSessionId: parentSession.id,
204904
+ expiresAt: new Date(Date.now() + HISTORY_SUMMARY_TTL_MS).toISOString(),
204905
+ reasoningEffort: "low"
204906
+ }
204907
+ );
204908
+ }
204909
+ async function collectInternalTextResponse(llm, params) {
204910
+ const stream = llm.streamChat(params);
204911
+ const reader = stream.getReader();
204912
+ let text2 = "";
204913
+ let sessionId = null;
204914
+ let error = "";
204915
+ while (true) {
204916
+ const { done, value } = await reader.read();
204917
+ if (done) break;
204918
+ for (const line of value.split("\n")) {
204919
+ if (!line.startsWith("data: ")) continue;
204920
+ let event;
204921
+ try {
204922
+ event = JSON.parse(line.slice(6));
204923
+ } catch {
204924
+ continue;
204925
+ }
204926
+ if (event.type === "text") {
204927
+ text2 += event.data;
204928
+ continue;
204929
+ }
204930
+ if (event.type === "status" || event.type === "result") {
204931
+ try {
204932
+ const parsed = JSON.parse(event.data);
204933
+ if (parsed.session_id) sessionId = parsed.session_id;
204934
+ } catch {
204935
+ }
204936
+ continue;
204937
+ }
204938
+ if (event.type === "error") {
204939
+ error = event.data || "Internal summary failed";
204940
+ }
204941
+ }
204942
+ }
204943
+ return {
204944
+ ok: !error,
204945
+ text: text2.trim(),
204946
+ sessionId,
204947
+ ...error ? { error } : {}
204948
+ };
204949
+ }
204950
+ function buildHistoryTranscript(messages) {
204951
+ return messages.map((message, index) => {
204952
+ const role = formatHistoryRole(message.role);
204953
+ return `${index + 1}. ${role}
204954
+ ${truncateHistoryContent(formatStoredMessageContent(message.content), 1600)}`;
204955
+ }).join("\n\n");
204956
+ }
204957
+ async function summarizeHistory(currentBinding) {
204958
+ const { store, llm } = getBridgeContext();
204959
+ const currentSession = store.getSession(currentBinding.codepilotSessionId);
204960
+ if (!currentSession) {
204961
+ return "\u5F53\u524D\u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u6574\u7406\u5386\u53F2\u8BB0\u5F55\u3002";
204962
+ }
204963
+ const limit = getHistoryMessageLimit();
204964
+ const desktopMessages = currentBinding.sdkSessionId ? readDesktopSessionMessages(currentBinding.sdkSessionId, limit) : [];
204965
+ const { messages: storedMessages } = store.getMessages(currentBinding.codepilotSessionId, { limit });
204966
+ const messages = desktopMessages.length > 0 ? desktopMessages : storedMessages;
204967
+ if (messages.length === 0) {
204968
+ return "\u5F53\u524D\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u5386\u53F2\u6D88\u606F\u3002";
204969
+ }
204970
+ const summarySession = getOrCreateHistorySummarySession(currentSession);
204971
+ const transcript = buildHistoryTranscript(messages);
204972
+ const prompt = [
204973
+ "\u8BF7\u53EA\u57FA\u4E8E\u4E0B\u9762\u7684\u4F1A\u8BDD\u8BB0\u5F55\u505A\u6574\u7406\uFF0C\u4E0D\u8981\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\uFF0C\u4E5F\u4E0D\u8981\u5F15\u7528\u5DE5\u4F5C\u533A\u5916\u7684\u4FE1\u606F\u3002",
204974
+ "\u8F93\u51FA\u683C\u5F0F\u56FA\u5B9A\u4E3A 4 \u6BB5\uFF1A",
204975
+ "1. \u5F53\u524D\u76EE\u6807",
204976
+ "2. \u6700\u8FD1\u8FDB\u5C55",
204977
+ "3. \u5F53\u524D\u963B\u585E/\u98CE\u9669",
204978
+ "4. \u4E0B\u4E00\u6B65\u5EFA\u8BAE",
204979
+ "\u6BCF\u6BB5\u63A7\u5236\u5728 1-3 \u53E5\uFF0C\u4E2D\u6587\u8F93\u51FA\uFF0C\u76F4\u63A5\u7ED9\u7ED3\u679C\u3002",
204980
+ "",
204981
+ transcript
204982
+ ].join("\n");
204983
+ const result = await collectInternalTextResponse(llm, {
204984
+ prompt,
204985
+ sessionId: summarySession.id,
204986
+ sdkSessionId: summarySession.sdk_session_id || void 0,
204987
+ model: currentSession.model || currentBinding.model || void 0,
204988
+ modelReasoningEffort: "low",
204989
+ sandboxMode: "read-only",
204990
+ permissionMode: "never",
204991
+ workingDirectory: summarySession.working_directory,
204992
+ conversationHistory: []
204993
+ });
204994
+ if (result.sessionId) {
204995
+ store.updateSdkSessionId(summarySession.id, result.sessionId);
204996
+ }
204997
+ if (!result.ok) {
204998
+ return `\u5386\u53F2\u6574\u7406\u5931\u8D25\uFF1A${result.error || "unknown error"}`;
204999
+ }
205000
+ return result.text || "\u5F53\u524D\u6CA1\u6709\u53EF\u6574\u7406\u7684\u5386\u53F2\u6458\u8981\u3002";
205001
+ }
205002
+ function getDesktopThreadTitle(threadId) {
205003
+ if (!threadId) return null;
205004
+ return getDesktopSessionByThreadId(threadId)?.title || null;
205005
+ }
205006
+ function formatCommandPath(cwd) {
205007
+ return cwd?.trim() || "~";
204310
205008
  }
204311
205009
  function getHistoryMessageLimit() {
204312
205010
  const { store } = getBridgeContext();
@@ -204421,7 +205119,14 @@ function getState() {
204421
205119
  running: false,
204422
205120
  startedAt: null,
204423
205121
  loopAborts: /* @__PURE__ */ new Map(),
205122
+ reconcileTimer: null,
205123
+ mirrorPollTimer: null,
205124
+ mirrorWakeTimer: null,
204424
205125
  activeTasks: /* @__PURE__ */ new Map(),
205126
+ mirrorSubscriptions: /* @__PURE__ */ new Map(),
205127
+ mirrorSyncInFlight: false,
205128
+ mirrorSuppressUntil: /* @__PURE__ */ new Map(),
205129
+ queuedCounts: /* @__PURE__ */ new Map(),
204425
205130
  sessionLocks: /* @__PURE__ */ new Map(),
204426
205131
  autoStartChecked: false
204427
205132
  };
@@ -204429,12 +205134,398 @@ function getState() {
204429
205134
  if (!g[GLOBAL_KEY].sessionLocks) {
204430
205135
  g[GLOBAL_KEY].sessionLocks = /* @__PURE__ */ new Map();
204431
205136
  }
205137
+ if (!g[GLOBAL_KEY].mirrorSubscriptions) {
205138
+ g[GLOBAL_KEY].mirrorSubscriptions = /* @__PURE__ */ new Map();
205139
+ }
205140
+ if (!g[GLOBAL_KEY].queuedCounts) {
205141
+ g[GLOBAL_KEY].queuedCounts = /* @__PURE__ */ new Map();
205142
+ }
205143
+ if (!g[GLOBAL_KEY].mirrorSuppressUntil) {
205144
+ g[GLOBAL_KEY].mirrorSuppressUntil = /* @__PURE__ */ new Map();
205145
+ }
205146
+ if (!Object.prototype.hasOwnProperty.call(g[GLOBAL_KEY], "mirrorSyncInFlight")) {
205147
+ g[GLOBAL_KEY].mirrorSyncInFlight = false;
205148
+ }
204432
205149
  return g[GLOBAL_KEY];
204433
205150
  }
205151
+ function getQueuedCount(sessionId) {
205152
+ const state = getState();
205153
+ return state.queuedCounts.get(sessionId) || 0;
205154
+ }
205155
+ function syncSessionRuntimeState(sessionId) {
205156
+ const { store } = getBridgeContext();
205157
+ const session = store.getSession(sessionId);
205158
+ if (!session) return;
205159
+ const queuedCount = getQueuedCount(sessionId);
205160
+ const isRunning = getState().activeTasks.has(sessionId);
205161
+ const runtimeStatus = queuedCount > 0 ? "queued" : isRunning ? "running" : "idle";
205162
+ if (session.queued_count === queuedCount && session.runtime_status === runtimeStatus) {
205163
+ return;
205164
+ }
205165
+ store.updateSession(sessionId, {
205166
+ queued_count: queuedCount,
205167
+ runtime_status: runtimeStatus,
205168
+ last_runtime_update_at: nowIso()
205169
+ });
205170
+ }
205171
+ function incrementQueuedCount(sessionId) {
205172
+ const state = getState();
205173
+ state.queuedCounts.set(sessionId, getQueuedCount(sessionId) + 1);
205174
+ syncSessionRuntimeState(sessionId);
205175
+ }
205176
+ function decrementQueuedCount(sessionId) {
205177
+ const state = getState();
205178
+ const next = Math.max(0, getQueuedCount(sessionId) - 1);
205179
+ if (next > 0) {
205180
+ state.queuedCounts.set(sessionId, next);
205181
+ } else {
205182
+ state.queuedCounts.delete(sessionId);
205183
+ }
205184
+ syncSessionRuntimeState(sessionId);
205185
+ }
205186
+ function formatRuntimeStatus(session) {
205187
+ const status = session?.runtime_status || "idle";
205188
+ const queuedCount = session?.queued_count && session.queued_count > 0 ? session.queued_count : 0;
205189
+ if (status === "queued") {
205190
+ return queuedCount > 0 ? `\u6392\u961F\u4E2D\uFF08${queuedCount}\uFF09` : "\u6392\u961F\u4E2D";
205191
+ }
205192
+ if (status === "running") {
205193
+ return "\u8FD0\u884C\u4E2D";
205194
+ }
205195
+ return "\u7A7A\u95F2";
205196
+ }
205197
+ function formatMirrorStatus(session) {
205198
+ if (session?.mirror_status === "watching") {
205199
+ return session.mirror_last_event_at ? `\u76D1\u542C\u4E2D \xB7 \u6700\u8FD1\u540C\u6B65 ${session.mirror_last_event_at}` : "\u76D1\u542C\u4E2D";
205200
+ }
205201
+ if (session?.mirror_status === "stale") {
205202
+ return "\u5F85\u6062\u590D\uFF08\u6682\u65F6\u6CA1\u5B9A\u4F4D\u5230\u684C\u9762 thread \u6587\u4EF6\uFF09";
205203
+ }
205204
+ return "\u672A\u76D1\u542C";
205205
+ }
205206
+ function markMirrorSuppressed(sessionId, durationMs = MIRROR_SUPPRESSION_WINDOW_MS) {
205207
+ getState().mirrorSuppressUntil.set(sessionId, Date.now() + durationMs);
205208
+ }
205209
+ function isMirrorSuppressed(sessionId) {
205210
+ const state = getState();
205211
+ const until = state.mirrorSuppressUntil.get(sessionId);
205212
+ if (!until) return false;
205213
+ if (until <= Date.now()) {
205214
+ state.mirrorSuppressUntil.delete(sessionId);
205215
+ return false;
205216
+ }
205217
+ return true;
205218
+ }
205219
+ function resetMirrorReadState(subscription) {
205220
+ subscription.fileOffset = 0;
205221
+ subscription.fileSize = null;
205222
+ subscription.fileMtimeMs = null;
205223
+ subscription.fileIdentity = null;
205224
+ subscription.trailingText = "";
205225
+ }
205226
+ function statMirrorFile(filePath) {
205227
+ try {
205228
+ const stat = fs5.statSync(filePath);
205229
+ if (!stat.isFile()) return null;
205230
+ return {
205231
+ size: stat.size,
205232
+ mtimeMs: stat.mtimeMs,
205233
+ identity: `${stat.dev}:${stat.ino}`
205234
+ };
205235
+ } catch {
205236
+ return null;
205237
+ }
205238
+ }
205239
+ function closeMirrorWatcher(subscription) {
205240
+ if (subscription.watcher) {
205241
+ try {
205242
+ subscription.watcher.close();
205243
+ } catch {
205244
+ }
205245
+ }
205246
+ subscription.watcher = null;
205247
+ subscription.watcherTarget = null;
205248
+ }
205249
+ function scheduleMirrorWake(delayMs = MIRROR_WATCH_DEBOUNCE_MS) {
205250
+ const state = getState();
205251
+ if (!state.running) return;
205252
+ if (state.mirrorWakeTimer) return;
205253
+ state.mirrorWakeTimer = setTimeout(() => {
205254
+ state.mirrorWakeTimer = null;
205255
+ void reconcileMirrorSubscriptions();
205256
+ }, delayMs);
205257
+ }
205258
+ function watchMirrorFile(subscription, filePath) {
205259
+ if (!filePath) {
205260
+ closeMirrorWatcher(subscription);
205261
+ return;
205262
+ }
205263
+ if (subscription.watcherTarget === filePath && subscription.watcher) {
205264
+ return;
205265
+ }
205266
+ closeMirrorWatcher(subscription);
205267
+ try {
205268
+ subscription.watcher = fs5.watch(filePath, () => {
205269
+ subscription.dirty = true;
205270
+ scheduleMirrorWake();
205271
+ });
205272
+ subscription.watcherTarget = filePath;
205273
+ } catch {
205274
+ subscription.watcher = null;
205275
+ subscription.watcherTarget = null;
205276
+ }
205277
+ }
205278
+ function syncMirrorSessionState(sessionId) {
205279
+ const { store } = getBridgeContext();
205280
+ const session = store.getSession(sessionId);
205281
+ if (!session) return;
205282
+ const subscriptions = Array.from(getState().mirrorSubscriptions.values()).filter((item) => item.sessionId === sessionId);
205283
+ const mirrorStatus = subscriptions.length === 0 ? "inactive" : subscriptions.some((item) => item.status === "watching") ? "watching" : subscriptions.some((item) => item.status === "stale") ? "stale" : "inactive";
205284
+ const deliveredAt = subscriptions.map((item) => item.lastDeliveredAt).filter((value) => Boolean(value)).sort().at(-1) || session.mirror_last_event_at;
205285
+ if (session.mirror_status === mirrorStatus && session.mirror_last_event_at === deliveredAt) {
205286
+ return;
205287
+ }
205288
+ store.updateSession(sessionId, {
205289
+ mirror_status: mirrorStatus,
205290
+ mirror_last_event_at: deliveredAt
205291
+ });
205292
+ }
205293
+ function formatMirrorMessage(threadTitle, events) {
205294
+ const recentEvents = events.filter((event) => event.role !== "user").slice(-MIRROR_EVENT_BATCH_LIMIT).map((event) => event.content.trim()).filter(Boolean);
205295
+ if (recentEvents.length === 0) {
205296
+ return "";
205297
+ }
205298
+ const title = threadTitle?.trim() || "\u684C\u9762\u7EBF\u7A0B";
205299
+ return `${title} \u56DE\u590D:
205300
+ ${recentEvents.join("\n\n")}`;
205301
+ }
205302
+ async function deliverMirrorEvents(subscription, events) {
205303
+ if (events.length === 0) return;
205304
+ const state = getState();
205305
+ const adapter = state.adapters.get(subscription.channelType);
205306
+ if (!adapter || !adapter.isRunning()) return;
205307
+ const text2 = formatMirrorMessage(getDesktopThreadTitle(subscription.threadId), events);
205308
+ if (!text2) return;
205309
+ const response = await deliver(adapter, {
205310
+ address: {
205311
+ channelType: subscription.channelType,
205312
+ chatId: subscription.chatId
205313
+ },
205314
+ text: text2,
205315
+ parseMode: "plain"
205316
+ }, {
205317
+ sessionId: subscription.sessionId,
205318
+ dedupKey: `mirror:${subscription.bindingId}:${events[0]?.signature}:${events[events.length - 1]?.signature}`
205319
+ });
205320
+ if (!response.ok) {
205321
+ throw new Error(response.error || "mirror delivery failed");
205322
+ }
205323
+ subscription.lastDeliveredAt = events[events.length - 1]?.timestamp || nowIso();
205324
+ }
205325
+ function removeMirrorSubscription(bindingId) {
205326
+ const state = getState();
205327
+ const existing = state.mirrorSubscriptions.get(bindingId);
205328
+ if (!existing) return;
205329
+ closeMirrorWatcher(existing);
205330
+ state.mirrorSubscriptions.delete(bindingId);
205331
+ syncMirrorSessionState(existing.sessionId);
205332
+ }
205333
+ function upsertMirrorSubscription(binding) {
205334
+ const { store } = getBridgeContext();
205335
+ const state = getState();
205336
+ const session = store.getSession(binding.codepilotSessionId);
205337
+ if (!session) {
205338
+ removeMirrorSubscription(binding.id);
205339
+ return;
205340
+ }
205341
+ const threadId = binding.sdkSessionId || session.sdk_session_id || "";
205342
+ if (!threadId) {
205343
+ removeMirrorSubscription(binding.id);
205344
+ return;
205345
+ }
205346
+ const desktopSession = getDesktopSessionByThreadId(threadId);
205347
+ const filePath = desktopSession?.filePath || null;
205348
+ const existing = state.mirrorSubscriptions.get(binding.id);
205349
+ if (!existing) {
205350
+ const created = {
205351
+ bindingId: binding.id,
205352
+ sessionId: binding.codepilotSessionId,
205353
+ channelType: binding.channelType,
205354
+ chatId: binding.chatId,
205355
+ threadId,
205356
+ filePath,
205357
+ cursor: { initialized: false, lastEventCount: 0 },
205358
+ dirty: true,
205359
+ status: filePath ? "watching" : "stale",
205360
+ watcher: null,
205361
+ watcherTarget: null,
205362
+ lastDeliveredAt: session.mirror_last_event_at || null,
205363
+ lastReconciledAt: null,
205364
+ fileOffset: 0,
205365
+ fileSize: null,
205366
+ fileMtimeMs: null,
205367
+ fileIdentity: null,
205368
+ trailingText: ""
205369
+ };
205370
+ watchMirrorFile(created, filePath);
205371
+ state.mirrorSubscriptions.set(binding.id, created);
205372
+ syncMirrorSessionState(binding.codepilotSessionId);
205373
+ return;
205374
+ }
205375
+ const previousSessionId = existing.sessionId;
205376
+ const threadChanged = existing.threadId !== threadId;
205377
+ const filePathChanged = existing.filePath !== filePath;
205378
+ existing.sessionId = binding.codepilotSessionId;
205379
+ existing.channelType = binding.channelType;
205380
+ existing.chatId = binding.chatId;
205381
+ existing.threadId = threadId;
205382
+ existing.filePath = filePath;
205383
+ existing.status = filePath ? "watching" : "stale";
205384
+ if (threadChanged) {
205385
+ existing.cursor = { initialized: false, lastEventCount: 0 };
205386
+ existing.lastDeliveredAt = session.mirror_last_event_at || null;
205387
+ existing.dirty = true;
205388
+ resetMirrorReadState(existing);
205389
+ } else if (filePathChanged) {
205390
+ existing.dirty = true;
205391
+ resetMirrorReadState(existing);
205392
+ }
205393
+ watchMirrorFile(existing, filePath);
205394
+ if (previousSessionId !== binding.codepilotSessionId) {
205395
+ syncMirrorSessionState(previousSessionId);
205396
+ }
205397
+ syncMirrorSessionState(binding.codepilotSessionId);
205398
+ }
205399
+ function syncMirrorSubscriptionSet() {
205400
+ const { store } = getBridgeContext();
205401
+ const state = getState();
205402
+ const desiredBindings = store.listChannelBindings().filter((binding) => {
205403
+ if (binding.active === false) return false;
205404
+ if (!state.adapters.has(binding.channelType)) return false;
205405
+ const session = store.getSession(binding.codepilotSessionId);
205406
+ return Boolean(binding.sdkSessionId || session?.sdk_session_id);
205407
+ });
205408
+ const desiredIds = /* @__PURE__ */ new Set();
205409
+ for (const binding of desiredBindings) {
205410
+ desiredIds.add(binding.id);
205411
+ upsertMirrorSubscription(binding);
205412
+ }
205413
+ for (const bindingId of Array.from(state.mirrorSubscriptions.keys())) {
205414
+ if (!desiredIds.has(bindingId)) {
205415
+ removeMirrorSubscription(bindingId);
205416
+ }
205417
+ }
205418
+ }
205419
+ async function reconcileMirrorSubscription(subscription) {
205420
+ const { store } = getBridgeContext();
205421
+ const session = store.getSession(subscription.sessionId);
205422
+ if (!session) {
205423
+ removeMirrorSubscription(subscription.bindingId);
205424
+ return;
205425
+ }
205426
+ const desktopSession = getDesktopSessionByThreadId(subscription.threadId);
205427
+ const filePathChanged = subscription.filePath !== (desktopSession?.filePath || null);
205428
+ subscription.filePath = desktopSession?.filePath || null;
205429
+ subscription.status = subscription.filePath ? "watching" : "stale";
205430
+ if (filePathChanged) {
205431
+ subscription.dirty = true;
205432
+ resetMirrorReadState(subscription);
205433
+ }
205434
+ watchMirrorFile(subscription, subscription.filePath);
205435
+ subscription.lastReconciledAt = nowIso();
205436
+ if (!subscription.filePath) {
205437
+ syncMirrorSessionState(subscription.sessionId);
205438
+ return;
205439
+ }
205440
+ const snapshot = statMirrorFile(subscription.filePath);
205441
+ if (!snapshot) {
205442
+ subscription.status = "stale";
205443
+ subscription.dirty = true;
205444
+ resetMirrorReadState(subscription);
205445
+ syncMirrorSessionState(subscription.sessionId);
205446
+ return;
205447
+ }
205448
+ const unchanged = !subscription.dirty && subscription.fileIdentity === snapshot.identity && subscription.fileSize === snapshot.size && subscription.fileMtimeMs === snapshot.mtimeMs;
205449
+ if (unchanged) {
205450
+ syncMirrorSessionState(subscription.sessionId);
205451
+ return;
205452
+ }
205453
+ let deliverableEvents = [];
205454
+ const requiresFullRecover = !subscription.cursor.initialized || subscription.fileOffset === 0 || subscription.fileIdentity !== null && subscription.fileIdentity !== snapshot.identity || subscription.fileSize !== null && snapshot.size < subscription.fileOffset || subscription.fileSize !== null && snapshot.size === subscription.fileOffset && subscription.fileMtimeMs !== null && snapshot.mtimeMs !== subscription.fileMtimeMs;
205455
+ if (requiresFullRecover) {
205456
+ const previousCursor = subscription.cursor;
205457
+ const events = readDesktopSessionEventStreamByFilePath(subscription.filePath);
205458
+ const delta = reconcileDesktopMirrorCursor(subscription.cursor, events);
205459
+ subscription.cursor = delta.nextCursor;
205460
+ deliverableEvents = filterDuplicateAssistantEvents(previousCursor, delta.deliverableEvents);
205461
+ subscription.trailingText = "";
205462
+ subscription.fileOffset = snapshot.size;
205463
+ } else if (snapshot.size > subscription.fileOffset || subscription.trailingText) {
205464
+ const previousCursor = subscription.cursor;
205465
+ const delta = readDesktopSessionEventDeltaByFilePath(
205466
+ subscription.filePath,
205467
+ subscription.fileOffset,
205468
+ snapshot.size,
205469
+ subscription.trailingText
205470
+ );
205471
+ deliverableEvents = filterDuplicateAssistantEvents(previousCursor, delta.events);
205472
+ subscription.cursor = advanceDesktopMirrorCursor(subscription.cursor, delta.events);
205473
+ subscription.trailingText = delta.trailingText;
205474
+ subscription.fileOffset = delta.nextOffset;
205475
+ }
205476
+ subscription.fileSize = snapshot.size;
205477
+ subscription.fileMtimeMs = snapshot.mtimeMs;
205478
+ subscription.fileIdentity = snapshot.identity;
205479
+ subscription.dirty = false;
205480
+ if (deliverableEvents.length === 0) {
205481
+ syncMirrorSessionState(subscription.sessionId);
205482
+ return;
205483
+ }
205484
+ if (getState().activeTasks.has(subscription.sessionId) || isMirrorSuppressed(subscription.sessionId)) {
205485
+ syncMirrorSessionState(subscription.sessionId);
205486
+ return;
205487
+ }
205488
+ try {
205489
+ await deliverMirrorEvents(subscription, deliverableEvents);
205490
+ } catch (error) {
205491
+ subscription.dirty = true;
205492
+ console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
205493
+ }
205494
+ syncMirrorSessionState(subscription.sessionId);
205495
+ }
205496
+ async function reconcileMirrorSubscriptions() {
205497
+ const state = getState();
205498
+ if (!state.running || state.mirrorSyncInFlight) return;
205499
+ state.mirrorSyncInFlight = true;
205500
+ try {
205501
+ syncMirrorSubscriptionSet();
205502
+ for (const subscription of state.mirrorSubscriptions.values()) {
205503
+ await reconcileMirrorSubscription(subscription);
205504
+ }
205505
+ } finally {
205506
+ state.mirrorSyncInFlight = false;
205507
+ }
205508
+ }
205509
+ function clearMirrorSubscriptions() {
205510
+ const state = getState();
205511
+ for (const bindingId of Array.from(state.mirrorSubscriptions.keys())) {
205512
+ removeMirrorSubscription(bindingId);
205513
+ }
205514
+ }
204434
205515
  function processWithSessionLock(sessionId, fn) {
204435
205516
  const state = getState();
204436
205517
  const prev = state.sessionLocks.get(sessionId) || Promise.resolve();
204437
- const current = prev.then(fn, fn);
205518
+ const queued = state.sessionLocks.has(sessionId);
205519
+ if (queued) {
205520
+ incrementQueuedCount(sessionId);
205521
+ }
205522
+ const wrapped = async () => {
205523
+ if (queued) {
205524
+ decrementQueuedCount(sessionId);
205525
+ }
205526
+ await fn();
205527
+ };
205528
+ const current = prev.then(wrapped, wrapped);
204438
205529
  state.sessionLocks.set(sessionId, current);
204439
205530
  current.finally(() => {
204440
205531
  if (state.sessionLocks.get(sessionId) === current) {
@@ -204444,37 +205535,85 @@ function processWithSessionLock(sessionId, fn) {
204444
205535
  });
204445
205536
  return current;
204446
205537
  }
204447
- async function start() {
205538
+ function getActiveChannelTypes(state = getState()) {
205539
+ return Array.from(state.adapters.keys()).sort();
205540
+ }
205541
+ function notifyAdapterSetChanged() {
205542
+ const { lifecycle } = getBridgeContext();
205543
+ lifecycle.onBridgeAdaptersChanged?.(getActiveChannelTypes());
205544
+ }
205545
+ async function stopAdapterInstance(channelType) {
204448
205546
  const state = getState();
204449
- if (state.running) return;
204450
- const { store, lifecycle } = getBridgeContext();
204451
- const bridgeEnabled = store.getSetting("remote_bridge_enabled") === "true";
204452
- if (!bridgeEnabled) {
204453
- console.log("[bridge-manager] Bridge not enabled (remote_bridge_enabled != true)");
204454
- return;
205547
+ const adapter = state.adapters.get(channelType);
205548
+ if (!adapter) return;
205549
+ state.loopAborts.get(channelType)?.abort();
205550
+ state.loopAborts.delete(channelType);
205551
+ try {
205552
+ await adapter.stop();
205553
+ console.log(`[bridge-manager] Stopped adapter: ${channelType}`);
205554
+ } catch (err) {
205555
+ console.error(`[bridge-manager] Error stopping adapter ${channelType}:`, err);
204455
205556
  }
205557
+ state.adapters.delete(channelType);
205558
+ state.adapterMeta.delete(channelType);
205559
+ }
205560
+ async function syncConfiguredAdapters(options) {
205561
+ const state = getState();
205562
+ const { store } = getBridgeContext();
205563
+ let changed = false;
204456
205564
  for (const channelType of getRegisteredTypes()) {
204457
- const settingKey = `bridge_${channelType}_enabled`;
204458
- if (store.getSetting(settingKey) !== "true") continue;
205565
+ const enabled = store.getSetting(`bridge_${channelType}_enabled`) === "true";
205566
+ const existing = state.adapters.get(channelType);
205567
+ if (!enabled) {
205568
+ if (existing) {
205569
+ await stopAdapterInstance(channelType);
205570
+ changed = true;
205571
+ }
205572
+ continue;
205573
+ }
205574
+ if (existing) {
205575
+ continue;
205576
+ }
204459
205577
  const adapter = createAdapter(channelType);
204460
205578
  if (!adapter) continue;
204461
205579
  const configError = adapter.validateConfig();
204462
- if (!configError) {
204463
- registerAdapter(adapter);
204464
- } else {
205580
+ if (configError) {
204465
205581
  console.warn(`[bridge-manager] ${channelType} adapter not valid:`, configError);
205582
+ continue;
204466
205583
  }
204467
- }
204468
- let startedCount = 0;
204469
- for (const [type, adapter] of state.adapters) {
204470
205584
  try {
205585
+ state.adapters.set(channelType, adapter);
205586
+ state.adapterMeta.set(channelType, {
205587
+ lastMessageAt: null,
205588
+ lastError: null
205589
+ });
204471
205590
  await adapter.start();
204472
- console.log(`[bridge-manager] Started adapter: ${type}`);
204473
- startedCount++;
205591
+ console.log(`[bridge-manager] Started adapter: ${channelType}`);
205592
+ if (options.startLoops && state.running && adapter.isRunning()) {
205593
+ runAdapterLoop(adapter);
205594
+ }
205595
+ changed = true;
204474
205596
  } catch (err) {
204475
- console.error(`[bridge-manager] Failed to start adapter ${type}:`, err);
205597
+ state.adapters.delete(channelType);
205598
+ state.adapterMeta.delete(channelType);
205599
+ console.error(`[bridge-manager] Failed to start adapter ${channelType}:`, err);
204476
205600
  }
204477
205601
  }
205602
+ if (changed) {
205603
+ notifyAdapterSetChanged();
205604
+ }
205605
+ }
205606
+ async function start() {
205607
+ const state = getState();
205608
+ if (state.running) return;
205609
+ const { store, lifecycle } = getBridgeContext();
205610
+ const bridgeEnabled = store.getSetting("remote_bridge_enabled") === "true";
205611
+ if (!bridgeEnabled) {
205612
+ console.log("[bridge-manager] Bridge not enabled (remote_bridge_enabled != true)");
205613
+ return;
205614
+ }
205615
+ await syncConfiguredAdapters({ startLoops: false });
205616
+ const startedCount = state.adapters.size;
204478
205617
  if (startedCount === 0) {
204479
205618
  console.warn("[bridge-manager] No adapters started successfully, bridge not activated");
204480
205619
  state.adapters.clear();
@@ -204489,6 +205628,19 @@ async function start() {
204489
205628
  runAdapterLoop(adapter);
204490
205629
  }
204491
205630
  }
205631
+ state.reconcileTimer = setInterval(() => {
205632
+ void syncConfiguredAdapters({ startLoops: true }).catch((err) => {
205633
+ console.error("[bridge-manager] Adapter reconcile failed:", err);
205634
+ });
205635
+ }, 5e3);
205636
+ state.mirrorPollTimer = setInterval(() => {
205637
+ void reconcileMirrorSubscriptions().catch((err) => {
205638
+ console.error("[bridge-manager] Mirror reconcile failed:", err);
205639
+ });
205640
+ }, MIRROR_POLL_INTERVAL_MS);
205641
+ void reconcileMirrorSubscriptions().catch((err) => {
205642
+ console.error("[bridge-manager] Initial mirror reconcile failed:", err);
205643
+ });
204492
205644
  console.log(`[bridge-manager] Bridge started with ${startedCount} adapter(s)`);
204493
205645
  }
204494
205646
  async function stop() {
@@ -204496,27 +205648,56 @@ async function stop() {
204496
205648
  if (!state.running) return;
204497
205649
  const { lifecycle } = getBridgeContext();
204498
205650
  state.running = false;
205651
+ if (state.reconcileTimer) {
205652
+ clearInterval(state.reconcileTimer);
205653
+ state.reconcileTimer = null;
205654
+ }
205655
+ if (state.mirrorPollTimer) {
205656
+ clearInterval(state.mirrorPollTimer);
205657
+ state.mirrorPollTimer = null;
205658
+ }
205659
+ if (state.mirrorWakeTimer) {
205660
+ clearTimeout(state.mirrorWakeTimer);
205661
+ state.mirrorWakeTimer = null;
205662
+ }
204499
205663
  for (const [, abort] of state.loopAborts) {
204500
205664
  abort.abort();
204501
205665
  }
204502
205666
  state.loopAborts.clear();
204503
- for (const [type, adapter] of state.adapters) {
204504
- try {
204505
- await adapter.stop();
204506
- console.log(`[bridge-manager] Stopped adapter: ${type}`);
204507
- } catch (err) {
204508
- console.error(`[bridge-manager] Error stopping adapter ${type}:`, err);
204509
- }
205667
+ const activeSessionIds = Array.from(state.activeTasks.keys());
205668
+ for (const abort of state.activeTasks.values()) {
205669
+ abort.abort();
205670
+ }
205671
+ state.activeTasks.clear();
205672
+ state.mirrorSuppressUntil.clear();
205673
+ state.queuedCounts.clear();
205674
+ for (const sessionId of activeSessionIds) {
205675
+ syncSessionRuntimeState(sessionId);
205676
+ }
205677
+ clearMirrorSubscriptions();
205678
+ for (const type of Array.from(state.adapters.keys())) {
205679
+ await stopAdapterInstance(type);
204510
205680
  }
204511
- state.adapters.clear();
204512
- state.adapterMeta.clear();
204513
205681
  state.startedAt = null;
204514
205682
  lifecycle.onBridgeStop?.();
204515
205683
  console.log("[bridge-manager] Bridge stopped");
204516
205684
  }
204517
- function registerAdapter(adapter) {
205685
+ function getStatus() {
204518
205686
  const state = getState();
204519
- state.adapters.set(adapter.channelType, adapter);
205687
+ return {
205688
+ running: state.running,
205689
+ startedAt: state.startedAt,
205690
+ adapters: Array.from(state.adapters.entries()).map(([type, adapter]) => {
205691
+ const meta = state.adapterMeta.get(type);
205692
+ return {
205693
+ channelType: adapter.channelType,
205694
+ running: adapter.isRunning(),
205695
+ connectedAt: state.startedAt,
205696
+ lastMessageAt: meta?.lastMessageAt ?? null,
205697
+ error: meta?.lastError ?? null
205698
+ };
205699
+ })
205700
+ };
204520
205701
  }
204521
205702
  function runAdapterLoop(adapter) {
204522
205703
  const state = getState();
@@ -204575,7 +205756,7 @@ async function handleMessage(adapter, msg) {
204575
205756
  const confirmMsg = {
204576
205757
  address: msg.address,
204577
205758
  text: "Permission response recorded.",
204578
- parseMode: "plain"
205759
+ parseMode: getCommandResponseParseMode(adapter.channelType)
204579
205760
  };
204580
205761
  await deliver(adapter, confirmMsg);
204581
205762
  }
@@ -204590,7 +205771,7 @@ async function handleMessage(adapter, msg) {
204590
205771
  await deliver(adapter, {
204591
205772
  address: msg.address,
204592
205773
  text: rawData.userVisibleError,
204593
- parseMode: "plain",
205774
+ parseMode: getCommandResponseParseMode(adapter.channelType),
204594
205775
  replyToMessageId: msg.messageId
204595
205776
  });
204596
205777
  } else if (rawData?.imageDownloadFailed || rawData?.attachmentDownloadFailed) {
@@ -204598,7 +205779,7 @@ async function handleMessage(adapter, msg) {
204598
205779
  await deliver(adapter, {
204599
205780
  address: msg.address,
204600
205781
  text: `Failed to download ${rawData.failedCount ?? 1} ${failureLabel}. Please try sending again.`,
204601
- parseMode: "plain",
205782
+ parseMode: getCommandResponseParseMode(adapter.channelType),
204602
205783
  replyToMessageId: msg.messageId
204603
205784
  });
204604
205785
  }
@@ -204608,7 +205789,11 @@ async function handleMessage(adapter, msg) {
204608
205789
  if (adapter.channelType === "feishu" || adapter.channelType === "qq" || adapter.channelType === "weixin") {
204609
205790
  const normalized = rawText.normalize("NFKC").replace(/[\u200B-\u200D\uFEFF]/g, "").trim();
204610
205791
  if (/^[123]$/.test(normalized)) {
204611
- const pendingLinks = store.listPendingPermissionLinksByChat(msg.address.chatId);
205792
+ const currentBinding = store.getChannelBinding(msg.address.channelType, msg.address.chatId);
205793
+ const pendingLinks = getPendingPermissionLinksForCurrentSession(
205794
+ msg.address.chatId,
205795
+ currentBinding?.codepilotSessionId
205796
+ );
204612
205797
  if (pendingLinks.length === 1) {
204613
205798
  const actionMap = { "1": "allow", "2": "allow_session", "3": "deny" };
204614
205799
  const action = actionMap[normalized];
@@ -204620,14 +205805,14 @@ async function handleMessage(adapter, msg) {
204620
205805
  await deliver(adapter, {
204621
205806
  address: msg.address,
204622
205807
  text: `${label}: recorded.`,
204623
- parseMode: "plain",
205808
+ parseMode: getCommandResponseParseMode(adapter.channelType),
204624
205809
  replyToMessageId: msg.messageId
204625
205810
  });
204626
205811
  } else {
204627
205812
  await deliver(adapter, {
204628
205813
  address: msg.address,
204629
205814
  text: `Permission not found or already resolved.`,
204630
- parseMode: "plain",
205815
+ parseMode: getCommandResponseParseMode(adapter.channelType),
204631
205816
  replyToMessageId: msg.messageId
204632
205817
  });
204633
205818
  }
@@ -204639,7 +205824,7 @@ async function handleMessage(adapter, msg) {
204639
205824
  address: msg.address,
204640
205825
  text: `Multiple pending permissions (${pendingLinks.length}). Please use the full command:
204641
205826
  /perm allow|allow_session|deny <id>`,
204642
- parseMode: "plain",
205827
+ parseMode: getCommandResponseParseMode(adapter.channelType),
204643
205828
  replyToMessageId: msg.messageId
204644
205829
  });
204645
205830
  ack();
@@ -204675,6 +205860,8 @@ async function handleMessage(adapter, msg) {
204675
205860
  const taskAbort = new AbortController();
204676
205861
  const state = getState();
204677
205862
  state.activeTasks.set(binding.codepilotSessionId, taskAbort);
205863
+ markMirrorSuppressed(binding.codepilotSessionId);
205864
+ syncSessionRuntimeState(binding.codepilotSessionId);
204678
205865
  let previewState = null;
204679
205866
  const caps = adapter.getPreviewCapabilities?.(msg.address.chatId) ?? null;
204680
205867
  if (caps?.supported) {
@@ -204803,7 +205990,9 @@ async function handleMessage(adapter, msg) {
204803
205990
  } catch {
204804
205991
  }
204805
205992
  }
205993
+ markMirrorSuppressed(binding.codepilotSessionId);
204806
205994
  state.activeTasks.delete(binding.codepilotSessionId);
205995
+ syncSessionRuntimeState(binding.codepilotSessionId);
204807
205996
  adapter.onMessageEnd?.(msg.address.chatId);
204808
205997
  ack();
204809
205998
  }
@@ -204811,8 +206000,9 @@ async function handleMessage(adapter, msg) {
204811
206000
  async function handleCommand(adapter, msg, text2) {
204812
206001
  const { store } = getBridgeContext();
204813
206002
  const parts = text2.split(/\s+/);
204814
- const command = parts[0].split("@")[0].toLowerCase();
206003
+ const rawCommand = parts[0].split("@")[0].toLowerCase();
204815
206004
  const args = parts.slice(1).join(" ").trim();
206005
+ const command = resolveCommandAlias(rawCommand, args);
204816
206006
  const dangerCheck = isDangerousInput(text2);
204817
206007
  if (dangerCheck.dangerous) {
204818
206008
  store.insertAuditLog({
@@ -204826,12 +206016,13 @@ async function handleCommand(adapter, msg, text2) {
204826
206016
  await deliver(adapter, {
204827
206017
  address: msg.address,
204828
206018
  text: `Command rejected: invalid input detected.`,
204829
- parseMode: "plain",
206019
+ parseMode: getCommandResponseParseMode(adapter.channelType),
204830
206020
  replyToMessageId: msg.messageId
204831
206021
  });
204832
206022
  return;
204833
206023
  }
204834
206024
  let response = "";
206025
+ let responseParseMode = getCommandResponseParseMode(adapter.channelType);
204835
206026
  const currentBinding = store.getChannelBinding(msg.address.channelType, msg.address.chatId);
204836
206027
  switch (command) {
204837
206028
  case "/start":
@@ -204841,44 +206032,50 @@ async function handleCommand(adapter, msg, text2) {
204841
206032
  "\u76F4\u63A5\u53D1\u9001\u6587\u672C\uFF0C\u5C31\u4F1A\u7EE7\u7EED\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u7684\u4F1A\u8BDD\u3002",
204842
206033
  "",
204843
206034
  "\u5E38\u7528\u6D41\u7A0B",
204844
- "1. /threads \u67E5\u770B\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD",
204845
- "2. /thread 1 \u63A5\u7BA1\u7B2C 1 \u6761\u684C\u9762\u4F1A\u8BDD",
206035
+ "1. /t \u67E5\u770B\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD",
206036
+ "2. /t 1 \u63A5\u7BA1\u7B2C 1 \u6761\u684C\u9762\u4F1A\u8BDD",
204846
206037
  "3. \u4E4B\u540E\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u8FD9\u6761\u4F1A\u8BDD",
204847
206038
  "",
204848
- "\u53D1\u9001 /help \u67E5\u770B\u5B8C\u6574\u8BF4\u660E\u3002"
206039
+ "\u53D1\u9001 /h \u67E5\u770B\u5B8C\u6574\u8BF4\u660E\u3002"
204849
206040
  ].join("\n");
204850
206041
  break;
204851
206042
  case "/new": {
204852
- const oldBinding = resolve(msg.address);
204853
- const st = getState();
204854
- const oldTask = st.activeTasks.get(oldBinding.codepilotSessionId);
204855
- if (oldTask) {
204856
- oldTask.abort();
204857
- st.activeTasks.delete(oldBinding.codepilotSessionId);
206043
+ if (currentBinding) {
206044
+ const st = getState();
206045
+ const oldTask = st.activeTasks.get(currentBinding.codepilotSessionId);
206046
+ if (oldTask) {
206047
+ oldTask.abort();
206048
+ st.activeTasks.delete(currentBinding.codepilotSessionId);
206049
+ syncSessionRuntimeState(currentBinding.codepilotSessionId);
206050
+ }
204858
206051
  }
204859
206052
  let workDir;
204860
206053
  if (args) {
204861
- const validated = validateWorkingDirectory(args);
204862
- if (!validated) {
204863
- response = "Invalid path. Must be an absolute path without traversal sequences.";
206054
+ const resolved = resolveNewWorkingDirectory(args);
206055
+ if (!resolved.ok) {
206056
+ response = resolved.message;
204864
206057
  break;
204865
206058
  }
204866
- workDir = validated;
206059
+ workDir = resolved.workDir;
206060
+ ensureWorkingDirectoryExists(workDir);
204867
206061
  }
204868
206062
  const binding = createBinding(msg.address, workDir);
204869
206063
  const session = store.getSession(binding.codepilotSessionId);
204870
- response = [
206064
+ response = buildCommandFields(
204871
206065
  "\u5DF2\u65B0\u5EFA\u4F1A\u8BDD",
204872
- "",
204873
- `\u6807\u9898: ${getSessionDisplayName(session, binding.workingDirectory)}`,
204874
- `Session: ${binding.codepilotSessionId}`,
204875
- `\u76EE\u5F55: ${formatCommandPath(binding.workingDirectory)}`
204876
- ].join("\n");
206066
+ [
206067
+ ["\u6807\u9898", getSessionDisplayName(session, binding.workingDirectory)],
206068
+ ["\u76EE\u5F55", formatCommandPath(binding.workingDirectory)],
206069
+ ["\u6A21\u5F0F", binding.mode]
206070
+ ],
206071
+ ["\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u3002"],
206072
+ responseParseMode === "Markdown"
206073
+ );
204877
206074
  break;
204878
206075
  }
204879
206076
  case "/bind": {
204880
206077
  if (!args) {
204881
- response = "\u7528\u6CD5\uFF1A/bind <session-id | thread-id | \u5E8F\u53F7>";
206078
+ response = "\u7528\u6CD5\uFF1A/bind <\u5E8F\u53F7>";
204882
206079
  break;
204883
206080
  }
204884
206081
  const prefersThreadList = parseListIndex(args) !== null;
@@ -204891,47 +206088,46 @@ async function handleCommand(adapter, msg, text2) {
204891
206088
  displayName: threadPick2.match.title
204892
206089
  });
204893
206090
  const session = store.getSession(importedBinding2.codepilotSessionId);
204894
- response = [
206091
+ response = buildCommandFields(
204895
206092
  "\u5DF2\u7ED1\u5B9A\u684C\u9762\u4F1A\u8BDD",
204896
- "",
204897
- `\u6807\u9898: ${threadPick2.match.title || getSessionDisplayName(session, importedBinding2.workingDirectory)}`,
204898
- `Thread: ${threadPick2.match.threadId}`,
204899
- `Session: ${importedBinding2.codepilotSessionId}`,
204900
- `\u76EE\u5F55: ${formatCommandPath(importedBinding2.workingDirectory)}`,
204901
- "",
204902
- "\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u8FD9\u6761\u4F1A\u8BDD\u3002"
204903
- ].join("\n");
206093
+ [
206094
+ ["\u6807\u9898", threadPick2.match.title || getSessionDisplayName(session, importedBinding2.workingDirectory)],
206095
+ ["\u76EE\u5F55", formatCommandPath(importedBinding2.workingDirectory)]
206096
+ ],
206097
+ ["\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u3002"],
206098
+ responseParseMode === "Markdown"
206099
+ );
204904
206100
  break;
204905
206101
  }
204906
206102
  }
204907
206103
  const displayedSessions = getDisplayedBridgeSessions(currentBinding?.codepilotSessionId);
204908
206104
  const sessionPick = resolveByIndexOrPrefix(args, displayedSessions, (session) => session.id);
204909
206105
  if (sessionPick.ambiguous) {
204910
- response = "\u5339\u914D\u5230\u591A\u4E2A\u5185\u90E8\u4F1A\u8BDD\uFF0C\u8BF7\u4F7F\u7528\u66F4\u957F\u7684 session id\uFF0C\u6216\u5148\u53D1\u9001 /sessions \u67E5\u770B\u5E8F\u53F7\u540E\u518D\u7528 /use 2\u3002";
206106
+ response = "\u5339\u914D\u5230\u591A\u4E2A\u517C\u5BB9\u4F1A\u8BDD\uFF0C\u8BF7\u4F7F\u7528\u66F4\u957F\u7684\u7F16\u53F7\uFF0C\u6216\u76F4\u63A5\u6539\u7528 `/t` \u5207\u6362\u684C\u9762\u4F1A\u8BDD\u3002";
204911
206107
  break;
204912
206108
  }
204913
206109
  if (sessionPick.match) {
204914
206110
  const binding = bindToSession(msg.address, sessionPick.match.id);
204915
206111
  if (binding) {
204916
- const threadTitle = getDesktopThreadTitle(binding.sdkSessionId);
204917
- response = [
204918
- "\u5DF2\u7ED1\u5B9A\u5185\u90E8\u4F1A\u8BDD",
204919
- "",
204920
- `\u6807\u9898: ${getSessionDisplayName(sessionPick.match, binding.workingDirectory)}`,
204921
- `Session: ${binding.codepilotSessionId}`,
204922
- `Thread: ${formatCommandMessageId(binding.sdkSessionId)}${threadTitle ? ` \xB7 ${threadTitle}` : ""}`,
204923
- `\u76EE\u5F55: ${formatCommandPath(binding.workingDirectory)}`
204924
- ].join("\n");
206112
+ response = buildCommandFields(
206113
+ "\u5DF2\u5207\u6362\u4F1A\u8BDD\uFF08\u517C\u5BB9\u547D\u4EE4\uFF09",
206114
+ [
206115
+ ["\u6807\u9898", getSessionDisplayName(sessionPick.match, binding.workingDirectory)],
206116
+ ["\u76EE\u5F55", formatCommandPath(binding.workingDirectory)]
206117
+ ],
206118
+ ["\u666E\u901A\u4F7F\u7528\u5EFA\u8BAE\u76F4\u63A5\u901A\u8FC7 `/t` \u5207\u6362\u684C\u9762\u4F1A\u8BDD\u3002"],
206119
+ responseParseMode === "Markdown"
206120
+ );
204925
206121
  break;
204926
206122
  }
204927
206123
  }
204928
206124
  const threadPick = resolveByIndexOrPrefix(args, displayedThreads, (session) => session.threadId);
204929
206125
  if (threadPick.ambiguous) {
204930
- response = "\u5339\u914D\u5230\u591A\u4E2A\u684C\u9762 thread\uFF0C\u8BF7\u4F7F\u7528\u66F4\u957F\u7684 thread id\uFF0C\u6216\u5148\u53D1\u9001 /threads \u67E5\u770B\u5E8F\u53F7\u540E\u518D\u7528 /thread 1\u3002";
206126
+ response = "\u5339\u914D\u5230\u591A\u4E2A\u684C\u9762\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u53D1\u9001 `/t` \u67E5\u770B\u5217\u8868\uFF0C\u518D\u7528 `/t 1` \u8FD9\u79CD\u5E8F\u53F7\u5207\u6362\u3002";
204931
206127
  break;
204932
206128
  }
204933
206129
  if (!threadPick.match) {
204934
- response = "\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684 session / thread\u3002\u5148\u53D1\u9001 /sessions \u6216 /threads \u67E5\u770B\u53EF\u9009\u9879\u3002";
206130
+ response = "\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u76EE\u6807\u3002\u5148\u53D1\u9001 `/t` \u67E5\u770B\u684C\u9762\u4F1A\u8BDD\uFF0C\u518D\u6309\u5E8F\u53F7\u5207\u6362\u3002";
204935
206131
  break;
204936
206132
  }
204937
206133
  const importedBinding = bindToSdkSession(msg.address, threadPick.match.threadId, {
@@ -204939,27 +206135,51 @@ async function handleCommand(adapter, msg, text2) {
204939
206135
  displayName: threadPick.match.title
204940
206136
  });
204941
206137
  const importedSession = store.getSession(importedBinding.codepilotSessionId);
204942
- response = [
206138
+ response = buildCommandFields(
204943
206139
  "\u5DF2\u7ED1\u5B9A\u684C\u9762\u4F1A\u8BDD",
204944
- "",
204945
- `\u6807\u9898: ${threadPick.match.title || getSessionDisplayName(importedSession, importedBinding.workingDirectory)}`,
204946
- `Thread: ${threadPick.match.threadId}`,
204947
- `Session: ${importedBinding.codepilotSessionId}`,
204948
- `\u76EE\u5F55: ${formatCommandPath(importedBinding.workingDirectory)}`,
204949
- "",
204950
- "\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u8FD9\u6761\u4F1A\u8BDD\u3002"
204951
- ].join("\n");
206140
+ [
206141
+ ["\u6807\u9898", threadPick.match.title || getSessionDisplayName(importedSession, importedBinding.workingDirectory)],
206142
+ ["\u76EE\u5F55", formatCommandPath(importedBinding.workingDirectory)]
206143
+ ],
206144
+ ["\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u3002"],
206145
+ responseParseMode === "Markdown"
206146
+ );
204952
206147
  break;
204953
206148
  }
204954
206149
  case "/thread": {
206150
+ if (args === "0" || args === "0 reset") {
206151
+ const draftSession = args === "0 reset" ? resetDraftSession(msg.address) : getOrCreateDraftSession(msg.address);
206152
+ const binding2 = bindToSession(msg.address, draftSession.id);
206153
+ if (!binding2) {
206154
+ response = "\u8349\u7A3F\u7EBF\u7A0B\u5207\u6362\u5931\u8D25\u3002";
206155
+ break;
206156
+ }
206157
+ updateBinding(binding2.id, {
206158
+ mode: "ask",
206159
+ workingDirectory: draftSession.working_directory,
206160
+ model: draftSession.model || binding2.model
206161
+ });
206162
+ response = buildCommandFields(
206163
+ args === "0 reset" ? "\u5DF2\u91CD\u7F6E\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B" : "\u5DF2\u5207\u6362\u5230\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B",
206164
+ [
206165
+ ["\u6807\u9898", getSessionDisplayName(draftSession, draftSession.working_directory)],
206166
+ ["\u76EE\u5F55", formatCommandPath(draftSession.working_directory)],
206167
+ ["\u8FC7\u671F\u65F6\u95F4", draftSession.expires_at || "-"],
206168
+ ["\u6A21\u5F0F", "ask"]
206169
+ ],
206170
+ ["\u8FD9\u662F\u9690\u85CF\u7684\u8349\u7A3F\u7EBF\u7A0B\uFF0C\u4E0D\u4F1A\u51FA\u73B0\u5728\u5E38\u89C4\u4F1A\u8BDD\u5217\u8868\u4E2D\u3002"],
206171
+ responseParseMode === "Markdown"
206172
+ );
206173
+ break;
206174
+ }
204955
206175
  if (!args) {
204956
- response = "\u7528\u6CD5\uFF1A/thread <thread-id | \u5E8F\u53F7>";
206176
+ response = "\u7528\u6CD5\uFF1A/thread <\u5E8F\u53F7>\uFF0C\u6216 /thread 0 \u8FDB\u5165\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B";
204957
206177
  break;
204958
206178
  }
204959
206179
  const displayedThreads = getDisplayedDesktopThreads(10);
204960
206180
  const threadPick = resolveByIndexOrPrefix(args, displayedThreads, (session) => session.threadId);
204961
206181
  if (threadPick.ambiguous) {
204962
- response = "\u5339\u914D\u5230\u591A\u4E2A\u684C\u9762 thread\uFF0C\u8BF7\u4F7F\u7528\u66F4\u957F\u7684 thread id\u3002";
206182
+ response = "\u5339\u914D\u5230\u591A\u4E2A\u684C\u9762\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u53D1\u9001 `/t` \u67E5\u770B\u5217\u8868\uFF0C\u518D\u7528 `/t 1` \u8FD9\u79CD\u5E8F\u53F7\u5207\u6362\u3002";
204963
206183
  break;
204964
206184
  }
204965
206185
  if (!threadPick.match) {
@@ -204970,50 +206190,56 @@ async function handleCommand(adapter, msg, text2) {
204970
206190
  displayName: desktop.title
204971
206191
  } : void 0);
204972
206192
  const session = store.getSession(binding2.codepilotSessionId);
204973
- response = [
204974
- "\u5DF2\u7ED1\u5B9A Thread",
204975
- "",
204976
- `\u6807\u9898: ${desktop?.title || getSessionDisplayName(session, binding2.workingDirectory)}`,
204977
- `Thread: ${args}`,
204978
- `Session: ${binding2.codepilotSessionId}`,
204979
- `\u76EE\u5F55: ${formatCommandPath(binding2.workingDirectory)}`,
204980
- "",
204981
- "\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u8FD9\u6761\u4F1A\u8BDD\u3002"
204982
- ].join("\n");
206193
+ response = buildCommandFields(
206194
+ "\u5DF2\u5207\u6362\u5230\u684C\u9762\u4F1A\u8BDD",
206195
+ [
206196
+ ["\u6807\u9898", desktop?.title || getSessionDisplayName(session, binding2.workingDirectory)],
206197
+ ["\u76EE\u5F55", formatCommandPath(binding2.workingDirectory)]
206198
+ ],
206199
+ ["\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u3002"],
206200
+ responseParseMode === "Markdown"
206201
+ );
204983
206202
  break;
204984
206203
  }
204985
- response = "\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u684C\u9762 thread\u3002\u5148\u53D1\u9001 /threads \u67E5\u770B\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD\u3002";
206204
+ response = "\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u7684\u684C\u9762\u4F1A\u8BDD\u3002\u5148\u53D1\u9001 `/t` \u67E5\u770B\u6700\u8FD1\u4F1A\u8BDD\uFF0C\u518D\u7528 `/t 1` \u63A5\u7BA1\u3002";
204986
206205
  break;
204987
206206
  }
204988
206207
  const binding = bindToSdkSession(msg.address, threadPick.match.threadId, {
204989
206208
  workingDirectory: threadPick.match.cwd,
204990
206209
  displayName: threadPick.match.title
204991
206210
  });
204992
- response = [
204993
- "\u5DF2\u7ED1\u5B9A Thread",
204994
- "",
204995
- `\u6807\u9898: ${threadPick.match.title || "\u672A\u547D\u540D\u7EBF\u7A0B"}`,
204996
- `Thread: ${threadPick.match.threadId}`,
204997
- `Session: ${binding.codepilotSessionId}`,
204998
- `\u76EE\u5F55: ${formatCommandPath(binding.workingDirectory)}`,
204999
- "",
205000
- "\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u8FD9\u6761\u4F1A\u8BDD\u3002"
205001
- ].join("\n");
206211
+ response = buildCommandFields(
206212
+ "\u5DF2\u5207\u6362\u5230\u684C\u9762\u4F1A\u8BDD",
206213
+ [
206214
+ ["\u6807\u9898", threadPick.match.title || "\u672A\u547D\u540D\u7EBF\u7A0B"],
206215
+ ["\u76EE\u5F55", formatCommandPath(binding.workingDirectory)]
206216
+ ],
206217
+ ["\u63A5\u4E0B\u6765\u76F4\u63A5\u53D1\u9001\u6587\u672C\u5373\u53EF\u7EE7\u7EED\u3002"],
206218
+ responseParseMode === "Markdown"
206219
+ );
205002
206220
  break;
205003
206221
  }
205004
206222
  case "/threads": {
205005
206223
  const desktopSessions = getDisplayedDesktopThreads(10);
205006
206224
  if (desktopSessions.length === 0) {
205007
- response = "\u6CA1\u6709\u627E\u5230\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD\u3002\u5148\u5728 Codex Windows App \u4E2D\u6253\u5F00\u4E00\u4E2A\u4F1A\u8BDD\uFF0C\u518D\u56DE\u6765\u8BD5\u4E00\u6B21\u3002";
206225
+ response = "\u6CA1\u6709\u627E\u5230\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD\u3002\u5148\u5728 Codex Desktop App \u4E2D\u6253\u5F00\u4E00\u4E2A\u4F1A\u8BDD\uFF0C\u518D\u56DE\u6765\u8BD5\u4E00\u6B21\u3002";
205008
206226
  break;
205009
206227
  }
205010
- const lines = ["\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD", ""];
205011
- for (const [index, session] of desktopSessions.entries()) {
205012
- lines.push(...buildDesktopSessionLines(session, index), "");
205013
- }
205014
- lines.push("\u53D1\u9001 /thread 1 \u53EF\u63A5\u7BA1\u7B2C 1 \u6761\u684C\u9762\u4F1A\u8BDD\u3002");
205015
- lines.push("\u4E5F\u652F\u6301\u5B8C\u6574 thread id \u6216\u552F\u4E00\u524D\u7F00\uFF0C\u4F8B\u5982 /thread 019d1da4\u3002");
205016
- response = lines.join("\n").trim();
206228
+ response = buildIndexedCommandList(
206229
+ "\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD",
206230
+ desktopSessions.map((session) => ({
206231
+ heading: session.title || "\u672A\u547D\u540D\u7EBF\u7A0B",
206232
+ details: [
206233
+ `\u76EE\u5F55\uFF1A${formatCommandPath(session.cwd)}`,
206234
+ `\u6765\u6E90\uFF1A${session.originator || "Codex Desktop"}`
206235
+ ]
206236
+ })),
206237
+ [
206238
+ "\u53D1\u9001 `/t 1` \u53EF\u63A5\u7BA1\u7B2C 1 \u6761\u684C\u9762\u4F1A\u8BDD\u3002",
206239
+ "\u5B8C\u6574\u547D\u4EE4\u4ECD\u517C\u5BB9\uFF0C\u4F8B\u5982 `/thread 1`\u3002"
206240
+ ],
206241
+ responseParseMode === "Markdown"
206242
+ );
205017
206243
  break;
205018
206244
  }
205019
206245
  case "/use": {
@@ -205024,7 +206250,7 @@ async function handleCommand(adapter, msg, text2) {
205024
206250
  const displayedSessions = getDisplayedBridgeSessions(currentBinding?.codepilotSessionId);
205025
206251
  const sessionPick = resolveByIndexOrPrefix(args, displayedSessions, (session) => session.id);
205026
206252
  if (sessionPick.ambiguous) {
205027
- response = "\u5339\u914D\u5230\u591A\u4E2A\u5185\u90E8\u4F1A\u8BDD\uFF0C\u8BF7\u4F7F\u7528\u66F4\u957F\u7684 session id\u3002";
206253
+ response = "\u5339\u914D\u5230\u591A\u4E2A\u5185\u90E8\u4F1A\u8BDD\uFF0C\u8BF7\u4F7F\u7528\u66F4\u957F\u7684\u7F16\u53F7\u3002";
205028
206254
  break;
205029
206255
  }
205030
206256
  if (!sessionPick.match) {
@@ -205036,63 +206262,126 @@ async function handleCommand(adapter, msg, text2) {
205036
206262
  response = "\u5207\u6362\u5931\u8D25\uFF0C\u8BE5\u4F1A\u8BDD\u4E0D\u5B58\u5728\u3002";
205037
206263
  break;
205038
206264
  }
205039
- const threadTitle = getDesktopThreadTitle(binding.sdkSessionId);
205040
- response = [
205041
- "\u5DF2\u5207\u6362\u4F1A\u8BDD",
205042
- "",
205043
- `\u6807\u9898: ${getSessionDisplayName(sessionPick.match, binding.workingDirectory)}`,
205044
- `Session: ${binding.codepilotSessionId}`,
205045
- `Thread: ${formatCommandMessageId(binding.sdkSessionId)}${threadTitle ? ` \xB7 ${threadTitle}` : ""}`,
205046
- `\u76EE\u5F55: ${formatCommandPath(binding.workingDirectory)}`
205047
- ].join("\n");
206265
+ response = buildCommandFields(
206266
+ "\u5DF2\u5207\u6362\u4F1A\u8BDD\uFF08\u517C\u5BB9\u547D\u4EE4\uFF09",
206267
+ [
206268
+ ["\u6807\u9898", getSessionDisplayName(sessionPick.match, binding.workingDirectory)],
206269
+ ["\u76EE\u5F55", formatCommandPath(binding.workingDirectory)]
206270
+ ],
206271
+ ["\u666E\u901A\u4F7F\u7528\u5EFA\u8BAE\u76F4\u63A5\u901A\u8FC7 `/t` \u5207\u6362\u684C\u9762\u4F1A\u8BDD\u3002"],
206272
+ responseParseMode === "Markdown"
206273
+ );
205048
206274
  break;
205049
206275
  }
205050
- case "/cwd": {
206276
+ case "/reasoning": {
206277
+ if (!currentBinding) {
206278
+ response = "\u5F53\u524D\u804A\u5929\u8FD8\u6CA1\u6709\u7ED1\u5B9A\u4F1A\u8BDD\u3002\u5148\u53D1\u9001\u6D88\u606F\u521B\u5EFA\u4F1A\u8BDD\uFF0C\u6216\u5148\u7528 `/t 1` \u63A5\u7BA1\u684C\u9762\u4F1A\u8BDD\u3002";
206279
+ break;
206280
+ }
206281
+ const session = store.getSession(currentBinding.codepilotSessionId);
206282
+ if (!session) {
206283
+ response = "\u5F53\u524D\u4F1A\u8BDD\u4E0D\u5B58\u5728\u3002";
206284
+ break;
206285
+ }
205051
206286
  if (!args) {
205052
- response = "\u7528\u6CD5\uFF1A/cwd /path/to/directory";
206287
+ response = buildCommandFields(
206288
+ "\u5F53\u524D\u601D\u8003\u7EA7\u522B",
206289
+ [["\u7EA7\u522B", formatReasoningEffort(resolveEffectiveReasoningEffort(session))]],
206290
+ [REASONING_OPTIONS_TEXT, "\u53D1\u9001 `/r 4` \u6216 `/r high` \u53EF\u5207\u6362\u3002"],
206291
+ responseParseMode === "Markdown"
206292
+ );
205053
206293
  break;
205054
206294
  }
205055
- const validatedPath = validateWorkingDirectory(args);
205056
- if (!validatedPath) {
205057
- response = "\u8DEF\u5F84\u65E0\u6548\u3002\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4E14\u4E0D\u80FD\u5305\u542B\u76EE\u5F55\u7A7F\u8D8A\u6216\u7279\u6B8A\u5B57\u7B26\u3002";
206295
+ const reasoning = normalizeReasoningEffort(args);
206296
+ if (!reasoning) {
206297
+ response = buildCommandFields(
206298
+ "\u601D\u8003\u7EA7\u522B\u7528\u6CD5",
206299
+ [["\u547D\u4EE4", "`/reasoning minimal|low|medium|high|xhigh`"]],
206300
+ ["\u4E5F\u652F\u6301\u5B8C\u6574\u547D\u4EE4\uFF1A`/reasoning 1|2|3|4|5`", REASONING_OPTIONS_TEXT],
206301
+ responseParseMode === "Markdown"
206302
+ );
205058
206303
  break;
205059
206304
  }
205060
- const binding = resolve(msg.address);
205061
- updateBinding(binding.id, { workingDirectory: validatedPath });
205062
- response = `\u5DF2\u66F4\u65B0\u5DE5\u4F5C\u76EE\u5F55\uFF1A${validatedPath}`;
206305
+ store.updateSession(session.id, {
206306
+ reasoning_effort: reasoning
206307
+ });
206308
+ response = buildCommandFields(
206309
+ "\u5DF2\u66F4\u65B0\u601D\u8003\u7EA7\u522B",
206310
+ [["\u7EA7\u522B", formatReasoningEffort(reasoning)]],
206311
+ [REASONING_OPTIONS_TEXT],
206312
+ responseParseMode === "Markdown"
206313
+ );
206314
+ break;
206315
+ }
206316
+ case "/cwd": {
206317
+ response = "\u5F53\u524D\u7248\u672C\u5DF2\u4E0D\u652F\u6301 /cwd\u3002\u8BF7\u4F7F\u7528 /new \u65B0\u5EFA\u4F1A\u8BDD\uFF0C\u6216\u4F7F\u7528 /thread /bind /use \u5207\u6362\u5230\u5DF2\u6709\u5DE5\u4F5C\u7A7A\u95F4\u3002";
205063
206318
  break;
205064
206319
  }
205065
206320
  case "/mode": {
206321
+ const binding = currentBinding || resolve(msg.address);
206322
+ if (!args) {
206323
+ response = buildCommandFields(
206324
+ "\u5F53\u524D\u6A21\u5F0F",
206325
+ [["\u6A21\u5F0F", binding.mode]],
206326
+ [MODE_OPTIONS_TEXT, "\u53D1\u9001 `/m code`\u3001`/m plan` \u6216 `/m ask` \u5207\u6362\u3002\u5B8C\u6574\u547D\u4EE4\u4E5F\u517C\u5BB9\uFF1A`/mode code`\u3002"],
206327
+ responseParseMode === "Markdown"
206328
+ );
206329
+ break;
206330
+ }
205066
206331
  if (!validateMode(args)) {
205067
- response = "\u7528\u6CD5\uFF1A/mode plan|code|ask";
206332
+ response = buildCommandFields(
206333
+ "\u6A21\u5F0F\u7528\u6CD5",
206334
+ [["\u547D\u4EE4", "`/mode plan|code|ask`"]],
206335
+ [MODE_OPTIONS_TEXT],
206336
+ responseParseMode === "Markdown"
206337
+ );
205068
206338
  break;
205069
206339
  }
205070
- const binding = resolve(msg.address);
206340
+ const session = store.getSession(binding.codepilotSessionId);
206341
+ if (session) {
206342
+ store.updateSession(session.id, {
206343
+ preferred_mode: args
206344
+ });
206345
+ }
205071
206346
  updateBinding(binding.id, { mode: args });
205072
- response = `\u5DF2\u5207\u6362\u6A21\u5F0F\uFF1A${args}`;
206347
+ response = buildCommandFields(
206348
+ "\u5DF2\u5207\u6362\u6A21\u5F0F",
206349
+ [["\u6A21\u5F0F", args]],
206350
+ [MODE_OPTIONS_TEXT],
206351
+ responseParseMode === "Markdown"
206352
+ );
205073
206353
  break;
205074
206354
  }
205075
206355
  case "/status": {
205076
206356
  const binding = resolve(msg.address);
205077
206357
  const session = store.getSession(binding.codepilotSessionId);
205078
206358
  const threadTitle = getDesktopThreadTitle(binding.sdkSessionId);
205079
- response = [
206359
+ const sandboxMode = resolveEffectiveSandboxMode();
206360
+ const reasoningEffort = resolveEffectiveReasoningEffort(session);
206361
+ const sessionKind = session?.session_type === "draft" ? "\u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B" : session?.session_type === "history_summary" ? "\u5386\u53F2\u6458\u8981\u7EBF\u7A0B" : "\u666E\u901A\u4F1A\u8BDD";
206362
+ response = buildCommandFields(
205080
206363
  "\u5F53\u524D\u4F1A\u8BDD",
205081
- "",
205082
- `\u6807\u9898: ${threadTitle || getSessionDisplayName(session, binding.workingDirectory)}`,
205083
- `Session: ${binding.codepilotSessionId}`,
205084
- `Thread: ${formatCommandMessageId(binding.sdkSessionId)}${threadTitle ? ` \xB7 ${threadTitle}` : ""}`,
205085
- `\u76EE\u5F55: ${formatCommandPath(binding.workingDirectory)}`,
205086
- `\u6A21\u5F0F: ${binding.mode}`,
205087
- `\u6A21\u578B: ${binding.model || "default"}`,
205088
- "",
205089
- binding.sdkSessionId ? "\u5F53\u524D\u804A\u5929\u5DF2\u7ED1\u5B9A\u5230\u4E00\u6761\u5171\u4EAB thread\uFF0C\u76F4\u63A5\u53D1\u9001\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u3002" : "\u5F53\u524D\u804A\u5929\u8FD8\u6CA1\u6709\u7ED1\u5B9A\u684C\u9762 thread\u3002\u53EF\u5148\u53D1\u9001 /threads \u518D\u7528 /thread 1 \u63A5\u7BA1\u3002"
205090
- ].join("\n");
206364
+ [
206365
+ ["\u6807\u9898", threadTitle || getSessionDisplayName(session, binding.workingDirectory)],
206366
+ ["\u76EE\u5F55", formatCommandPath(binding.workingDirectory)],
206367
+ ["\u6A21\u5F0F", binding.mode],
206368
+ ["\u6A21\u578B", binding.model || "default"],
206369
+ ["\u7C7B\u578B", sessionKind],
206370
+ ["\u8FD0\u884C\u72B6\u6001", formatRuntimeStatus(session)],
206371
+ ["\u5171\u4EAB\u955C\u50CF", formatMirrorStatus(session)],
206372
+ ["\u6587\u4EF6\u7CFB\u7EDF\u6743\u9650", sandboxMode],
206373
+ ["\u601D\u8003\u7EA7\u522B", formatReasoningEffort(reasoningEffort)]
206374
+ ],
206375
+ [
206376
+ binding.sdkSessionId ? "\u5F53\u524D\u804A\u5929\u5DF2\u7ED1\u5B9A\u5230\u4E00\u6761\u5171\u4EAB\u4F1A\u8BDD\uFF0C\u76F4\u63A5\u53D1\u9001\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u3002" : "\u5F53\u524D\u804A\u5929\u8FD8\u6CA1\u6709\u7ED1\u5B9A\u684C\u9762\u4F1A\u8BDD\u3002\u53EF\u5148\u53D1\u9001 `/t`\uFF0C\u518D\u7528 `/t 1` \u63A5\u7BA1\u3002"
206377
+ ],
206378
+ responseParseMode === "Markdown"
206379
+ );
205091
206380
  break;
205092
206381
  }
205093
206382
  case "/history": {
205094
206383
  if (!currentBinding) {
205095
- response = "\u5F53\u524D\u804A\u5929\u8FD8\u6CA1\u6709\u7ED1\u5B9A\u4F1A\u8BDD\u3002\u5148\u53D1\u9001\u6D88\u606F\u521B\u5EFA\u4F1A\u8BDD\uFF0C\u6216\u5148\u7528 /thread 1 \u63A5\u7BA1\u684C\u9762\u4F1A\u8BDD\u3002";
206384
+ response = "\u5F53\u524D\u804A\u5929\u8FD8\u6CA1\u6709\u7ED1\u5B9A\u4F1A\u8BDD\u3002\u5148\u53D1\u9001\u6D88\u606F\u521B\u5EFA\u4F1A\u8BDD\uFF0C\u6216\u5148\u7528 `/t 1` \u63A5\u7BA1\u684C\u9762\u4F1A\u8BDD\u3002";
205096
206385
  break;
205097
206386
  }
205098
206387
  const limit = getHistoryMessageLimit();
@@ -205105,35 +206394,63 @@ async function handleCommand(adapter, msg, text2) {
205105
206394
  }
205106
206395
  const threadTitle = getDesktopThreadTitle(currentBinding.sdkSessionId);
205107
206396
  const session = store.getSession(currentBinding.codepilotSessionId);
205108
- const lines = [
205109
- "\u6700\u8FD1\u5BF9\u8BDD",
205110
- "",
205111
- `\u6807\u9898: ${threadTitle || getSessionDisplayName(session, currentBinding.workingDirectory)}`,
205112
- `Session: ${currentBinding.codepilotSessionId}`,
205113
- `Thread: ${formatCommandMessageId(currentBinding.sdkSessionId)}${threadTitle ? ` \xB7 ${threadTitle}` : ""}`,
205114
- `\u6765\u6E90: ${desktopMessages.length > 0 ? "desktop thread" : "bridge cache"}`,
205115
- `\u8FD4\u56DE\u6761\u6570: ${messages.length} / \u914D\u7F6E ${limit}`,
205116
- ""
205117
- ];
205118
- for (const [index, message] of messages.entries()) {
205119
- lines.push(`${index + 1}. ${formatHistoryRole(message.role)}`);
205120
- lines.push(truncateHistoryContent(formatStoredMessageContent(message.content)));
205121
- lines.push("");
206397
+ if (args === "raw") {
206398
+ const header2 = buildCommandFields(
206399
+ "\u6700\u8FD1\u5BF9\u8BDD\uFF08raw\uFF09",
206400
+ [
206401
+ ["\u6807\u9898", threadTitle || getSessionDisplayName(session, currentBinding.workingDirectory)],
206402
+ ["\u6765\u6E90", desktopMessages.length > 0 ? "desktop thread" : "bridge cache"],
206403
+ ["\u8FD4\u56DE\u6761\u6570", `${messages.length} / \u914D\u7F6E ${limit}`]
206404
+ ],
206405
+ [],
206406
+ responseParseMode === "Markdown"
206407
+ );
206408
+ const body = messages.map((message, index) => {
206409
+ if (responseParseMode === "Markdown") {
206410
+ return `${index + 1}. **${formatHistoryRole(message.role)}**
206411
+
206412
+ ${truncateHistoryContent(formatStoredMessageContent(message.content))}`;
206413
+ }
206414
+ return `${index + 1}. ${formatHistoryRole(message.role)}
206415
+ ${truncateHistoryContent(formatStoredMessageContent(message.content))}`;
206416
+ }).join("\n\n");
206417
+ response = [header2, body].join("\n\n").trim();
206418
+ break;
205122
206419
  }
205123
- response = lines.join("\n").trim();
206420
+ const summary = await summarizeHistory(currentBinding);
206421
+ const header = buildCommandFields(
206422
+ "\u6700\u8FD1\u5BF9\u8BDD\uFF08\u6574\u7406\uFF09",
206423
+ [
206424
+ ["\u6807\u9898", threadTitle || getSessionDisplayName(session, currentBinding.workingDirectory)]
206425
+ ],
206426
+ [`\u539F\u59CB\u8BB0\u5F55\u53EF\u53D1\u9001 \`/his raw\` \u67E5\u770B\uFF08\u5B8C\u6574\u547D\u4EE4\uFF1A\`/history raw\`\uFF1B\u5F53\u524D\u6293\u53D6 ${messages.length} \u6761\uFF0C\u914D\u7F6E ${limit} \u6761\uFF09\u3002`],
206427
+ responseParseMode === "Markdown"
206428
+ );
206429
+ response = [header, summary].join("\n\n").trim();
205124
206430
  break;
205125
206431
  }
205126
206432
  case "/sessions": {
205127
206433
  const sessions = getDisplayedBridgeSessions(currentBinding?.codepilotSessionId);
205128
206434
  if (sessions.length === 0) {
205129
- response = "\u5F53\u524D\u6CA1\u6709\u5185\u90E8\u4F1A\u8BDD\u3002\u5148\u53D1\u9001\u4E00\u6761\u6D88\u606F\uFF0C\u6216\u5148\u7528 /thread 1 \u63A5\u7BA1\u684C\u9762\u4F1A\u8BDD\u3002";
206435
+ response = "\u5F53\u524D\u6CA1\u6709\u5185\u90E8\u4F1A\u8BDD\u3002\u666E\u901A\u4F7F\u7528\u5EFA\u8BAE\u76F4\u63A5\u53D1\u9001\u6D88\u606F\u521B\u5EFA\u4F1A\u8BDD\uFF0C\u6216\u5148\u7528 `/t 1` \u63A5\u7BA1\u684C\u9762\u4F1A\u8BDD\u3002";
205130
206436
  } else {
205131
- const lines = ["\u53EF\u5207\u6362\u7684\u5185\u90E8\u4F1A\u8BDD", ""];
205132
- for (const [index, session] of sessions.slice(0, 10).entries()) {
205133
- lines.push(...buildBridgeSessionLines(session, index, session.id === currentBinding?.codepilotSessionId), "");
205134
- }
205135
- lines.push("\u53D1\u9001 /use 2 \u53EF\u5207\u6362\u5230\u7B2C 2 \u6761\u5185\u90E8\u4F1A\u8BDD\u3002");
205136
- response = lines.join("\n").trim();
206437
+ response = buildIndexedCommandList(
206438
+ "\u53EF\u5207\u6362\u7684\u5185\u90E8\u4F1A\u8BDD\uFF08\u517C\u5BB9\u547D\u4EE4\uFF09",
206439
+ sessions.slice(0, 10).map((session) => {
206440
+ const threadTitle = session.sdk_session_id ? getDesktopThreadTitle(session.sdk_session_id) : null;
206441
+ return {
206442
+ heading: `${getSessionDisplayName(session, session.working_directory)}${session.id === currentBinding?.codepilotSessionId ? " [\u5F53\u524D]" : ""}`,
206443
+ details: [
206444
+ `\u76EE\u5F55\uFF1A${formatCommandPath(session.working_directory)}`
206445
+ ]
206446
+ };
206447
+ }),
206448
+ [
206449
+ "\u666E\u901A\u4F7F\u7528\u5EFA\u8BAE\u76F4\u63A5\u901A\u8FC7 `/t` \u5207\u6362\u684C\u9762\u4F1A\u8BDD\u3002",
206450
+ "\u517C\u5BB9\u547D\u4EE4\u4ECD\u53EF\u7528\uFF0C\u4F8B\u5982 `/use 2`\u3002"
206451
+ ],
206452
+ responseParseMode === "Markdown"
206453
+ );
205137
206454
  }
205138
206455
  break;
205139
206456
  }
@@ -205144,6 +206461,7 @@ async function handleCommand(adapter, msg, text2) {
205144
206461
  if (taskAbort) {
205145
206462
  taskAbort.abort();
205146
206463
  st.activeTasks.delete(binding.codepilotSessionId);
206464
+ syncSessionRuntimeState(binding.codepilotSessionId);
205147
206465
  response = "Stopping current task...";
205148
206466
  } else {
205149
206467
  response = "No task is currently running.";
@@ -205158,6 +206476,15 @@ async function handleCommand(adapter, msg, text2) {
205158
206476
  response = "\u7528\u6CD5\uFF1A/perm allow|allow_session|deny <permission_id>";
205159
206477
  break;
205160
206478
  }
206479
+ const link2 = store.getPermissionLink(permId);
206480
+ if (!link2) {
206481
+ response = "\u6CA1\u6709\u627E\u5230\u5BF9\u5E94\u6743\u9650\uFF0C\u6216\u8BE5\u6743\u9650\u5DF2\u5904\u7406\u3002";
206482
+ break;
206483
+ }
206484
+ if (currentBinding?.codepilotSessionId && link2.sessionId && link2.sessionId !== currentBinding.codepilotSessionId) {
206485
+ response = "\u8FD9\u6761\u6743\u9650\u8BF7\u6C42\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\u3002\u8BF7\u5148\u5207\u56DE\u5BF9\u5E94\u4F1A\u8BDD\uFF0C\u518D\u5904\u7406\u8BE5\u6743\u9650\u3002";
206486
+ break;
206487
+ }
205161
206488
  const callbackData = `perm:${permAction}:${permId}`;
205162
206489
  const handled = handlePermissionCallback(callbackData, msg.address.chatId);
205163
206490
  if (handled) {
@@ -205168,47 +206495,39 @@ async function handleCommand(adapter, msg, text2) {
205168
206495
  break;
205169
206496
  }
205170
206497
  case "/help":
206498
+ responseParseMode = getCommandResponseParseMode(adapter.channelType);
205171
206499
  response = [
205172
- "Codex to IM \u547D\u4EE4\u8BF4\u660E",
205173
- "",
205174
- "\u6700\u5E38\u7528",
205175
- "/threads \u67E5\u770B\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD",
205176
- "/thread 1 \u63A5\u7BA1\u7B2C 1 \u6761\u684C\u9762\u4F1A\u8BDD",
205177
- "\u76F4\u63A5\u53D1\u9001\u6587\u672C \u7EE7\u7EED\u5F53\u524D\u5DF2\u7ED1\u5B9A\u4F1A\u8BDD",
205178
- "/status \u67E5\u770B\u5F53\u524D\u804A\u5929\u7ED1\u5B9A\u5230\u4E86\u54EA\u6761\u4F1A\u8BDD",
205179
- "/history \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u6700\u8FD1 N \u6761\u6D88\u606F",
205180
- "",
205181
- "\u5207\u6362\u4E0E\u7ED1\u5B9A",
205182
- "/threads \u5217\u51FA\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD",
205183
- "/thread <thread-id | \u5E8F\u53F7> \u7ED1\u5B9A\u684C\u9762 thread",
205184
- "/sessions \u5217\u51FA\u5185\u90E8\u4F1A\u8BDD",
205185
- "/use <session-id | \u5E8F\u53F7> \u5207\u6362\u5230\u5185\u90E8\u4F1A\u8BDD",
205186
- "/bind <session-id | thread-id | \u5E8F\u53F7> \u667A\u80FD\u7ED1\u5B9A\uFF0C\u517C\u5BB9\u65E7\u7528\u6CD5",
206500
+ "**\u547D\u4EE4\u901F\u89C8**",
205187
206501
  "",
205188
- "\u4F1A\u8BDD\u8BBE\u7F6E",
205189
- "/new [\u7EDD\u5BF9\u8DEF\u5F84] \u65B0\u5EFA\u4F1A\u8BDD",
205190
- "/cwd /path/to/project \u4FEE\u6539\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55",
205191
- "/mode plan|code|ask \u4FEE\u6539\u6A21\u5F0F",
205192
- "/history \u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u6700\u8FD1 N \u6761\u6D88\u606F",
205193
- "/stop \u505C\u6B62\u5F53\u524D\u4EFB\u52A1",
206502
+ "**\u5E38\u7528**",
206503
+ "- `/` \u5F53\u524D\u4F1A\u8BDD",
206504
+ "- `/h` \u5E2E\u52A9",
206505
+ "- `/t` \u6700\u8FD1\u684C\u9762\u4F1A\u8BDD",
206506
+ "- `/t 1` \u63A5\u7BA1\u7B2C 1 \u6761\u4F1A\u8BDD",
206507
+ "- `/n proj1` \u65B0\u5EFA\u4F1A\u8BDD",
206508
+ "- `/his` \u5386\u53F2\u6458\u8981",
205194
206509
  "",
205195
- "\u6743\u9650",
205196
- "/perm allow|allow_session|deny <id>",
205197
- "1/2/3 \u5FEB\u901F\u5904\u7406\u5355\u4E2A\u5F85\u6279\u51C6\u6743\u9650",
206510
+ "**\u8BBE\u7F6E**",
206511
+ "- `/m` \u67E5\u770B\u6A21\u5F0F\uFF1B\u53EF\u7528 `code | plan | ask`",
206512
+ "- `/r` \u67E5\u770B\u601D\u8003\u7EA7\u522B\uFF1B\u53EF\u7528 `1 | 2 | 3 | 4 | 5`",
206513
+ "- `/t 0` \u4E34\u65F6\u8349\u7A3F\u7EBF\u7A0B",
206514
+ "- `/t 0 reset` \u91CD\u7F6E\u8349\u7A3F\u7EBF\u7A0B",
206515
+ "- `/stop` \u505C\u6B62\u5F53\u524D\u4EFB\u52A1",
205198
206516
  "",
205199
- "\u63D0\u793A",
205200
- "thread / session \u90FD\u652F\u6301\u5B8C\u6574 id\u3001\u552F\u4E00\u524D\u7F00\uFF0C\u6216\u5217\u8868\u91CC\u7684\u5E8F\u53F7\u3002"
206517
+ "**\u5176\u5B83**",
206518
+ "- `/his raw` \u539F\u59CB\u8BB0\u5F55",
206519
+ "- `/perm allow|allow_session|deny <id>` \u6216 `1 / 2 / 3` \u5904\u7406\u6743\u9650"
205201
206520
  ].join("\n");
205202
206521
  break;
205203
206522
  default:
205204
- response = `\u672A\u77E5\u547D\u4EE4\uFF1A${command}
205205
- \u53D1\u9001 /help \u67E5\u770B\u53EF\u7528\u547D\u4EE4\u3002`;
206523
+ response = `\u672A\u77E5\u547D\u4EE4\uFF1A${rawCommand}
206524
+ \u53D1\u9001 /h \u6216 /help \u67E5\u770B\u53EF\u7528\u547D\u4EE4\u3002`;
205206
206525
  }
205207
206526
  if (response) {
205208
206527
  await deliver(adapter, {
205209
206528
  address: msg.address,
205210
206529
  text: response,
205211
- parseMode: "plain",
206530
+ parseMode: responseParseMode,
205212
206531
  replyToMessageId: msg.messageId
205213
206532
  });
205214
206533
  }
@@ -205224,22 +206543,22 @@ function computeSdkSessionUpdate(sdkSessionId, hasError) {
205224
206543
  }
205225
206544
 
205226
206545
  // src/store.ts
205227
- import fs5 from "node:fs";
206546
+ import fs6 from "node:fs";
205228
206547
  import path8 from "node:path";
205229
- import crypto8 from "node:crypto";
206548
+ import crypto9 from "node:crypto";
205230
206549
  var DATA_DIR2 = path8.join(CTI_HOME, "data");
205231
206550
  var MESSAGES_DIR = path8.join(DATA_DIR2, "messages");
205232
206551
  function ensureDir2(dir) {
205233
- fs5.mkdirSync(dir, { recursive: true });
206552
+ fs6.mkdirSync(dir, { recursive: true });
205234
206553
  }
205235
206554
  function atomicWrite2(filePath, data) {
205236
206555
  const tmp = filePath + ".tmp";
205237
- fs5.writeFileSync(tmp, data, "utf-8");
205238
- fs5.renameSync(tmp, filePath);
206556
+ fs6.writeFileSync(tmp, data, "utf-8");
206557
+ fs6.renameSync(tmp, filePath);
205239
206558
  }
205240
206559
  function readJson2(filePath, fallback) {
205241
206560
  try {
205242
- const raw = fs5.readFileSync(filePath, "utf-8");
206561
+ const raw = fs6.readFileSync(filePath, "utf-8");
205243
206562
  return JSON.parse(raw);
205244
206563
  } catch {
205245
206564
  return fallback;
@@ -205249,13 +206568,14 @@ function writeJson(filePath, data) {
205249
206568
  atomicWrite2(filePath, JSON.stringify(data, null, 2));
205250
206569
  }
205251
206570
  function uuid() {
205252
- return crypto8.randomUUID();
206571
+ return crypto9.randomUUID();
205253
206572
  }
205254
206573
  function now() {
205255
206574
  return (/* @__PURE__ */ new Date()).toISOString();
205256
206575
  }
205257
206576
  var JsonFileStore = class {
205258
206577
  settings;
206578
+ dynamicSettings;
205259
206579
  sessions = /* @__PURE__ */ new Map();
205260
206580
  bindings = /* @__PURE__ */ new Map();
205261
206581
  messages = /* @__PURE__ */ new Map();
@@ -205264,8 +206584,9 @@ var JsonFileStore = class {
205264
206584
  dedupKeys = /* @__PURE__ */ new Map();
205265
206585
  locks = /* @__PURE__ */ new Map();
205266
206586
  auditLog = [];
205267
- constructor(settingsMap) {
206587
+ constructor(settingsMap, options) {
205268
206588
  this.settings = settingsMap;
206589
+ this.dynamicSettings = options?.dynamicSettings === true;
205269
206590
  ensureDir2(DATA_DIR2);
205270
206591
  ensureDir2(MESSAGES_DIR);
205271
206592
  this.loadAll();
@@ -205360,7 +206681,19 @@ var JsonFileStore = class {
205360
206681
  return msgs;
205361
206682
  }
205362
206683
  // ── Settings ──
206684
+ refreshSettings() {
206685
+ if (!this.dynamicSettings) return;
206686
+ try {
206687
+ const next = configToSettings(loadConfig());
206688
+ this.settings = new Map([
206689
+ ...this.settings,
206690
+ ...next
206691
+ ]);
206692
+ } catch {
206693
+ }
206694
+ }
205363
206695
  getSetting(key) {
206696
+ this.refreshSettings();
205364
206697
  return this.settings.get(key) ?? null;
205365
206698
  }
205366
206699
  // ── Channel Bindings ──
@@ -205379,6 +206712,7 @@ var JsonFileStore = class {
205379
206712
  sdkSessionId: data.sdkSessionId ?? existing.sdkSessionId,
205380
206713
  workingDirectory: data.workingDirectory,
205381
206714
  model: data.model,
206715
+ mode: data.mode ?? existing.mode,
205382
206716
  updatedAt: now()
205383
206717
  };
205384
206718
  this.bindings.set(key, updated);
@@ -205393,7 +206727,7 @@ var JsonFileStore = class {
205393
206727
  sdkSessionId: data.sdkSessionId ?? "",
205394
206728
  workingDirectory: data.workingDirectory,
205395
206729
  model: data.model,
205396
- mode: this.settings.get("bridge_default_mode") || "code",
206730
+ mode: data.mode || this.getSetting("bridge_default_mode") || "code",
205397
206731
  active: true,
205398
206732
  createdAt: now(),
205399
206733
  updatedAt: now()
@@ -205436,14 +206770,23 @@ var JsonFileStore = class {
205436
206770
  }
205437
206771
  return null;
205438
206772
  }
205439
- createSession(name, model, systemPrompt, cwd, _mode) {
206773
+ createSession(name, model, systemPrompt, cwd, mode, options) {
205440
206774
  this.reloadSessions();
206775
+ const timestamp = now();
205441
206776
  const session = {
205442
206777
  id: uuid(),
205443
206778
  name,
205444
- working_directory: cwd || this.settings.get("bridge_default_work_dir") || process.cwd(),
206779
+ working_directory: cwd || this.getSetting("bridge_default_work_dir") || process.cwd(),
205445
206780
  model,
205446
- system_prompt: systemPrompt
206781
+ preferred_mode: mode,
206782
+ system_prompt: systemPrompt,
206783
+ reasoning_effort: options?.reasoningEffort,
206784
+ session_type: options?.sessionType || "normal",
206785
+ hidden: options?.hidden === true,
206786
+ parent_session_id: options?.parentSessionId,
206787
+ expires_at: options?.expiresAt,
206788
+ created_at: timestamp,
206789
+ updated_at: timestamp
205447
206790
  };
205448
206791
  this.sessions.set(session.id, session);
205449
206792
  this.persistSessions();
@@ -205454,9 +206797,40 @@ var JsonFileStore = class {
205454
206797
  const s = this.sessions.get(sessionId);
205455
206798
  if (s) {
205456
206799
  s.provider_id = providerId;
206800
+ s.updated_at = now();
205457
206801
  this.persistSessions();
205458
206802
  }
205459
206803
  }
206804
+ updateSession(sessionId, updates) {
206805
+ this.reloadSessions();
206806
+ const session = this.sessions.get(sessionId);
206807
+ if (!session) return;
206808
+ const next = {
206809
+ ...session,
206810
+ ...updates,
206811
+ id: session.id,
206812
+ updated_at: now()
206813
+ };
206814
+ this.sessions.set(sessionId, next);
206815
+ this.persistSessions();
206816
+ }
206817
+ deleteSession(sessionId) {
206818
+ this.reloadSessions();
206819
+ this.reloadBindings();
206820
+ this.sessions.delete(sessionId);
206821
+ for (const [key, binding] of this.bindings) {
206822
+ if (binding.codepilotSessionId === sessionId) {
206823
+ this.bindings.delete(key);
206824
+ }
206825
+ }
206826
+ this.messages.delete(sessionId);
206827
+ try {
206828
+ fs6.rmSync(path8.join(MESSAGES_DIR, `${sessionId}.json`), { force: true });
206829
+ } catch {
206830
+ }
206831
+ this.persistSessions();
206832
+ this.persistBindings();
206833
+ }
205460
206834
  // ── Messages ──
205461
206835
  addMessage(sessionId, role, content, _usage) {
205462
206836
  const msgs = this.loadMessages(sessionId);
@@ -205496,6 +206870,26 @@ var JsonFileStore = class {
205496
206870
  }
205497
206871
  }
205498
206872
  setSessionRuntimeStatus(_sessionId, _status) {
206873
+ this.reloadSessions();
206874
+ const session = this.sessions.get(_sessionId);
206875
+ if (!session) return;
206876
+ const queuedCount = session.queued_count && session.queued_count > 0 ? session.queued_count : 0;
206877
+ let runtimeStatus;
206878
+ if (_status === "running") {
206879
+ runtimeStatus = queuedCount > 0 ? "queued" : "running";
206880
+ } else if (_status === "idle") {
206881
+ runtimeStatus = queuedCount > 0 ? "queued" : "idle";
206882
+ } else {
206883
+ runtimeStatus = session.runtime_status;
206884
+ }
206885
+ const next = {
206886
+ ...session,
206887
+ runtime_status: runtimeStatus,
206888
+ last_runtime_update_at: now(),
206889
+ updated_at: now()
206890
+ };
206891
+ this.sessions.set(_sessionId, next);
206892
+ this.persistSessions();
205499
206893
  }
205500
206894
  // ── SDK Session ──
205501
206895
  updateSdkSessionId(sessionId, sdkSessionId) {
@@ -205504,6 +206898,7 @@ var JsonFileStore = class {
205504
206898
  const s = this.sessions.get(sessionId);
205505
206899
  if (s) {
205506
206900
  s.sdk_session_id = sdkSessionId;
206901
+ s.updated_at = now();
205507
206902
  this.persistSessions();
205508
206903
  }
205509
206904
  for (const [key, b] of this.bindings) {
@@ -205518,6 +206913,7 @@ var JsonFileStore = class {
205518
206913
  const s = this.sessions.get(sessionId);
205519
206914
  if (s) {
205520
206915
  s.model = model;
206916
+ s.updated_at = now();
205521
206917
  this.persistSessions();
205522
206918
  }
205523
206919
  }
@@ -205574,6 +206970,7 @@ var JsonFileStore = class {
205574
206970
  permissionRequestId: link2.permissionRequestId,
205575
206971
  chatId: link2.chatId,
205576
206972
  messageId: link2.messageId,
206973
+ sessionId: link2.sessionId,
205577
206974
  resolved: false,
205578
206975
  suggestions: link2.suggestions
205579
206976
  };
@@ -205611,7 +207008,7 @@ var JsonFileStore = class {
205611
207008
 
205612
207009
  // src/llm-provider.ts
205613
207010
  init_sse_utils();
205614
- import fs6 from "node:fs";
207011
+ import fs7 from "node:fs";
205615
207012
  import { execSync } from "node:child_process";
205616
207013
  import { query } from "@anthropic-ai/claude-agent-sdk";
205617
207014
  var ENV_WHITELIST = /* @__PURE__ */ new Set([
@@ -205765,7 +207162,7 @@ function preflightCheck(cliPath) {
205765
207162
  }
205766
207163
  function isExecutable(p) {
205767
207164
  try {
205768
- fs6.accessSync(p, fs6.constants.X_OK);
207165
+ fs7.accessSync(p, fs7.constants.X_OK);
205769
207166
  return true;
205770
207167
  } catch {
205771
207168
  return false;
@@ -206121,7 +207518,7 @@ var PendingPermissions = class {
206121
207518
  };
206122
207519
 
206123
207520
  // src/logger.ts
206124
- import fs7 from "node:fs";
207521
+ import fs8 from "node:fs";
206125
207522
  import path9 from "node:path";
206126
207523
  var MASK_PATTERNS = [
206127
207524
  /(?:token|secret|password|api_key)["']?\s*[:=]\s*["']?([^\s"',]+)/gi,
@@ -206145,11 +207542,11 @@ var MAX_LOG_SIZE = 10 * 1024 * 1024;
206145
207542
  var MAX_ROTATED = 3;
206146
207543
  var logStream = null;
206147
207544
  function openLogStream() {
206148
- return fs7.createWriteStream(LOG_PATH, { flags: "a" });
207545
+ return fs8.createWriteStream(LOG_PATH, { flags: "a" });
206149
207546
  }
206150
207547
  function rotateIfNeeded() {
206151
207548
  try {
206152
- const stat = fs7.statSync(LOG_PATH);
207549
+ const stat = fs8.statSync(LOG_PATH);
206153
207550
  if (stat.size < MAX_LOG_SIZE) return;
206154
207551
  } catch {
206155
207552
  return;
@@ -206159,17 +207556,17 @@ function rotateIfNeeded() {
206159
207556
  logStream = null;
206160
207557
  }
206161
207558
  const path32 = `${LOG_PATH}.${MAX_ROTATED}`;
206162
- if (fs7.existsSync(path32)) fs7.unlinkSync(path32);
207559
+ if (fs8.existsSync(path32)) fs8.unlinkSync(path32);
206163
207560
  for (let i = MAX_ROTATED - 1; i >= 1; i--) {
206164
207561
  const src = `${LOG_PATH}.${i}`;
206165
207562
  const dst = `${LOG_PATH}.${i + 1}`;
206166
- if (fs7.existsSync(src)) fs7.renameSync(src, dst);
207563
+ if (fs8.existsSync(src)) fs8.renameSync(src, dst);
206167
207564
  }
206168
- fs7.renameSync(LOG_PATH, `${LOG_PATH}.1`);
207565
+ fs8.renameSync(LOG_PATH, `${LOG_PATH}.1`);
206169
207566
  logStream = openLogStream();
206170
207567
  }
206171
207568
  function setupLogger() {
206172
- fs7.mkdirSync(LOG_DIR, { recursive: true });
207569
+ fs8.mkdirSync(LOG_DIR, { recursive: true });
206173
207570
  logStream = openLogStream();
206174
207571
  const write = (level, args) => {
206175
207572
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -206237,24 +207634,27 @@ async function resolveProvider(config2, pendingPerms) {
206237
207634
  return new SDKLLMProvider(pendingPerms, cliPath, config2.autoApprove);
206238
207635
  }
206239
207636
  function writeStatus(info) {
206240
- fs9.mkdirSync(RUNTIME_DIR, { recursive: true });
207637
+ fs10.mkdirSync(RUNTIME_DIR, { recursive: true });
206241
207638
  let existing = {};
206242
207639
  try {
206243
- existing = JSON.parse(fs9.readFileSync(STATUS_FILE, "utf-8"));
207640
+ existing = JSON.parse(fs10.readFileSync(STATUS_FILE, "utf-8"));
206244
207641
  } catch {
206245
207642
  }
206246
207643
  const merged = { ...existing, ...info };
206247
207644
  const tmp = STATUS_FILE + ".tmp";
206248
- fs9.writeFileSync(tmp, JSON.stringify(merged, null, 2), "utf-8");
206249
- fs9.renameSync(tmp, STATUS_FILE);
207645
+ fs10.writeFileSync(tmp, JSON.stringify(merged, null, 2), "utf-8");
207646
+ fs10.renameSync(tmp, STATUS_FILE);
207647
+ }
207648
+ function getRunningChannels() {
207649
+ return getStatus().adapters.map((adapter) => adapter.channelType).sort();
206250
207650
  }
206251
207651
  async function main() {
206252
207652
  const config2 = loadConfig();
206253
207653
  setupLogger();
206254
- const runId = crypto9.randomUUID();
207654
+ const runId = crypto10.randomUUID();
206255
207655
  console.log(`[codex-to-im] Starting bridge (run_id: ${runId})`);
206256
207656
  const settings = configToSettings(config2);
206257
- const store = new JsonFileStore(settings);
207657
+ const store = new JsonFileStore(settings, { dynamicSettings: true });
206258
207658
  const pendingPerms = new PendingPermissions();
206259
207659
  const llm = await resolveProvider(config2, pendingPerms);
206260
207660
  console.log(`[codex-to-im] Runtime: ${config2.runtime}`);
@@ -206267,19 +207667,29 @@ async function main() {
206267
207667
  permissions: gateway,
206268
207668
  lifecycle: {
206269
207669
  onBridgeStart: () => {
206270
- fs9.mkdirSync(RUNTIME_DIR, { recursive: true });
206271
- fs9.writeFileSync(PID_FILE, String(process.pid), "utf-8");
207670
+ fs10.mkdirSync(RUNTIME_DIR, { recursive: true });
207671
+ fs10.writeFileSync(PID_FILE, String(process.pid), "utf-8");
207672
+ const channels = getRunningChannels();
206272
207673
  writeStatus({
206273
207674
  running: true,
206274
207675
  pid: process.pid,
206275
207676
  runId,
206276
207677
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
206277
- channels: config2.enabledChannels
207678
+ channels
207679
+ });
207680
+ console.log(`[codex-to-im] Bridge started (PID: ${process.pid}, channels: ${channels.join(", ")})`);
207681
+ },
207682
+ onBridgeAdaptersChanged: (channels) => {
207683
+ writeStatus({
207684
+ running: true,
207685
+ pid: process.pid,
207686
+ runId,
207687
+ channels
206278
207688
  });
206279
- console.log(`[codex-to-im] Bridge started (PID: ${process.pid}, channels: ${config2.enabledChannels.join(", ")})`);
207689
+ console.log(`[codex-to-im] Active channels updated: ${channels.join(", ") || "none"}`);
206280
207690
  },
206281
207691
  onBridgeStop: () => {
206282
- writeStatus({ running: false });
207692
+ writeStatus({ running: false, channels: [] });
206283
207693
  console.log("[codex-to-im] Bridge stopped");
206284
207694
  }
206285
207695
  }