@ycodium-ai/plugin-im 0.2.3232 → 0.2.3236

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/index.js CHANGED
@@ -126208,9 +126208,7 @@ var makeAcknowledgements = (core) => {
126208
126208
  };
126209
126209
 
126210
126210
  // src/botConfig.ts
126211
- import {
126212
- PLUGIN_PROXY_DIRECT
126213
- } from "@ycodium-ai/plugin-api";
126211
+ import { PLUGIN_PROXY_DIRECT } from "@ycodium-ai/plugin-api";
126214
126212
 
126215
126213
  // src/attachedThreads.ts
126216
126214
  var ATTACHED_THREAD_LIMIT = 200;
@@ -126510,6 +126508,23 @@ var makeConclusionLedger = (core) => {
126510
126508
  };
126511
126509
  };
126512
126510
 
126511
+ // src/sessionTransfer.ts
126512
+ var foldSessionTransferred = (state, data) => {
126513
+ const chatKey = data["chatKey"];
126514
+ const projectId = data["projectId"];
126515
+ const threadId = data["threadId"];
126516
+ if (typeof chatKey !== "string" || chatKey === "" || typeof projectId !== "string" || projectId === "" || typeof threadId !== "string" || threadId === "")
126517
+ return state;
126518
+ const chats = { ...state.chats };
126519
+ for (const [key, record4] of Object.entries(chats)) {
126520
+ if (key !== chatKey && record4.threadId === threadId) {
126521
+ chats[key] = { ...record4, threadId: null };
126522
+ }
126523
+ }
126524
+ chats[chatKey] = { ...chatOf(state, chatKey), projectId, threadId, target: { kind: "thread" } };
126525
+ return { ...state, chats };
126526
+ };
126527
+
126513
126528
  // src/chatState.ts
126514
126529
  var IM_STATE_KEY = "chats";
126515
126530
  var IM_STATE_VERSION = 1;
@@ -126676,6 +126691,8 @@ var withChat = (state, chatKey, patch) => ({
126676
126691
  var foldImEvent = (state, name, data) => {
126677
126692
  if (!isRecord(data)) return state;
126678
126693
  switch (name) {
126694
+ case "session-transferred":
126695
+ return foldSessionTransferred(state, data);
126679
126696
  case "conclusion-owed":
126680
126697
  case "conclusion-delivered":
126681
126698
  return foldConclusionEvent(state, name, data);
@@ -127294,6 +127311,7 @@ var parseOnOff = (value2, defaultOn) => {
127294
127311
  var buildBotColumns = (adapters) => {
127295
127312
  const platformColumn = {
127296
127313
  key: "platform",
127314
+ group: "essential",
127297
127315
  kind: "select",
127298
127316
  labelKey: "column.platform.label",
127299
127317
  options: adapters.map((adapter) => ({
@@ -127312,6 +127330,7 @@ var buildBotColumns = (adapters) => {
127312
127330
  return [
127313
127331
  {
127314
127332
  key: "displayName",
127333
+ group: "essential",
127315
127334
  kind: "text",
127316
127335
  labelKey: "column.displayName.label",
127317
127336
  placeholderKey: "column.displayName.placeholder"
@@ -127320,6 +127339,7 @@ var buildBotColumns = (adapters) => {
127320
127339
  ...credentialColumns,
127321
127340
  {
127322
127341
  key: "accessMode",
127342
+ group: "essential",
127323
127343
  kind: "select",
127324
127344
  labelKey: "column.accessMode.label",
127325
127345
  descriptionKey: "column.accessMode.description",
@@ -127327,6 +127347,7 @@ var buildBotColumns = (adapters) => {
127327
127347
  },
127328
127348
  {
127329
127349
  key: "allowedSenders",
127350
+ group: "essential",
127330
127351
  kind: "text",
127331
127352
  labelKey: "column.allowedSenders.label",
127332
127353
  descriptionKey: "column.allowedSenders.description",
@@ -127337,12 +127358,14 @@ var buildBotColumns = (adapters) => {
127337
127358
  // 选项由宿主填,连三态一起——插件只声明「这个格子装的是代理」,宿主因此
127338
127359
  // 也就认得出这一行引用了哪个代理,删代理时拦得住(路线图 #64)。
127339
127360
  key: "proxy",
127361
+ group: "more",
127340
127362
  kind: "proxy",
127341
127363
  labelKey: "column.proxy.label",
127342
127364
  descriptionKey: "column.proxy.description"
127343
127365
  },
127344
127366
  {
127345
127367
  key: "runtimeMode",
127368
+ group: "more",
127346
127369
  kind: "select",
127347
127370
  labelKey: "column.runtimeMode.label",
127348
127371
  descriptionKey: "column.runtimeMode.description",
@@ -127355,6 +127378,7 @@ var buildBotColumns = (adapters) => {
127355
127378
  },
127356
127379
  {
127357
127380
  key: "groupMention",
127381
+ group: "more",
127358
127382
  kind: "select",
127359
127383
  labelKey: "column.groupMention.label",
127360
127384
  descriptionKey: "column.groupMention.description",
@@ -127366,6 +127390,7 @@ var buildBotColumns = (adapters) => {
127366
127390
  },
127367
127391
  {
127368
127392
  key: "queueMode",
127393
+ group: "more",
127369
127394
  kind: "select",
127370
127395
  labelKey: "column.queueMode.label",
127371
127396
  descriptionKey: "column.queueMode.description",
@@ -127373,6 +127398,7 @@ var buildBotColumns = (adapters) => {
127373
127398
  },
127374
127399
  {
127375
127400
  key: "larkPanelMode",
127401
+ group: "more",
127376
127402
  kind: "select",
127377
127403
  labelKey: "column.larkPanelMode.label",
127378
127404
  descriptionKey: "column.larkPanelMode.description",
@@ -127381,6 +127407,7 @@ var buildBotColumns = (adapters) => {
127381
127407
  },
127382
127408
  {
127383
127409
  key: "wecomNumberedHints",
127410
+ group: "more",
127384
127411
  kind: "select",
127385
127412
  labelKey: "column.wecomNumberedHints.label",
127386
127413
  descriptionKey: "column.wecomNumberedHints.description",
@@ -127720,16 +127747,19 @@ var formatProjects = (rows, t) => {
127720
127747
  ].join("\n");
127721
127748
  };
127722
127749
  var listedProjectIds = (rows) => rows.slice(0, PROJECT_LIST_MAX).map((row) => row.projectId);
127750
+ var isProjectNumber = (arg) => /^[1-9]\d*$/u.test(arg.trim());
127751
+ var pickProjectNumber = (arg, listed, rows) => {
127752
+ if (!isProjectNumber(arg)) return null;
127753
+ const ids = listed ?? listedProjectIds(rows);
127754
+ return ids[Number(arg.trim()) - 1] ?? null;
127755
+ };
127723
127756
  var pickProject = (arg, listed, rows) => {
127724
127757
  const wanted = arg.trim();
127725
127758
  if (wanted === "") return null;
127726
127759
  const byId = rows.find((row) => row.projectId === wanted);
127727
127760
  if (byId !== void 0) return byId.projectId;
127728
- if (/^[1-9]\d*$/u.test(wanted)) {
127729
- const ids = listed ?? listedProjectIds(rows);
127730
- const byNumber = ids[Number(wanted) - 1];
127731
- if (byNumber !== void 0) return byNumber;
127732
- }
127761
+ const byNumber = pickProjectNumber(wanted, listed, rows);
127762
+ if (byNumber !== null) return byNumber;
127733
127763
  const needle = wanted.toLowerCase();
127734
127764
  const exact = rows.filter((row) => row.title.toLowerCase() === needle);
127735
127765
  if (exact.length > 0) return exact.length === 1 ? exact[0]?.projectId ?? null : null;
@@ -127774,29 +127804,42 @@ var sessionLine = (row, position, now, t) => {
127774
127804
  ].filter((badge) => badge !== null);
127775
127805
  return [`${String(position)}. ${title}`, age, ...badges, shortId(row.threadId)].filter((part) => part !== "").join(" \xB7 ");
127776
127806
  };
127777
- var formatSessions = (rows, now, t) => {
127778
- if (rows.length === 0) return t("sessions.empty");
127807
+ var formatSessions = (rows, now, t, options = {}) => {
127808
+ if (rows.length === 0) return options.empty ?? t("sessions.empty");
127779
127809
  const shown = rows.slice(0, SESSION_LIST_MAX);
127780
127810
  return [
127781
- t("sessions.title", { count: rows.length }),
127811
+ options.title ?? t("sessions.title", { count: rows.length }),
127782
127812
  ...shown.map((row, index) => sessionLine(row, index + 1, now, t)),
127783
127813
  ...rows.length > shown.length ? [t("sessions.more", { count: rows.length - shown.length })] : [],
127784
127814
  "",
127785
- t("sessions.footer")
127815
+ options.footer ?? t("sessions.footer")
127786
127816
  ].join("\n");
127787
127817
  };
127818
+ var formatProjectSessions = (rows, now, t, project) => formatSessions(rows, now, t, {
127819
+ title: t("sessions.otherProjectTitle", { project, count: rows.length }),
127820
+ empty: `${t("sessions.project", { title: project })}
127821
+ ${t("sessions.projectEmpty")}`,
127822
+ footer: t("sessions.otherProjectFooter")
127823
+ });
127788
127824
  var listedThreadIds = (rows) => rows.slice(0, SESSION_LIST_MAX).map((row) => row.threadId);
127789
127825
  var looksLikeListNumber = (wanted) => /^[1-9]\d*$/u.test(wanted) && wanted.length <= String(SESSION_LIST_MAX).length;
127790
- var pickSession = (arg, listed, rows) => {
127826
+ var matchSession = (arg, listed, rows) => {
127791
127827
  const wanted = arg.trim();
127792
- if (wanted === "") return null;
127828
+ if (wanted === "") return { kind: "notFound" };
127793
127829
  if (looksLikeListNumber(wanted)) {
127794
127830
  const ids = listed ?? listedThreadIds(rows);
127795
- return ids[Number(wanted) - 1] ?? null;
127831
+ const threadId2 = ids[Number(wanted) - 1];
127832
+ return threadId2 === void 0 ? { kind: "notFound" } : { kind: "unique", threadId: threadId2 };
127796
127833
  }
127797
127834
  const needle = wanted.toLowerCase();
127798
127835
  const matches = rows.filter((row) => row.threadId.toLowerCase().startsWith(needle));
127799
- return matches.length === 1 ? matches[0]?.threadId ?? null : null;
127836
+ if (matches.length > 1) return { kind: "ambiguous" };
127837
+ const threadId = matches[0]?.threadId;
127838
+ return threadId === void 0 ? { kind: "notFound" } : { kind: "unique", threadId };
127839
+ };
127840
+ var pickSession = (arg, listed, rows) => {
127841
+ const match5 = matchSession(arg, listed, rows);
127842
+ return match5.kind === "unique" ? match5.threadId : null;
127800
127843
  };
127801
127844
 
127802
127845
  // src/chatSetup.ts
@@ -133162,59 +133205,31 @@ var SimpleChatCommand = Struct({
133162
133205
  "help",
133163
133206
  "panel",
133164
133207
  "selectTeam",
133165
- "selectThread",
133166
- "sessions"
133208
+ "selectThread"
133167
133209
  ])
133168
133210
  });
133169
133211
  var PlanCommandMode = Literals(["toggle", "on", "off", "invalid"]);
133170
133212
  var ParsedChatCommand = Union2([
133171
133213
  SimpleChatCommand,
133214
+ Struct({ kind: Literal2("sessions"), arg: optionalKey2(String4) }),
133172
133215
  Struct({ kind: Literals(["resume", "model", "repo"]), arg: String4 }),
133173
133216
  Struct({ kind: Literal2("plan"), mode: PlanCommandMode }),
133174
133217
  Struct({ kind: Literal2("group"), name: String4 })
133175
133218
  ]);
133176
- var teamArgument = (text3, chinese) => {
133177
- const match5 = text3.match(/^\/team(?:\s+([\s\S]*))?$/i);
133219
+ var commandArgument = (text3, chinese, english, aliasPattern) => {
133220
+ const match5 = text3.match(english);
133178
133221
  if (match5) return (match5[1] ?? "").trim();
133179
133222
  if (!chinese) return void 0;
133180
- const alias = text3.match(/^换团队(?:\s+([\s\S]*))?$/);
133181
- return alias ? (alias[1] ?? "").trim() : void 0;
133182
- };
133183
- var resumeArgument = (text3, chinese) => {
133184
- const match5 = text3.match(/^\/resume(?:\s+([\s\S]*))?$/i);
133185
- if (match5) return (match5[1] ?? "").trim();
133186
- if (!chinese) return void 0;
133187
- const alias = text3.match(/^切换会话(?:\s+([\s\S]*))?$/);
133188
- return alias ? (alias[1] ?? "").trim() : void 0;
133189
- };
133190
- var modelArgument = (text3, chinese) => {
133191
- const match5 = text3.match(/^\/model(?:\s+([\s\S]*))?$/i);
133192
- if (match5) return (match5[1] ?? "").trim();
133193
- if (!chinese) return void 0;
133194
- const alias = text3.match(/^换模型(?:\s+([\s\S]*))?$/);
133195
- return alias ? (alias[1] ?? "").trim() : void 0;
133196
- };
133197
- var planArgument = (text3, chinese) => {
133198
- const match5 = text3.match(/^\/plan(?:\s+([\s\S]*))?$/i);
133199
- if (match5) return (match5[1] ?? "").trim().toLowerCase();
133200
- if (!chinese) return void 0;
133201
- const alias = text3.match(/^计划模式(?:\s+([\s\S]*))?$/);
133202
- return alias ? (alias[1] ?? "").trim().toLowerCase() : void 0;
133203
- };
133204
- var repoArgument = (text3, chinese) => {
133205
- const match5 = text3.match(/^\/repo(?:\s+([\s\S]*))?$/i);
133206
- if (match5) return (match5[1] ?? "").trim();
133207
- if (!chinese) return void 0;
133208
- const alias = text3.match(/^换项目(?:\s+([\s\S]*))?$/);
133209
- return alias ? (alias[1] ?? "").trim() : void 0;
133210
- };
133211
- var groupArgument = (text3, chinese) => {
133212
- const match5 = text3.match(/^\/group(?:\s+([\s\S]*))?$/i);
133213
- if (match5) return (match5[1] ?? "").trim();
133214
- if (!chinese) return void 0;
133215
- const alias = text3.match(/^建群(?:\s+([\s\S]*))?$/);
133223
+ const alias = text3.match(aliasPattern);
133216
133224
  return alias ? (alias[1] ?? "").trim() : void 0;
133217
133225
  };
133226
+ var teamArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/team(?:\s+([\s\S]*))?$/i, /^换团队(?:\s+([\s\S]*))?$/);
133227
+ var sessionsArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/sessions(?:\s+([\s\S]*))?$/i, /^会话列表(?:\s+([\s\S]*))?$/);
133228
+ var resumeArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/resume(?:\s+([\s\S]*))?$/i, /^切换会话(?:\s+([\s\S]*))?$/);
133229
+ var modelArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/model(?:\s+([\s\S]*))?$/i, /^换模型(?:\s+([\s\S]*))?$/);
133230
+ var planArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/plan(?:\s+([\s\S]*))?$/i, /^计划模式(?:\s+([\s\S]*))?$/);
133231
+ var repoArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/repo(?:\s+([\s\S]*))?$/i, /^换项目(?:\s+([\s\S]*))?$/);
133232
+ var groupArgument = (text3, chinese) => commandArgument(text3, chinese, /^\/group(?:\s+([\s\S]*))?$/i, /^建群(?:\s+([\s\S]*))?$/);
133218
133233
  var planModeOf = (argument, chinese) => {
133219
133234
  if (argument === "") return "toggle";
133220
133235
  if (argument === "on" || chinese && argument === "\u5F00") return "on";
@@ -133230,8 +133245,7 @@ var ZH_ALIASES = {
133230
133245
  \u72B6\u6001: "status",
133231
133246
  \u65B0\u5BF9\u8BDD: "new",
133232
133247
  \u5E2E\u52A9: "help",
133233
- \u9762\u677F: "panel",
133234
- \u4F1A\u8BDD\u5217\u8868: "sessions"
133248
+ \u9762\u677F: "panel"
133235
133249
  };
133236
133250
  var parseChatCommand = (input) => {
133237
133251
  const trimmed = input.text.trim();
@@ -133241,7 +133255,10 @@ var parseChatCommand = (input) => {
133241
133255
  if (/^\/new$/i.test(trimmed)) return { kind: "new" };
133242
133256
  if (/^\/help$/i.test(trimmed)) return { kind: "help" };
133243
133257
  if (/^\/panel$/i.test(trimmed)) return { kind: "panel" };
133244
- if (/^\/sessions$/i.test(trimmed)) return { kind: "sessions" };
133258
+ const sessions = sessionsArgument(trimmed, input.chinese);
133259
+ if (sessions !== void 0) {
133260
+ return sessions === "" ? { kind: "sessions" } : { kind: "sessions", arg: sessions };
133261
+ }
133245
133262
  const resume = resumeArgument(input.text, input.chinese);
133246
133263
  if (resume !== void 0) {
133247
133264
  return resume === "" ? { kind: "sessions" } : { kind: "resume", arg: resume };
@@ -133249,7 +133266,8 @@ var parseChatCommand = (input) => {
133249
133266
  const model = modelArgument(input.text, input.chinese);
133250
133267
  if (model !== void 0) return { kind: "model", arg: model };
133251
133268
  const plan = planArgument(input.text, input.chinese);
133252
- if (plan !== void 0) return { kind: "plan", mode: planModeOf(plan, input.chinese) };
133269
+ if (plan !== void 0)
133270
+ return { kind: "plan", mode: planModeOf(plan.toLowerCase(), input.chinese) };
133253
133271
  const repo = repoArgument(input.text, input.chinese);
133254
133272
  if (repo !== void 0) return { kind: "repo", arg: repo };
133255
133273
  const group = groupArgument(input.text, input.chinese);
@@ -133397,7 +133415,7 @@ var makeCommands = (core, deps) => {
133397
133415
  await deliver(binding, helpText(t, { panel: false }));
133398
133416
  return;
133399
133417
  case "sessions":
133400
- await showSessions(binding);
133418
+ await showSessions(binding, command.arg);
133401
133419
  return;
133402
133420
  case "resume":
133403
133421
  await resumeSession(binding, command.arg);
@@ -134723,7 +134741,15 @@ var OUTBOUND_QUEUE_LIMIT = 100;
134723
134741
  var makeImCore = (input) => {
134724
134742
  const { host, t, adapters } = input;
134725
134743
  const adapterFor = (platform) => adapters.find((adapter) => adapter.platform === platform);
134726
- let state = emptyState();
134744
+ let confirmed = emptyState();
134745
+ let state = confirmed;
134746
+ const pending = [];
134747
+ const rebuildState = () => {
134748
+ state = pending.reduce(
134749
+ (current, event) => foldImEvent(current, event.name, event.data),
134750
+ confirmed
134751
+ );
134752
+ };
134727
134753
  const stateHandle = input.derivedState({
134728
134754
  key: IM_STATE_KEY,
134729
134755
  version: IM_STATE_VERSION,
@@ -134733,18 +134759,30 @@ var makeImCore = (input) => {
134733
134759
  });
134734
134760
  const ready = stateHandle.get().then(
134735
134761
  (restored) => {
134736
- state = restored;
134762
+ confirmed = restored;
134763
+ rebuildState();
134737
134764
  },
134738
134765
  (error) => {
134739
134766
  host.log.warn("chat could not restore its saved state", { error: describeError(error) });
134740
134767
  }
134741
134768
  );
134742
- let persisting = Promise.resolve();
134769
+ let persisting = ready;
134743
134770
  const commitDurably = (name, data) => {
134771
+ pending.push({ name, data });
134744
134772
  state = foldImEvent(state, name, data);
134745
134773
  const saved = persisting.then(
134746
134774
  () => host.events.append({ name, aggregate: { kind: "plugin", id: input.pluginId }, data })
134747
- ).then(() => void 0);
134775
+ ).then(
134776
+ () => {
134777
+ confirmed = foldImEvent(confirmed, name, data);
134778
+ pending.shift();
134779
+ },
134780
+ (error) => {
134781
+ pending.shift();
134782
+ rebuildState();
134783
+ throw error;
134784
+ }
134785
+ );
134748
134786
  persisting = saved.catch((error) => {
134749
134787
  host.log.warn("chat could not save its state", { name, error: describeError(error) });
134750
134788
  });
@@ -138420,6 +138458,35 @@ var registerWecomSetup = (ctx, options = {}) => {
138420
138458
  });
138421
138459
  };
138422
138460
 
138461
+ // src/larkBotColumns.ts
138462
+ var larkBotColumns = [
138463
+ {
138464
+ key: "larkAppId",
138465
+ group: "essential",
138466
+ kind: "text",
138467
+ labelKey: "column.larkAppId.label",
138468
+ placeholderKey: "column.larkAppId.placeholder"
138469
+ },
138470
+ {
138471
+ key: "larkAppSecret",
138472
+ group: "essential",
138473
+ kind: "password",
138474
+ labelKey: "column.larkAppSecret.label"
138475
+ },
138476
+ // 被 @ 时答复要不要开话题。只有飞书适配器读它,没填过就是开(见 larkAdapter.ts 的 `topicOnMentionOf`)。
138477
+ {
138478
+ key: "larkTopicOnMention",
138479
+ group: "more",
138480
+ kind: "select",
138481
+ labelKey: "column.larkTopicOnMention.label",
138482
+ descriptionKey: "column.larkTopicOnMention.description",
138483
+ options: [
138484
+ { value: "on", labelKey: "settings.onOff.on" },
138485
+ { value: "off", labelKey: "settings.onOff.off" }
138486
+ ]
138487
+ }
138488
+ ];
138489
+
138423
138490
  // src/larkApi.ts
138424
138491
  import { parseRetryAfterMs } from "@ycodium-ai/plugin-api";
138425
138492
 
@@ -145581,8 +145648,8 @@ var en_default = {
145581
145648
  team: "\xB7 /team \u2014 give this project's team a goal with each message; use /repo to pick the project first",
145582
145649
  thread: "\xB7 /thread \u2014 go back to a normal session",
145583
145650
  aliases: "Chinese shortcuts (\u505C / \u7EE7\u7EED\u53D1\u9001 / \u72B6\u6001 / \u65B0\u5BF9\u8BDD / \u4F1A\u8BDD\u5217\u8868 / \u5207\u6362\u4F1A\u8BDD / \u6362\u6A21\u578B / \u8BA1\u5212\u6A21\u5F0F / \u6362\u9879\u76EE / \u5EFA\u7FA4 / \u6362\u56E2\u961F / \u6362\u5BF9\u8BDD) work when the reply language is Chinese.",
145584
- sessions: "\xB7 /sessions \u2014 list the sessions this bot opened for this chat's project",
145585
- resume: "\xB7 /resume number \u2014 switch to one of them (a number from /sessions, or the start of its id)",
145651
+ sessions: "\xB7 /sessions \u2014 list this project's sessions; add a project number from /repo to list the ones this bot opened in another project",
145652
+ resume: "\xB7 /resume number-or-id-prefix \u2014 switch sessions; one from another project switches the project too",
145586
145653
  model: "\xB7 /model \u2014 see the model this chat uses; /model number or /model instance/model switches it",
145587
145654
  plan: "\xB7 /plan \u2014 toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it",
145588
145655
  panel: "Send /panel to open the command panel with a button for every command.",
@@ -145591,12 +145658,18 @@ var en_default = {
145591
145658
  syncNote: "\xB7 After a private chat attaches a session, web user text and final assistant text sync here (groups never sync)."
145592
145659
  },
145593
145660
  sessions: {
145594
- title: "Sessions this bot opened for this chat's project ({{count}}):",
145595
- empty: "This bot has not opened any session for this chat's project yet. Send a message to start one.",
145661
+ title: "This project's sessions ({{count}}):",
145662
+ empty: "This bot has not opened any session in this project yet. Send a message to start one.",
145596
145663
  more: "{{count}} more not shown \u2014 reach them with /resume and the start of an id.",
145597
145664
  footer: "Switch with /resume number, or /resume and the start of an id.",
145598
145665
  untitled: "Untitled",
145599
145666
  unavailable: "Could not read the session list right now. Try again in a moment.",
145667
+ projectNotFound: "That project number does not exist. Send /repo to see the numbers.",
145668
+ projectNumberExpected: "Put a project number after /sessions (a number from /repo), for example /sessions 2.",
145669
+ project: 'Project: "{{title}}"',
145670
+ otherProjectTitle: 'Sessions this bot opened in project "{{project}}" ({{count}}):',
145671
+ otherProjectFooter: "You are still in this chat's current project. /resume number, or /resume with the start of an id (also an unlisted one this bot opened), switches there and switches the chat's project too.",
145672
+ projectEmpty: "This bot has not opened any session in that project yet. To start one there, send /repo with that project number to move this chat there, then send a message.",
145600
145673
  badge: {
145601
145674
  current: "\u2605 current",
145602
145675
  running: "\u25CF running",
@@ -145612,11 +145685,14 @@ var en_default = {
145612
145685
  },
145613
145686
  resume: {
145614
145687
  notFound: 'No session matches "{{query}}". Use /sessions to see the list.',
145688
+ ambiguous: 'More than one session starts with "{{query}}". Give a few more characters of the id.',
145615
145689
  busy: "A turn is running in this chat \u2014 send /stop first, then switch.",
145616
145690
  already: "This chat is already on that session.",
145617
145691
  gone: "That session was deleted or archived, so it cannot be resumed.",
145618
145692
  runningElsewhere: "That session is running a turn in another chat \u2014 stop it there first.",
145619
145693
  done: 'Switched to "{{title}}". Send a message to keep going from there.',
145694
+ doneInProject: 'Switched to project "{{project}}", session "{{title}}". Send a message to keep going from there.',
145695
+ switchFailed: "Could not switch to that session. The chat's project and session have not changed; send that /resume again in a moment.",
145620
145696
  confirmWeb: '"{{title}}" was opened on the web. Send /resume with the same number again (or /resume confirm) to attach; /resume cancel to abort.',
145621
145697
  cancelled: "Attach cancelled.",
145622
145698
  nothingPending: "No web session is waiting for confirm. Use /sessions to see the list."
@@ -145649,6 +145725,8 @@ var en_default = {
145649
145725
  interrupted: "Interrupted",
145650
145726
  continued: "Continues in the card below"
145651
145727
  },
145728
+ thinking: "Thinking\u2026",
145729
+ usingTool: "Using {{name}}\u2026",
145652
145730
  step: {
145653
145731
  think: "think",
145654
145732
  command: "command",
@@ -145981,11 +146059,14 @@ var en_default = {
145981
146059
  reference: "Reference {{referenceId}}"
145982
146060
  },
145983
146061
  empty: "No bots yet",
146062
+ emptyGuide: `Use "{{larkLabel}}" or "{{wecomLabel}}" to scan and create a bot in one step. For other platforms, choose "{{addBotLabel}}" and enter the credentials from that platform's developer console.`,
145984
146063
  unavailable: "This machine doesn't have the chat bridge right now.",
145985
146064
  loadFailed: "Couldn't load the chat-bridge settings.",
145986
146065
  retry: "Try again",
145987
146066
  general: "General",
145988
146067
  addBot: "Add a bot",
146068
+ moreSettings: "More settings",
146069
+ moreSettingsIssues: "More settings ({{count}} to fix)",
145989
146070
  save: "Save",
145990
146071
  revert: "Undo changes",
145991
146072
  saveFailed: "Couldn't save.",
@@ -146422,8 +146503,8 @@ var zh_CN_default = {
146422
146503
  team: "\xB7 /team \u2014\u2014 \u6BCF\u6761\u6D88\u606F\u90FD\u7ED9\u8FD9\u4E2A\u9879\u76EE\u7684\u56E2\u961F\u5F00\u4E00\u4E2A\u76EE\u6807\uFF1B\u5148\u7528 /repo \u9009\u9879\u76EE",
146423
146504
  thread: "\xB7 /thread \u2014\u2014 \u5207\u56DE\u666E\u901A\u4F1A\u8BDD",
146424
146505
  aliases: "\u4E2D\u6587\u4E5F\u884C\uFF1A\u505C / \u7EE7\u7EED\u53D1\u9001 / \u72B6\u6001 / \u65B0\u5BF9\u8BDD / \u4F1A\u8BDD\u5217\u8868 / \u5207\u6362\u4F1A\u8BDD / \u6362\u6A21\u578B / \u8BA1\u5212\u6A21\u5F0F / \u6362\u9879\u76EE / \u5EFA\u7FA4 / \u6362\u56E2\u961F / \u6362\u5BF9\u8BDD\u3002",
146425
- sessions: "\xB7 /sessions \u2014\u2014 \u5217\u51FA\u672C\u673A\u5668\u4EBA\u4E3A\u8FD9\u4E2A\u804A\u5929\u7684\u9879\u76EE\u5F00\u8FC7\u7684\u4F1A\u8BDD",
146426
- resume: "\xB7 /resume \u7F16\u53F7 \u2014\u2014 \u5207\u5230\u5176\u4E2D\u4E00\u6761\uFF08/sessions \u91CC\u7684\u7F16\u53F7\uFF0C\u6216\u4F1A\u8BDD id \u7684\u5F00\u5934\uFF09",
146506
+ sessions: "\xB7 /sessions \u2014\u2014 \u5217\u51FA\u672C\u9879\u76EE\u7684\u4F1A\u8BDD\uFF1B\u52A0 /repo \u91CC\u7684\u9879\u76EE\u7F16\u53F7\uFF0C\u5217\u51FA\u672C\u673A\u5668\u4EBA\u5728\u522B\u7684\u9879\u76EE\u5F00\u8FC7\u7684",
146507
+ resume: "\xB7 /resume \u7F16\u53F7\u6216 id \u5F00\u5934 \u2014\u2014 \u5207\u4F1A\u8BDD\uFF1B\u522B\u7684\u9879\u76EE\u7684\u4F1A\u8BDD\u4F1A\u8FDE\u540C\u9879\u76EE\u4E00\u8D77\u5207",
146427
146508
  model: "\xB7 /model \u2014\u2014 \u770B\u8FD9\u4E2A\u804A\u5929\u7528\u7684\u6A21\u578B\uFF1B/model \u7F16\u53F7 \u6216 /model \u5B9E\u4F8B/\u6A21\u578B \u5207\u6362",
146428
146509
  plan: "\xB7 /plan \u2014\u2014 \u5F00\u5173\u8BA1\u5212\u6A21\u5F0F\uFF08agent \u5148\u51FA\u8BA1\u5212\u3001\u4E0D\u52A8\u624B\uFF09\uFF1B/plan on\u3001/plan off \u660E\u786E\u8BBE\u5B9A",
146429
146510
  panel: "\u53D1 /panel \u6253\u5F00\u53EF\u70B9\u7684\u547D\u4EE4\u9762\u677F\u3002",
@@ -146432,12 +146513,18 @@ var zh_CN_default = {
146432
146513
  syncNote: "\xB7 \u79C1\u804A\u9644\u7740\u4F1A\u8BDD\u540E\uFF0C\u7F51\u9875\u4E0A\u7684\u7528\u6237\u6587\u5B57\u4E0E\u7EC8\u6001\u52A9\u624B\u6587\u5B57\u4F1A\u540C\u6B65\u5230\u6B64\uFF08\u7FA4\u804A\u4E0D\u540C\u6B65\uFF09\u3002"
146433
146514
  },
146434
146515
  sessions: {
146435
- title: "\u672C\u673A\u5668\u4EBA\u4E3A\u8FD9\u4E2A\u804A\u5929\u7684\u9879\u76EE\u5F00\u8FC7\u7684\u4F1A\u8BDD\uFF08{{count}} \u6761\uFF09\uFF1A",
146436
- empty: "\u672C\u673A\u5668\u4EBA\u8FD8\u6CA1\u6709\u4E3A\u8FD9\u4E2A\u804A\u5929\u7684\u9879\u76EE\u5F00\u8FC7\u4F1A\u8BDD\uFF0C\u53D1\u4E00\u53E5\u8BDD\u5C31\u4F1A\u5F00\u4E00\u6761\u3002",
146516
+ title: "\u672C\u9879\u76EE\u7684\u4F1A\u8BDD\uFF08{{count}} \u6761\uFF09\uFF1A",
146517
+ empty: "\u672C\u673A\u5668\u4EBA\u8FD8\u6CA1\u5728\u672C\u9879\u76EE\u5F00\u8FC7\u4F1A\u8BDD\uFF0C\u53D1\u4E00\u53E5\u8BDD\u5C31\u4F1A\u5F00\u4E00\u6761\u3002",
146437
146518
  more: "\u8FD8\u6709 {{count}} \u6761\u6CA1\u5217\u51FA\u2014\u2014\u7528 /resume \u52A0\u4F1A\u8BDD id \u7684\u5F00\u5934\u4E5F\u80FD\u5207\u5230\u3002",
146438
146519
  footer: "\u7528 /resume \u7F16\u53F7 \u5207\u6362\uFF0C\u6216 /resume \u52A0\u4F1A\u8BDD id \u7684\u5F00\u5934\u3002",
146439
146520
  untitled: "\uFF08\u65E0\u6807\u9898\uFF09",
146440
146521
  unavailable: "\u73B0\u5728\u8BFB\u4E0D\u51FA\u4F1A\u8BDD\u5217\u8868\uFF0C\u7A0D\u540E\u518D\u8BD5\u3002",
146522
+ projectNotFound: "\u9879\u76EE\u7F16\u53F7\u4E0D\u5B58\u5728\u3002\u53D1 /repo \u770B\u6709\u54EA\u4E9B\u7F16\u53F7\u3002",
146523
+ projectNumberExpected: "/sessions \u540E\u9762\u8DDF\u9879\u76EE\u7F16\u53F7\uFF08/repo \u5217\u51FA\u7684\u6570\u5B57\uFF09\uFF0C\u4F8B\u5982 /sessions 2\u3002",
146524
+ project: "\u9879\u76EE\uFF1A\u300C{{title}}\u300D",
146525
+ otherProjectTitle: "\u672C\u673A\u5668\u4EBA\u5728\u9879\u76EE\u300C{{project}}\u300D\u91CC\u5F00\u8FC7\u7684\u4F1A\u8BDD\uFF08{{count}} \u6761\uFF09\uFF1A",
146526
+ otherProjectFooter: "\u4F60\u4ECD\u5728\u8FD9\u4E2A\u804A\u5929\u5F53\u524D\u7684\u9879\u76EE\u91CC\u3002\u53D1 /resume \u7F16\u53F7\uFF0C\u6216 /resume \u52A0\u4F1A\u8BDD id \u7684\u5F00\u5934\uFF08\u672C\u673A\u5668\u4EBA\u5F00\u8FC7\u4F46\u6CA1\u5217\u51FA\u7684\u4E5F\u884C\uFF09\uFF0C\u5C31\u80FD\u5207\u8FC7\u53BB\uFF0C\u804A\u5929\u7684\u9879\u76EE\u4F1A\u4E00\u8D77\u5207\u3002",
146527
+ projectEmpty: "\u672C\u673A\u5668\u4EBA\u8FD8\u6CA1\u5728\u90A3\u4E2A\u9879\u76EE\u91CC\u5F00\u8FC7\u4F1A\u8BDD\u3002\u60F3\u5728\u90A3\u91CC\u5F00\uFF0C\u53D1 /repo \u52A0\u90A3\u4E2A\u9879\u76EE\u7F16\u53F7\u6362\u8FC7\u53BB\uFF0C\u518D\u53D1\u4E00\u53E5\u8BDD\u3002",
146441
146528
  badge: {
146442
146529
  current: "\u2605 \u5F53\u524D",
146443
146530
  running: "\u25CF \u5728\u8DD1",
@@ -146453,11 +146540,14 @@ var zh_CN_default = {
146453
146540
  },
146454
146541
  resume: {
146455
146542
  notFound: "\u6CA1\u6709\u54EA\u6761\u4F1A\u8BDD\u5BF9\u5F97\u4E0A\u300C{{query}}\u300D\u3002\u7528 /sessions \u770B\u770B\u6709\u54EA\u4E9B\u3002",
146543
+ ambiguous: "\u5BF9\u5F97\u4E0A\u300C{{query}}\u300D\u7684\u4F1A\u8BDD\u4E0D\u6B62\u4E00\u6761\uFF0Cid \u5F00\u5934\u518D\u591A\u7ED9\u51E0\u4F4D\u3002",
146456
146544
  busy: "\u8FD9\u4E2A\u804A\u5929\u6709\u4E00\u8F6E\u6B63\u5728\u8DD1\u2014\u2014\u5148 /stop\uFF0C\u518D\u5207\u6362\u3002",
146457
146545
  already: "\u8FD9\u4E2A\u804A\u5929\u5DF2\u7ECF\u5728\u90A3\u6761\u4F1A\u8BDD\u91CC\u4E86\u3002",
146458
146546
  gone: "\u90A3\u6761\u4F1A\u8BDD\u5DF2\u88AB\u5220\u9664\u6216\u5F52\u6863\uFF0C\u63A5\u4E0D\u4E0A\u4E86\u3002",
146459
146547
  runningElsewhere: "\u90A3\u6761\u4F1A\u8BDD\u6B63\u5728\u522B\u7684\u804A\u5929\u91CC\u8DD1\u7740\u4E00\u8F6E\u2014\u2014\u5148\u5230\u90A3\u8FB9\u505C\u6389\u5B83\u3002",
146460
146548
  done: "\u5DF2\u5207\u5230\u300C{{title}}\u300D\u3002\u53D1\u4E00\u53E5\u8BDD\u5C31\u4ECE\u90A3\u91CC\u63A5\u7740\u804A\u3002",
146549
+ doneInProject: "\u5DF2\u5207\u5230\u9879\u76EE\u300C{{project}}\u300D\u7684\u4F1A\u8BDD\u300C{{title}}\u300D\u3002\u53D1\u4E00\u53E5\u8BDD\u5C31\u4ECE\u90A3\u91CC\u63A5\u7740\u804A\u3002",
146550
+ switchFailed: "\u6CA1\u80FD\u5207\u5230\u90A3\u6761\u4F1A\u8BDD\uFF0C\u804A\u5929\u7684\u9879\u76EE\u548C\u4F1A\u8BDD\u90FD\u6CA1\u53D8\uFF0C\u7A0D\u540E\u91CD\u53D1\u521A\u624D\u90A3\u6761 /resume\u3002",
146461
146551
  confirmWeb: "\u300C{{title}}\u300D\u662F\u7F51\u9875\u5F00\u7684\u4F1A\u8BDD\u3002\u518D\u53D1\u4E00\u6B21 /resume \u540C\u4E00\u7F16\u53F7\uFF08\u6216 /resume confirm\uFF09\u786E\u8BA4\u9644\u7740\uFF1B/resume cancel \u53D6\u6D88\u3002",
146462
146552
  cancelled: "\u5DF2\u53D6\u6D88\u9644\u7740\u3002",
146463
146553
  nothingPending: "\u6CA1\u6709\u5F85\u786E\u8BA4\u7684\u7F51\u9875\u4F1A\u8BDD\u3002\u7528 /sessions \u770B\u770B\u6709\u54EA\u4E9B\u3002"
@@ -146490,6 +146580,8 @@ var zh_CN_default = {
146490
146580
  interrupted: "\u5DF2\u4E2D\u65AD",
146491
146581
  continued: "\u540E\u7EED\u89C1\u4E0B\u65B9\u5361\u7247"
146492
146582
  },
146583
+ thinking: "\u6B63\u5728\u601D\u8003\u4E2D\u2026",
146584
+ usingTool: "\u6B63\u5728\u4F7F\u7528 {{name}}\u2026",
146493
146585
  step: {
146494
146586
  think: "\u601D\u8003",
146495
146587
  command: "\u547D\u4EE4",
@@ -146822,11 +146914,14 @@ var zh_CN_default = {
146822
146914
  reference: "\u53C2\u8003\u53F7 {{referenceId}}"
146823
146915
  },
146824
146916
  empty: "\u8FD8\u6CA1\u6709\u673A\u5668\u4EBA",
146917
+ emptyGuide: "\u7528\u300C{{larkLabel}}\u300D\u6216\u300C{{wecomLabel}}\u300D\u626B\u4E00\u4E0B\u5C31\u80FD\u5EFA\u597D\u673A\u5668\u4EBA\u3002\u5176\u4ED6\u5E73\u53F0\u70B9\u300C{{addBotLabel}}\u300D\uFF0C\u586B\u5165\u5728\u8BE5\u5E73\u53F0\u5F00\u53D1\u8005\u540E\u53F0\u62FF\u5230\u7684\u51ED\u636E\u3002",
146825
146918
  unavailable: "\u8FD9\u53F0\u673A\u5668\u73B0\u5728\u6CA1\u6709\u804A\u5929\u6865\u3002",
146826
146919
  loadFailed: "\u804A\u5929\u6865\u7684\u8BBE\u7F6E\u6CA1\u80FD\u8BFB\u51FA\u6765\u3002",
146827
146920
  retry: "\u518D\u8BD5\u4E00\u6B21",
146828
146921
  general: "\u901A\u7528\u8BBE\u7F6E",
146829
146922
  addBot: "\u6DFB\u52A0\u673A\u5668\u4EBA",
146923
+ moreSettings: "\u66F4\u591A\u8BBE\u7F6E",
146924
+ moreSettingsIssues: "\u66F4\u591A\u8BBE\u7F6E\uFF08{{count}} \u9879\u5F85\u4FEE\u6B63\uFF09",
146830
146925
  save: "\u4FDD\u5B58",
146831
146926
  revert: "\u64A4\u9500\u6539\u52A8",
146832
146927
  saveFailed: "\u6CA1\u80FD\u5B58\u4E0A\u3002",
@@ -147297,26 +147392,6 @@ var makeLarkSlashCommands = (deps) => {
147297
147392
 
147298
147393
  // src/larkAdapter.ts
147299
147394
  var TOKEN_CACHE_MAX_AGE_MS = 30 * 6e4;
147300
- var COLUMNS = [
147301
- {
147302
- key: "larkAppId",
147303
- kind: "text",
147304
- labelKey: "column.larkAppId.label",
147305
- placeholderKey: "column.larkAppId.placeholder"
147306
- },
147307
- { key: "larkAppSecret", kind: "password", labelKey: "column.larkAppSecret.label" },
147308
- // 被 @ 时答复要不要开话题。只有这个适配器读它,没填过就是开(见 `topicOnMentionOf`)。
147309
- {
147310
- key: "larkTopicOnMention",
147311
- kind: "select",
147312
- labelKey: "column.larkTopicOnMention.label",
147313
- descriptionKey: "column.larkTopicOnMention.description",
147314
- options: [
147315
- { value: "on", labelKey: "settings.onOff.on" },
147316
- { value: "off", labelKey: "settings.onOff.off" }
147317
- ]
147318
- }
147319
- ];
147320
147395
  var topicOnMentionOf = (binding) => binding.values["larkTopicOnMention"] !== "off";
147321
147396
  var TOPIC_THREAD_CACHE_LIMIT = 2e3;
147322
147397
  var MESSAGE_CONTEXT_CACHE_LIMIT = 2e3;
@@ -147413,7 +147488,7 @@ var makeLarkAdapter = (deps) => {
147413
147488
  botIdentity: (bot) => larkCredentials(bot).appId,
147414
147489
  icon: "lark",
147415
147490
  label: "Lark",
147416
- columns: COLUMNS,
147491
+ columns: larkBotColumns,
147417
147492
  unlistedValues: ["larkPanelMode"],
147418
147493
  quotesReplies: true,
147419
147494
  parentChatIdOf: (chatId) => {
@@ -149327,8 +149402,8 @@ var shellOf = (thread) => ({
149327
149402
  archivedAt: thread.archivedAt,
149328
149403
  sessionStatus: thread.session?.status ?? null
149329
149404
  });
149330
- var makeSessions = (core) => {
149331
- const { host, t, commit, bindChat, deliver } = core;
149405
+ var makeSessions = (core, projects = makeProjectRows(core)) => {
149406
+ const { host, t, commit, deliver } = core;
149332
149407
  const listedByChat = /* @__PURE__ */ new Map();
149333
149408
  const pendingAttachByChat = /* @__PURE__ */ new Map();
149334
149409
  const rowFromThread = (thread, chatKey, origin) => {
@@ -149369,27 +149444,112 @@ var makeSessions = (core) => {
149369
149444
  return shells;
149370
149445
  };
149371
149446
  const originOf = (threadId, projectId) => core.state.opened[threadId]?.projectId === projectId ? "plugin" : "web";
149372
- const sessionRowsFor = async (binding) => {
149447
+ const isPluginThread = (threadId, projectId) => originOf(threadId, projectId) === "plugin";
149448
+ const sessionRowsFor = async (binding, projectId = binding.projectId, pluginOnly = false) => {
149373
149449
  const chatKey = chatKeyOf(binding);
149374
- const shells = await projectShells(binding.projectId);
149450
+ const shells = await projectShells(projectId);
149375
149451
  return shells.flatMap((shell) => {
149376
- const row = rowFromThread(shell, chatKey, originOf(shell.threadId, binding.projectId));
149452
+ if (pluginOnly && !isPluginThread(shell.threadId, projectId)) return [];
149453
+ const row = rowFromThread(shell, chatKey, originOf(shell.threadId, projectId));
149377
149454
  return row === null ? [] : [row];
149378
149455
  });
149379
149456
  };
149380
149457
  const rowForThread = async (binding, chatKey, threadId) => {
149381
149458
  const thread = await threadOrNull7(host.threads, threadId, { archived: "include" });
149382
- if (thread === null || thread.projectId !== binding.projectId) return null;
149383
- return rowFromThread(shellOf(thread), chatKey, originOf(threadId, binding.projectId));
149459
+ if (thread === null) return null;
149460
+ if (thread.projectId !== binding.projectId && !isPluginThread(threadId, thread.projectId)) {
149461
+ return null;
149462
+ }
149463
+ const row = rowFromThread(shellOf(thread), chatKey, originOf(threadId, thread.projectId));
149464
+ return row === null ? null : { ...row, projectId: thread.projectId };
149384
149465
  };
149385
- const showSessions = async (binding) => {
149386
- const rows = await sessionRowsFor(binding);
149387
- listedByChat.set(chatKeyOf(binding), listedThreadIds(rows));
149388
- await deliver(binding, formatSessions(rows, Date.now(), t));
149466
+ const resumeRowsFor = async (binding, wanted, listed) => {
149467
+ if (looksLikeListNumber(wanted)) return listed === void 0 ? sessionRowsFor(binding) : [];
149468
+ const needle = wanted.toLowerCase();
149469
+ const threadIds = Object.entries(core.state.opened).filter(
149470
+ ([id, opened]) => opened.projectId !== binding.projectId && id.toLowerCase().startsWith(needle)
149471
+ ).map(([id]) => id);
149472
+ const [rows, candidates] = await Promise.all([
149473
+ sessionRowsFor(binding),
149474
+ Promise.all(threadIds.map((id) => rowForThread(binding, chatKeyOf(binding), id)))
149475
+ ]);
149476
+ return [
149477
+ ...rows,
149478
+ ...candidates.flatMap(
149479
+ (row) => row === null || row.projectId === binding.projectId ? [] : [row]
149480
+ )
149481
+ ];
149482
+ };
149483
+ const showSessions = async (binding, arg = "") => {
149484
+ const chatKey = chatKeyOf(binding);
149485
+ const wanted = arg.trim();
149486
+ if (wanted === "") {
149487
+ const rows = await sessionRowsFor(binding);
149488
+ listedByChat.set(chatKey, listedThreadIds(rows));
149489
+ await deliver(binding, formatSessions(rows, Date.now(), t));
149490
+ return;
149491
+ }
149492
+ if (!isProjectNumber(wanted)) {
149493
+ await deliver(binding, t("sessions.projectNumberExpected"));
149494
+ return;
149495
+ }
149496
+ try {
149497
+ const available = await projects.projectRowsFor(chatKey);
149498
+ const picked = pickProjectNumber(
149499
+ wanted,
149500
+ projects.listedProjectsByChat.get(chatKey),
149501
+ available
149502
+ );
149503
+ if (picked === null) {
149504
+ await deliver(binding, t("sessions.projectNotFound"));
149505
+ return;
149506
+ }
149507
+ const project = available.find((row) => row.projectId === picked);
149508
+ if (project === void 0) {
149509
+ await deliver(binding, t("repo.gone"));
149510
+ return;
149511
+ }
149512
+ if (picked === binding.projectId) {
149513
+ await showSessions(binding);
149514
+ return;
149515
+ }
149516
+ const rows = await sessionRowsFor(binding, project.projectId, true);
149517
+ listedByChat.set(chatKey, listedThreadIds(rows));
149518
+ await deliver(binding, formatProjectSessions(rows, Date.now(), t, project.title));
149519
+ } catch (error) {
149520
+ host.log.warn("chat could not list the sessions", { error: describeError(error) });
149521
+ await deliver(binding, t("sessions.unavailable"));
149522
+ }
149389
149523
  };
149390
149524
  const bindToSession = async (binding, chatKey, target2) => {
149391
- for (const [otherKey, record4] of Object.entries(core.state.chats)) {
149392
- if (otherKey !== chatKey && record4.threadId === target2.threadId) bindChat(otherKey, null);
149525
+ let project;
149526
+ if (target2.projectId !== binding.projectId) {
149527
+ try {
149528
+ project = (await projects.projectRowsFor(chatKey)).find(
149529
+ (row) => row.projectId === target2.projectId
149530
+ );
149531
+ } catch (error) {
149532
+ host.log.warn("chat could not list the projects", { error: describeError(error) });
149533
+ await deliver(binding, t("resume.switchFailed"));
149534
+ return;
149535
+ }
149536
+ if (project === void 0) {
149537
+ await deliver(binding, t("repo.gone"));
149538
+ return;
149539
+ }
149540
+ }
149541
+ try {
149542
+ await core.commitDurably("session-transferred", {
149543
+ chatKey,
149544
+ projectId: target2.projectId,
149545
+ threadId: target2.threadId
149546
+ });
149547
+ } catch (error) {
149548
+ host.log.warn("chat could not switch the project and session", {
149549
+ error: describeError(error)
149550
+ });
149551
+ await deliver(binding, t("resume.switchFailed"));
149552
+ return;
149393
149553
  }
149394
149554
  if (target2.origin === "web" && !hasAttached(core.state, chatKey, target2.threadId)) {
149395
149555
  commit("thread-attached", {
@@ -149399,10 +149559,13 @@ var makeSessions = (core) => {
149399
149559
  attachedAt: (/* @__PURE__ */ new Date()).toISOString()
149400
149560
  });
149401
149561
  }
149402
- commit("chat-updated", { chatKey, threadId: target2.threadId, target: { kind: "thread" } });
149403
149562
  pendingAttachByChat.delete(chatKey);
149404
149563
  listedByChat.delete(chatKey);
149405
- await deliver(binding, t("resume.done", { title: target2.title }));
149564
+ if (project !== void 0) projects.listedProjectsByChat.delete(chatKey);
149565
+ await deliver(
149566
+ binding,
149567
+ project === void 0 ? t("resume.done", { title: target2.title }) : t("resume.doneInProject", { project: project.title, title: target2.title })
149568
+ );
149406
149569
  };
149407
149570
  const resumeSession = async (binding, arg) => {
149408
149571
  const chatKey = chatKeyOf(binding);
@@ -149429,11 +149592,12 @@ var makeSessions = (core) => {
149429
149592
  const listed = listedByChat.get(chatKey);
149430
149593
  let targetId = confirming ? pending ?? null : listed === void 0 ? null : pickSession(wanted, listed, []);
149431
149594
  if (targetId === null) {
149432
- targetId = pickSession(wanted, listed, await sessionRowsFor(binding));
149433
- if (targetId === null) {
149434
- await deliver(binding, t("resume.notFound", { query: arg }));
149595
+ const match5 = matchSession(wanted, listed, await resumeRowsFor(binding, wanted, listed));
149596
+ if (match5.kind !== "unique") {
149597
+ await deliver(binding, t(`resume.${match5.kind}`, { query: arg }));
149435
149598
  return;
149436
149599
  }
149600
+ targetId = match5.threadId;
149437
149601
  }
149438
149602
  const currentId = chatOf(core.state, chatKey).threadId;
149439
149603
  const [target2, other] = await Promise.all([
@@ -150133,21 +150297,38 @@ var clip4 = (text3, max) => {
150133
150297
  const chars = Array.from(text3);
150134
150298
  return chars.length <= max ? text3 : `${chars.slice(0, max - 1).join("")}\u2026`;
150135
150299
  };
150136
- var turnTextParts = (view, t) => {
150300
+ var titleAndSteps = (view, t) => {
150301
+ const lines = [view.title === "" ? t("card.untitled") : view.title];
150137
150302
  const names = view.steps.map((kind) => t(`card.step.${kind}`));
150138
150303
  if (view.omitted > 0) names.unshift(t("card.step.omitted", { count: view.omitted }));
150139
- const headLines = [view.title === "" ? t("card.untitled") : view.title];
150140
- if (names.length > 0) headLines.push(names.join(" \u2192 "));
150304
+ if (names.length > 0) lines.push(names.join(" \u2192 "));
150305
+ return lines;
150306
+ };
150307
+ var settledFoot = (view, t) => [t(`card.status.${view.status}`), ...view.errorText ? [view.errorText] : []].join("\n");
150308
+ var turnTextParts = (view, t) => {
150309
+ const headLines = titleAndSteps(view, t);
150310
+ const hasSteps = headLines.length > 1;
150141
150311
  if (view.running !== null) headLines.push(`\u{1F527} ${clip4(view.running, RUNNING_LABEL_MAX_CHARS)}`);
150142
150312
  const body = view.body.trim();
150143
- const footLines = [];
150144
- if (view.status === "working") {
150145
- if (body === "" && names.length === 0) footLines.push(t("card.status.working"));
150146
- } else {
150147
- footLines.push(t(`card.status.${view.status}`));
150148
- if (view.errorText !== null && view.errorText !== "") footLines.push(view.errorText);
150149
- }
150150
- return { head: headLines.join("\n"), body, foot: footLines.join("\n") };
150313
+ const foot = view.status !== "working" ? settledFoot(view, t) : body === "" && !hasSteps ? t("card.status.working") : "";
150314
+ return { head: headLines.join("\n"), body, foot };
150315
+ };
150316
+ var wecomSettledText = (view, t) => {
150317
+ const body = view.body.trim();
150318
+ const context2 = body === "" ? titleAndSteps(view, t).join("\n") : body;
150319
+ return joinParts(context2, settledFoot(view, t));
150320
+ };
150321
+ var wecomThinkingText = (view, t) => {
150322
+ const lines = titleAndSteps(view, t);
150323
+ lines.push(
150324
+ view.running === null ? t("card.thinking") : (
150325
+ // 文案自己以「…」结尾,截短的标签不再叠一个。
150326
+ t("card.usingTool", {
150327
+ name: clip4(view.running, RUNNING_LABEL_MAX_CHARS).replace(/…$/u, "")
150328
+ })
150329
+ )
150330
+ );
150331
+ return lines.join("\n").replace(/<\/?think>/gi, "");
150151
150332
  };
150152
150333
  var joinParts = (...parts) => parts.filter((part) => part !== "").join("\n\n");
150153
150334
 
@@ -150203,8 +150384,13 @@ var makeTurnWriter = (deps) => {
150203
150384
  };
150204
150385
 
150205
150386
  // src/telegramAdapter.ts
150206
- var COLUMNS2 = [
150207
- { key: "telegramToken", kind: "password", labelKey: "column.telegramToken.label" }
150387
+ var COLUMNS = [
150388
+ {
150389
+ key: "telegramToken",
150390
+ group: "essential",
150391
+ kind: "password",
150392
+ labelKey: "column.telegramToken.label"
150393
+ }
150208
150394
  ];
150209
150395
  var BACKOFF_START_MS = 1e3;
150210
150396
  var BACKOFF_MAX_MS = 6e4;
@@ -150322,7 +150508,7 @@ var makeTelegramAdapter = (deps) => {
150322
150508
  icon: "telegram",
150323
150509
  label: "Telegram",
150324
150510
  quotesReplies: true,
150325
- columns: COLUMNS2,
150511
+ columns: COLUMNS,
150326
150512
  validate: telegramConfigIssues,
150327
150513
  isConfigured: (binding) => telegramConfigIssues(binding).every((issue) => issue.code !== "required"),
150328
150514
  connect: async (binding, handlers) => {
@@ -151807,9 +151993,10 @@ var registerSettingsCommands = (ctx, adapters, updateSettings, connections) => {
151807
151993
  var WECHAT_TEXT_MAX_CHARS = 1800;
151808
151994
 
151809
151995
  // src/wechatAdapter.ts
151810
- var COLUMNS3 = [
151996
+ var COLUMNS2 = [
151811
151997
  {
151812
151998
  key: "wechatBotToken",
151999
+ group: "essential",
151813
152000
  kind: "password",
151814
152001
  labelKey: "column.wechatBotToken.label",
151815
152002
  descriptionKey: "column.wechatBotToken.description"
@@ -151821,10 +152008,10 @@ var makeWechatAdapter = (deps) => {
151821
152008
  return {
151822
152009
  platform: "wechat",
151823
152010
  botIdentity: () => "wechat-stub",
151824
- icon: "wechat",
152011
+ icon: "weixin",
151825
152012
  // 平台名与设置表的平台选项同一个键,免得默认机器人名与选项写成两种叫法。
151826
152013
  label: deps.translate("platform.wechat"),
151827
- columns: COLUMNS3,
152014
+ columns: COLUMNS2,
151828
152015
  validate: wechatConfigIssues,
151829
152016
  isConfigured: (bot) => wechatConfigIssues(bot).length === 0,
151830
152017
  connect: (bot, handlers) => {
@@ -152467,9 +152654,19 @@ var WECOM_CARD_THROTTLE_MS = 400;
152467
152654
  var LIVE_STREAM_LIMIT = 200;
152468
152655
  var BACKOFF_START_MS2 = 1e3;
152469
152656
  var BACKOFF_MAX_MS2 = 6e4;
152470
- var COLUMNS4 = [
152471
- { key: "wecomBotId", kind: "text", labelKey: "column.wecomBotId.label" },
152472
- { key: "wecomBotSecret", kind: "password", labelKey: "column.wecomBotSecret.label" }
152657
+ var COLUMNS3 = [
152658
+ {
152659
+ key: "wecomBotId",
152660
+ group: "essential",
152661
+ kind: "text",
152662
+ labelKey: "column.wecomBotId.label"
152663
+ },
152664
+ {
152665
+ key: "wecomBotSecret",
152666
+ group: "essential",
152667
+ kind: "password",
152668
+ labelKey: "column.wecomBotSecret.label"
152669
+ }
152473
152670
  ];
152474
152671
  var defaultSleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
152475
152672
  var credentials = (binding) => ({
@@ -152507,21 +152704,26 @@ var makeWecomAdapter = (deps) => {
152507
152704
  }
152508
152705
  };
152509
152706
  const emit = async (entry, view) => {
152510
- const { head, body, foot } = turnTextParts(view, t);
152511
152707
  if (view.status === "working") {
152512
- let shown = body;
152513
- const whole = joinParts(head, body, foot);
152514
- if (whole.length > WECOM_TEXT_MAX_CHARS) {
152515
- const marker = t("card.truncated", { count: 9999999 });
152516
- const room = WECOM_TEXT_MAX_CHARS - head.length - foot.length - marker.length - 6;
152517
- const kept = room > 0 ? body.slice(Math.max(0, body.length - room)) : "";
152518
- shown = `${t("card.truncated", { count: body.length - kept.length })}
152708
+ const body = view.body.trim();
152709
+ let content = `<think>${wecomThinkingText(view, t)}`;
152710
+ if (body !== "") {
152711
+ const prefix = `${content}</think>
152712
+ `;
152713
+ let shown = body;
152714
+ if (prefix.length + body.length > WECOM_TEXT_MAX_CHARS) {
152715
+ const marker = t("card.truncated", { count: 9999999 });
152716
+ const room = WECOM_TEXT_MAX_CHARS - prefix.length - marker.length - 6;
152717
+ const kept = room > 0 ? body.slice(Math.max(0, body.length - room)) : "";
152718
+ shown = `${t("card.truncated", { count: body.length - kept.length })}
152519
152719
  ${kept}`;
152720
+ }
152721
+ content = `${prefix}${shown}`;
152520
152722
  }
152521
- await sending("replyStream", () => entry.stream.send(joinParts(head, shown, foot), false));
152723
+ await sending("replyStream", () => entry.stream.send(content, false));
152522
152724
  return;
152523
152725
  }
152524
- const chunks = splitForWecom(joinParts(head, body, foot));
152726
+ const chunks = splitForWecom(wecomSettledText(view, t));
152525
152727
  const delivered = await sending("replyStream", () => entry.stream.send(chunks[0] ?? "", true));
152526
152728
  for (const chunk of delivered ? chunks.slice(1) : chunks) {
152527
152729
  await sending("sendMessage", () => entry.client.sendText(entry.chatId, chunk));
@@ -152538,7 +152740,7 @@ ${kept}`;
152538
152740
  botIdentity: (bot) => credentials(bot).botId,
152539
152741
  icon: "wecom",
152540
152742
  label: "WeCom",
152541
- columns: COLUMNS4,
152743
+ columns: COLUMNS3,
152542
152744
  unlistedValues: ["wecomEndpoint"],
152543
152745
  validate: wecomConfigIssues,
152544
152746
  isConfigured: (binding) => wecomConfigIssues(binding).length === 0,
@@ -153585,29 +153787,43 @@ var readWecomAppConnectConfig = (bot) => {
153585
153787
  };
153586
153788
 
153587
153789
  // src/wecomAppAdapter.ts
153588
- var COLUMNS5 = [
153790
+ var COLUMNS4 = [
153589
153791
  {
153590
153792
  key: "wecomAppCorpId",
153793
+ group: "essential",
153591
153794
  kind: "text",
153592
153795
  labelKey: "column.wecomAppCorpId.label",
153593
153796
  placeholderKey: "column.wecomAppCorpId.placeholder"
153594
153797
  },
153595
153798
  {
153596
153799
  key: "wecomAppAgentId",
153800
+ group: "essential",
153597
153801
  kind: "text",
153598
153802
  labelKey: "column.wecomAppAgentId.label",
153599
153803
  placeholderKey: "column.wecomAppAgentId.placeholder"
153600
153804
  },
153601
- { key: "wecomAppSecret", kind: "password", labelKey: "column.wecomAppSecret.label" },
153602
- { key: "wecomAppToken", kind: "password", labelKey: "column.wecomAppToken.label" },
153805
+ {
153806
+ key: "wecomAppSecret",
153807
+ group: "essential",
153808
+ kind: "password",
153809
+ labelKey: "column.wecomAppSecret.label"
153810
+ },
153811
+ {
153812
+ key: "wecomAppToken",
153813
+ group: "essential",
153814
+ kind: "password",
153815
+ labelKey: "column.wecomAppToken.label"
153816
+ },
153603
153817
  {
153604
153818
  key: "wecomAppEncodingAESKey",
153819
+ group: "essential",
153605
153820
  kind: "password",
153606
153821
  labelKey: "column.wecomAppEncodingAESKey.label",
153607
153822
  placeholderKey: "column.wecomAppEncodingAESKey.placeholder"
153608
153823
  },
153609
153824
  {
153610
153825
  key: "wecomAppCallbackBaseUrl",
153826
+ group: "more",
153611
153827
  kind: "text",
153612
153828
  labelKey: "column.wecomAppCallbackBaseUrl.label",
153613
153829
  descriptionKey: "column.wecomAppCallbackBaseUrl.description",
@@ -153615,6 +153831,7 @@ var COLUMNS5 = [
153615
153831
  },
153616
153832
  {
153617
153833
  key: "wecomAppApiBaseUrl",
153834
+ group: "more",
153618
153835
  kind: "text",
153619
153836
  labelKey: "column.wecomAppApiBaseUrl.label",
153620
153837
  descriptionKey: "column.wecomAppApiBaseUrl.description",
@@ -153622,6 +153839,7 @@ var COLUMNS5 = [
153622
153839
  },
153623
153840
  {
153624
153841
  key: "wecomAppListenPort",
153842
+ group: "more",
153625
153843
  kind: "text",
153626
153844
  labelKey: "column.wecomAppListenPort.label",
153627
153845
  descriptionKey: "column.wecomAppListenPort.description",
@@ -153629,6 +153847,7 @@ var COLUMNS5 = [
153629
153847
  },
153630
153848
  {
153631
153849
  key: "wecomAppListenHost",
153850
+ group: "more",
153632
153851
  kind: "text",
153633
153852
  labelKey: "column.wecomAppListenHost.label",
153634
153853
  descriptionKey: "column.wecomAppListenHost.description",
@@ -153760,7 +153979,7 @@ var makeWecomAppAdapter = (deps) => {
153760
153979
  },
153761
153980
  icon: "wecom",
153762
153981
  label: deps.translate("platform.wecom-app"),
153763
- columns: COLUMNS5,
153982
+ columns: COLUMNS4,
153764
153983
  // 设置页标格子用完整的逐列结果。算不算配齐仍是原来那份,不能收成「一个问题都没有」:
153765
153984
  // 必填都填了、EncodingAESKey 格式对。别的列格式不对的行照样交给 connect 报出来(一条日志,
153766
153985
  // 不重试),也不挡设置页的「检查连接」去探出站凭据。
@@ -153958,7 +154177,7 @@ var makeWecomAppAdapter = (deps) => {
153958
154177
  // src/index.ts
153959
154178
  var definition = {
153960
154179
  id: "im",
153961
- apiVersion: 24,
154180
+ apiVersion: 25,
153962
154181
  // 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
153963
154182
  activate: async (ctx, testing = {}) => {
153964
154183
  const { dispatchSleep } = testing;
@@ -154099,9 +154318,9 @@ var definition = {
154099
154318
  now: testing.retryClock?.now,
154100
154319
  timeZone: testing.timeZone
154101
154320
  });
154102
- const sessions = makeSessions(core);
154103
- const modelPlan = makeModelPlan(core, turns);
154104
154321
  const projectRows = makeProjectRows(core);
154322
+ const sessions = makeSessions(core, projectRows);
154323
+ const modelPlan = makeModelPlan(core, turns);
154105
154324
  const chatQueue = makeChatQueue(core);
154106
154325
  const panelWiring = makePanelWiring(core, {
154107
154326
  enqueueForChat: chatQueue.enqueueForChat,