@ycodium-ai/plugin-im 0.2.3254 → 0.2.3265

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
@@ -126363,6 +126363,10 @@ var makeAcknowledgements = (core, intervals) => {
126363
126363
  };
126364
126364
  };
126365
126365
 
126366
+ // src/approvalSettlement.ts
126367
+ var approvalSettlementTitle = (settlement, t) => settlement.kind === "timedOut" ? t("card.approval.timedOutTitle") : t("card.approval.doneTitle");
126368
+ var approvalSettlementText = (settlement, t) => settlement.kind === "chosen" ? t("card.approval.chosen", { label: settlement.label }) : settlement.kind === "timedOut" ? t("approval.timedOut", { minutes: settlement.minutes }) : t("card.approval.elsewhere");
126369
+
126366
126370
  // src/botConfig.ts
126367
126371
  import { PLUGIN_PROXY_DIRECT } from "@ycodium-ai/plugin-api";
126368
126372
 
@@ -126685,6 +126689,20 @@ var foldSessionTransferred = (state, data) => {
126685
126689
  var isRecord = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
126686
126690
  var nonEmpty3 = (value2) => typeof value2 === "string" && value2 !== "" ? value2 : null;
126687
126691
 
126692
+ // src/runtimeMode.ts
126693
+ var RUNTIME_MODE_OPTIONS = [
126694
+ { value: "approval-required", labelKey: "settings.runtimeMode.approvalRequired" },
126695
+ { value: "auto-accept-edits", labelKey: "settings.runtimeMode.autoAcceptEdits" },
126696
+ { value: "auto", labelKey: "settings.runtimeMode.auto" },
126697
+ { value: "full-access", labelKey: "settings.runtimeMode.fullAccess" }
126698
+ ];
126699
+ var isRuntimeMode = (value2) => RUNTIME_MODE_OPTIONS.some((option2) => option2.value === value2);
126700
+ var effectiveRuntimeMode = (ceiling, override) => {
126701
+ if (override == null) return ceiling;
126702
+ const rank = (mode) => RUNTIME_MODE_OPTIONS.findIndex((option2) => option2.value === mode);
126703
+ return rank(override) < rank(ceiling) ? override : ceiling;
126704
+ };
126705
+
126688
126706
  // src/chatRecordPatch.ts
126689
126707
  var readChatPatch = (data) => {
126690
126708
  const patch = {};
@@ -126715,6 +126733,9 @@ var readChatPatch = (data) => {
126715
126733
  if (data["preset"] === null) patch.preset = null;
126716
126734
  else if (nonEmpty3(data["preset"]) !== null) patch.preset = nonEmpty3(data["preset"]);
126717
126735
  if (typeof data["plan"] === "boolean") patch.plan = data["plan"];
126736
+ if (data["runtimeMode"] === null || isRuntimeMode(data["runtimeMode"]))
126737
+ patch.runtimeMode = data["runtimeMode"];
126738
+ if (typeof data["includeArchived"] === "boolean") patch.includeArchived = data["includeArchived"];
126718
126739
  if (data["chatKind"] === null) patch.chatKind = null;
126719
126740
  else if (data["chatKind"] === "private" || data["chatKind"] === "group")
126720
126741
  patch.chatKind = data["chatKind"];
@@ -127450,14 +127471,13 @@ var pickChatChoice = (arg, choices, matches, qualified) => {
127450
127471
  const named = choices.filter((choice) => matches(choice, needle));
127451
127472
  return named.length === 1 ? named[0] ?? null : null;
127452
127473
  };
127474
+ var chatChoiceLine = (index, current, label) => `${String(index + 1)}. ${current ? "\u2605 " : ""}${label}`;
127453
127475
  var formatChatChoices = (choices, label, isCurrent, text3) => {
127454
127476
  const shown = choices.slice(0, CHAT_CHOICE_LIST_MAX);
127455
127477
  return [
127456
127478
  "",
127457
127479
  text3.title,
127458
- ...shown.map(
127459
- (choice, index) => `${String(index + 1)}. ${isCurrent(choice) ? "\u2605 " : ""}${label(choice)}`
127460
- ),
127480
+ ...shown.map((choice, index) => chatChoiceLine(index, isCurrent(choice), label(choice))),
127461
127481
  ...choices.length > shown.length ? [text3.more(choices.length - shown.length)] : [],
127462
127482
  "",
127463
127483
  text3.footer
@@ -127520,15 +127540,22 @@ var pickModel = (arg, choices) => {
127520
127540
  };
127521
127541
 
127522
127542
  // src/botConfig.ts
127543
+ var DEFAULT_APPROVAL_TIMEOUT_MINUTES = 60;
127544
+ var MAX_APPROVAL_TIMEOUT_MINUTES = 1440;
127545
+ var parseApprovalTimeoutMinutes = (value2) => {
127546
+ if (value2 === "") return DEFAULT_APPROVAL_TIMEOUT_MINUTES;
127547
+ if (!/^\d+$/.test(value2)) return null;
127548
+ const minutes = Number(value2);
127549
+ return Number.isInteger(minutes) && minutes <= MAX_APPROVAL_TIMEOUT_MINUTES ? minutes : null;
127550
+ };
127551
+ var readApprovalTimeoutMinutes = (bot) => parseApprovalTimeoutMinutes(bot.values["approvalTimeoutMinutes"]?.trim() ?? "") ?? DEFAULT_APPROVAL_TIMEOUT_MINUTES;
127552
+ var validateBotSettings = (bot, adapter) => [
127553
+ ...adapter.validate?.(bot) ?? [],
127554
+ ...parseApprovalTimeoutMinutes(bot.values["approvalTimeoutMinutes"]?.trim() ?? "") === null ? [{ column: "approvalTimeoutMinutes", code: "format" }] : []
127555
+ ];
127523
127556
  var BOTS_FIELD = "bots";
127524
127557
  var REPLY_LANGUAGE_FIELD = "replyLanguage";
127525
127558
  var parseGroupMention = (value2) => value2 === "never" || value2 === "ambient" ? value2 : "always";
127526
- var INBOUND_RUNTIME_MODES = /* @__PURE__ */ new Set([
127527
- "approval-required",
127528
- "auto-accept-edits",
127529
- "auto",
127530
- "full-access"
127531
- ]);
127532
127559
  var DEFAULT_INBOUND_RUNTIME_MODE = "auto-accept-edits";
127533
127560
  var QUEUE_MODE_OPTIONS = [
127534
127561
  { value: "queue", labelKey: "settings.queueMode.queue" },
@@ -127663,12 +127690,7 @@ var buildBotColumns = (adapters, models = [], catalogComplete = true, presets =
127663
127690
  kind: "select",
127664
127691
  labelKey: "column.runtimeMode.label",
127665
127692
  descriptionKey: "column.runtimeMode.description",
127666
- options: [
127667
- { value: "approval-required", labelKey: "settings.runtimeMode.approvalRequired" },
127668
- { value: "auto-accept-edits", labelKey: "settings.runtimeMode.autoAcceptEdits" },
127669
- { value: "auto", labelKey: "settings.runtimeMode.auto" },
127670
- { value: "full-access", labelKey: "settings.runtimeMode.fullAccess" }
127671
- ]
127693
+ options: RUNTIME_MODE_OPTIONS
127672
127694
  },
127673
127695
  {
127674
127696
  key: "groupMention",
@@ -127690,6 +127712,16 @@ var buildBotColumns = (adapters, models = [], catalogComplete = true, presets =
127690
127712
  descriptionKey: "column.queueMode.description",
127691
127713
  options: [...QUEUE_MODE_OPTIONS]
127692
127714
  },
127715
+ {
127716
+ key: "approvalTimeoutMinutes",
127717
+ group: "more",
127718
+ kind: "number",
127719
+ labelKey: "column.approvalTimeoutMinutes.label",
127720
+ descriptionKey: "column.approvalTimeoutMinutes.description",
127721
+ defaultValue: String(DEFAULT_APPROVAL_TIMEOUT_MINUTES),
127722
+ min: 0,
127723
+ max: MAX_APPROVAL_TIMEOUT_MINUTES
127724
+ },
127693
127725
  {
127694
127726
  key: "larkPanelMode",
127695
127727
  group: "more",
@@ -127739,6 +127771,8 @@ var buildSettingsCard = (adapters) => {
127739
127771
  var readBotView = (rowId, row, adapter) => {
127740
127772
  const values = {};
127741
127773
  for (const key2 of Object.keys(row)) values[key2] = readString(row, key2);
127774
+ if (typeof row["approvalTimeoutMinutes"] === "number")
127775
+ values["approvalTimeoutMinutes"] = String(row["approvalTimeoutMinutes"]);
127742
127776
  const proxyValue = readString(row, "proxy");
127743
127777
  return {
127744
127778
  rowId,
@@ -127765,7 +127799,7 @@ var readBot = (adapterFor, rowId, row) => {
127765
127799
  ...defaultPreset === "" ? {} : { defaultPreset },
127766
127800
  accessMode: parseAccessMode(readString(row, "accessMode")),
127767
127801
  allowedSenders,
127768
- runtimeMode: INBOUND_RUNTIME_MODES.has(requested) ? requested : DEFAULT_INBOUND_RUNTIME_MODE,
127802
+ runtimeMode: isRuntimeMode(requested) ? requested : DEFAULT_INBOUND_RUNTIME_MODE,
127769
127803
  queueMode: parseQueueMode(readString(row, "queueMode")),
127770
127804
  larkPanelMode: parseLarkPanelMode(readString(row, "larkPanelMode")),
127771
127805
  wecomNumberedHints: parseOnOff(readString(row, "wecomNumberedHints"), true)
@@ -127798,6 +127832,145 @@ var isAllowedSender = (bot, sender, chatKind) => {
127798
127832
  return bot.allowedSenders.has(sender);
127799
127833
  };
127800
127834
 
127835
+ // src/approvalTimeouts.ts
127836
+ var RETRY_BASE_MS = 3e4;
127837
+ var RETRY_MAX_MS = 5 * 6e4;
127838
+ var makeApprovalTimeouts = (core, finish) => {
127839
+ const deadlines = /* @__PURE__ */ new Map();
127840
+ const tasks = /* @__PURE__ */ new Set();
127841
+ let stopped = false;
127842
+ const minutesFor = (approval) => {
127843
+ const binding = core.bindingForChatKey(approval.chatKey);
127844
+ return binding === void 0 ? 0 : readApprovalTimeoutMinutes(binding);
127845
+ };
127846
+ const clearTimer = (entry) => {
127847
+ clearTimeout(entry.timer);
127848
+ entry.timer = void 0;
127849
+ };
127850
+ const cancel = (requestId) => {
127851
+ const entry = deadlines.get(requestId);
127852
+ if (entry === void 0) return;
127853
+ clearTimer(entry);
127854
+ if (!entry.running) deadlines.delete(requestId);
127855
+ };
127856
+ const active = (entry) => !stopped && core.state.approvals[entry.approval.requestId]?.resolved === false;
127857
+ const schedule2 = (entry, delay2) => {
127858
+ clearTimer(entry);
127859
+ if (!active(entry) || minutesFor(entry.approval) === 0) {
127860
+ if (!entry.running) deadlines.delete(entry.approval.requestId);
127861
+ return;
127862
+ }
127863
+ entry.timer = setTimeout(
127864
+ () => {
127865
+ entry.timer = void 0;
127866
+ const task = expire(entry).finally(() => tasks.delete(task));
127867
+ tasks.add(task);
127868
+ },
127869
+ Math.max(0, delay2)
127870
+ );
127871
+ entry.timer.unref?.();
127872
+ };
127873
+ const remaining = (entry) => entry.createdAt === null ? 0 : entry.createdAt + minutesFor(entry.approval) * 6e4 - Date.now();
127874
+ const expire = async (entry) => {
127875
+ if (!active(entry) || minutesFor(entry.approval) === 0) return;
127876
+ entry.running = true;
127877
+ const { approval } = entry;
127878
+ try {
127879
+ const pending = await core.host.approvals.pending.list({ threadId: approval.threadId });
127880
+ if (!active(entry) || minutesFor(approval) === 0) return;
127881
+ const candidate = pending.find((item) => item.requestId === approval.requestId);
127882
+ if (candidate === void 0) return;
127883
+ entry.createdAt = Date.parse(candidate.createdAt);
127884
+ if (!Number.isFinite(entry.createdAt)) {
127885
+ throw new Error("invalid approval creation time");
127886
+ }
127887
+ if (remaining(entry) > 0) {
127888
+ schedule2(entry, remaining(entry));
127889
+ return;
127890
+ }
127891
+ const minutes = minutesFor(approval);
127892
+ entry.responding = true;
127893
+ await core.host.approvals.respond({
127894
+ requestId: entry.responseId,
127895
+ threadId: approval.threadId,
127896
+ approvalRequestId: approval.requestId,
127897
+ decision: "decline"
127898
+ });
127899
+ if (stopped) return;
127900
+ await finish(core.state.approvals[approval.requestId] ?? approval, {
127901
+ kind: "timedOut",
127902
+ minutes
127903
+ });
127904
+ } catch {
127905
+ if (!active(entry)) return;
127906
+ core.host.log.warn("chat could not decline an expired approval", {
127907
+ threadId: approval.threadId,
127908
+ requestId: approval.requestId
127909
+ });
127910
+ if (entry.elsewhere !== void 0) {
127911
+ entry.elsewhere();
127912
+ } else if (remaining(entry) > 0) {
127913
+ schedule2(entry, remaining(entry));
127914
+ } else {
127915
+ schedule2(entry, Math.min(RETRY_BASE_MS * 2 ** entry.attempts, RETRY_MAX_MS));
127916
+ entry.attempts += 1;
127917
+ }
127918
+ } finally {
127919
+ entry.responding = false;
127920
+ entry.running = false;
127921
+ if (entry.timer === void 0) deadlines.delete(approval.requestId);
127922
+ }
127923
+ };
127924
+ const track = (approval, createdAt) => {
127925
+ if (stopped || approval.resolved || minutesFor(approval) === 0) return;
127926
+ const existing = deadlines.get(approval.requestId);
127927
+ if (existing !== void 0) return;
127928
+ const entry = {
127929
+ approval,
127930
+ createdAt: createdAt === null ? null : Date.parse(createdAt),
127931
+ timer: void 0,
127932
+ attempts: 0,
127933
+ running: false,
127934
+ responding: false,
127935
+ responseId: crypto.randomUUID()
127936
+ };
127937
+ deadlines.set(approval.requestId, entry);
127938
+ schedule2(entry, remaining(entry));
127939
+ };
127940
+ const reconcile = async () => {
127941
+ await core.ready;
127942
+ if (stopped) return;
127943
+ for (const entry of deadlines.values()) {
127944
+ clearTimer(entry);
127945
+ entry.attempts = 0;
127946
+ if (!entry.running) schedule2(entry, remaining(entry));
127947
+ }
127948
+ for (const approval of Object.values(core.state.approvals)) track(approval, null);
127949
+ };
127950
+ core.host.onDispose(async () => {
127951
+ stopped = true;
127952
+ for (const entry of deadlines.values()) clearTimer(entry);
127953
+ await Promise.all(tasks);
127954
+ deadlines.clear();
127955
+ });
127956
+ void reconcile();
127957
+ return {
127958
+ track,
127959
+ cancel,
127960
+ reconcile,
127961
+ responding: (requestId) => deadlines.get(requestId)?.responding === true,
127962
+ /** 超时提交在途时,宿主已答事件必须等待提交结果后再决定定稿来源。 */
127963
+ resolved: (requestId, elsewhere) => {
127964
+ const entry = deadlines.get(requestId);
127965
+ if (entry?.responding === true) entry.elsewhere = elsewhere;
127966
+ else {
127967
+ cancel(requestId);
127968
+ elsewhere();
127969
+ }
127970
+ }
127971
+ };
127972
+ };
127973
+
127801
127974
  // src/numberedReply.ts
127802
127975
  var optionIndexFrom = (text3, count) => {
127803
127976
  const trimmed = text3.trim();
@@ -127902,34 +128075,52 @@ var makeReplyAnchors = (host) => {
127902
128075
  var makeApprovals = (core, anchors) => {
127903
128076
  const { host, t, adapterFor, commit, outbound, bindingForChatKey, bindingOfThread, ready } = core;
127904
128077
  const settleApprovalCard = async (approval, settlement) => {
127905
- if (approval.cardId === void 0) return;
127906
128078
  const binding = bindingForChatKey(approval.chatKey);
127907
128079
  const settle = binding === void 0 ? void 0 : adapterFor(binding.platform)?.settleApproval;
127908
- if (binding === void 0 || settle === void 0) return;
127909
- try {
127910
- await settle(binding, approval.cardId, settlement);
127911
- } catch (error) {
127912
- host.log.warn("chat could not update the approval card", { error: describeError(error) });
128080
+ if (binding === void 0) return;
128081
+ if (approval.cardId !== void 0 && settle !== void 0) {
128082
+ try {
128083
+ await settle(binding, approval.cardId, settlement);
128084
+ return;
128085
+ } catch (error) {
128086
+ host.log.warn("chat could not update the approval card", { error: describeError(error) });
128087
+ }
128088
+ }
128089
+ if (settlement.kind === "timedOut") {
128090
+ await core.deliver(binding, approvalSettlementText(settlement, t));
127913
128091
  }
127914
128092
  };
127915
- const answerApproval = async (approval, decision, label) => {
128093
+ const finishApproval = async (approval, settlement, respond) => {
127916
128094
  commit("approval-resolved", { requestId: approval.requestId });
127917
- await host.approvals.respond({
127918
- requestId: crypto.randomUUID(),
127919
- threadId: approval.threadId,
127920
- approvalRequestId: approval.requestId,
127921
- // 落盘按钮保留 string 形状;合法决定仍由端口 schema 校验。
127922
- decision
127923
- });
127924
- host.log.info("chat answered an approval", { threadId: approval.threadId, decision });
127925
- await settleApprovalCard(core.state.approvals[approval.requestId] ?? approval, {
127926
- kind: "chosen",
127927
- label
128095
+ await respond?.();
128096
+ outbound(
128097
+ approval.chatKey,
128098
+ () => settleApprovalCard(core.state.approvals[approval.requestId] ?? approval, settlement)
128099
+ );
128100
+ };
128101
+ const timeouts = makeApprovalTimeouts(core, finishApproval);
128102
+ const answerApproval = async (approval, decision, label) => {
128103
+ timeouts.cancel(approval.requestId);
128104
+ await finishApproval(approval, { kind: "chosen", label }, async () => {
128105
+ await host.approvals.respond({
128106
+ requestId: crypto.randomUUID(),
128107
+ threadId: approval.threadId,
128108
+ approvalRequestId: approval.requestId,
128109
+ // 落盘按钮保留 string 形状;合法决定仍由端口 schema 校验。
128110
+ decision
128111
+ });
128112
+ host.log.info("chat answered an approval", { threadId: approval.threadId, decision });
127928
128113
  });
127929
128114
  };
128115
+ const settled = (approval) => approval.resolved || timeouts.responding(approval.requestId);
127930
128116
  const answerByToken = async (token) => {
127931
128117
  const found = findApprovalByToken(core.state, token);
127932
- if (found === void 0 || found.approval.resolved) return;
128118
+ if (found === void 0) return;
128119
+ if (settled(found.approval)) {
128120
+ const binding = bindingForChatKey(found.approval.chatKey);
128121
+ if (binding !== void 0) await core.deliver(binding, t("approval.alreadyHandled"));
128122
+ return;
128123
+ }
127933
128124
  await answerApproval(found.approval, found.button.decision, found.button.label);
127934
128125
  };
127935
128126
  const sendApprovalCard = async (binding, adapter, threadId, offer) => {
@@ -127946,10 +128137,7 @@ var makeApprovals = (core, anchors) => {
127946
128137
  replyHint: approvalReplyHint(t, binding)
127947
128138
  });
127948
128139
  host.log.info("chat asked for an approval", { threadId, requestId: offer.requestId });
127949
- if (cardId === null) return;
127950
- commit("approval-carded", { requestId: offer.requestId, cardId });
127951
- const record4 = core.state.approvals[offer.requestId];
127952
- if (record4?.resolved === true) await settleApprovalCard(record4, { kind: "elsewhere" });
128140
+ if (cardId !== null) commit("approval-carded", { requestId: offer.requestId, cardId });
127953
128141
  } catch (error) {
127954
128142
  host.log.warn("chat could not ask for the approval", { error: describeError(error) });
127955
128143
  }
@@ -127988,6 +128176,7 @@ var makeApprovals = (core, anchors) => {
127988
128176
  });
127989
128177
  return {
127990
128178
  requestId: approval.requestId,
128179
+ createdAt: approval.createdAt,
127991
128180
  title: approval.appName ?? "Ycodium",
127992
128181
  // 正文用这道题自己的说明;事件的 summary 只是宿主拼的「哪条会话在等批准」,最后才用。
127993
128182
  body: approval.detail ?? approval.summary ?? event.summary,
@@ -127997,12 +128186,14 @@ var makeApprovals = (core, anchors) => {
127997
128186
  );
127998
128187
  for (const offer of offers) {
127999
128188
  outbound(chatKeyOf(binding), () => sendApprovalCard(binding, adapter, threadId, offer));
128189
+ const record4 = core.state.approvals[offer.requestId];
128190
+ if (record4 !== void 0) timeouts.track(record4, offer.createdAt);
128000
128191
  }
128001
128192
  });
128002
128193
  const handleApprovalClick = (token, operatorId) => {
128003
128194
  const found = findApprovalByToken(core.state, token);
128004
128195
  if (found === void 0) return null;
128005
- if (found.approval.resolved) return t("approval.alreadyHandled");
128196
+ if (settled(found.approval)) return t("approval.alreadyHandled");
128006
128197
  const target2 = bindingForChatKey(found.approval.chatKey);
128007
128198
  if (target2 === void 0 || !isAllowedSender(target2, operatorId, chatOf(core.state, found.approval.chatKey).chatKind)) {
128008
128199
  host.log.warn("chat ignored an approval click from someone not on the allow list", {
@@ -128018,7 +128209,18 @@ var makeApprovals = (core, anchors) => {
128018
128209
  );
128019
128210
  return null;
128020
128211
  };
128021
- return { settleApprovalCard, answerByToken, handleApprovalClick };
128212
+ return {
128213
+ answerByToken,
128214
+ handleApprovalClick,
128215
+ reconcileTimeouts: timeouts.reconcile,
128216
+ approvalResolved: (requestId) => {
128217
+ const record4 = core.state.approvals[requestId];
128218
+ if (record4 === void 0 || record4.resolved) return;
128219
+ timeouts.resolved(requestId, () => {
128220
+ void finishApproval(record4, { kind: "elsewhere" });
128221
+ });
128222
+ }
128223
+ };
128022
128224
  };
128023
128225
 
128024
128226
  // src/chatQueue.ts
@@ -128144,7 +128346,8 @@ var sessionLine = (row, position, now, t) => {
128144
128346
  row.current ? t("sessions.badge.current") : null,
128145
128347
  row.running ? t("sessions.badge.running") : null,
128146
128348
  row.elsewhere ? t("sessions.badge.elsewhere") : null,
128147
- row.origin === "web" ? t("sessions.badge.web") : null
128349
+ row.origin === "web" ? t("sessions.badge.web") : null,
128350
+ row.archived === true ? t("sessions.badge.archived") : null
128148
128351
  ].filter((badge) => badge !== null);
128149
128352
  return [`${String(position)}. ${title}`, age, ...badges, shortId(row.threadId)].filter((part) => part !== "").join(" \xB7 ");
128150
128353
  };
@@ -128351,7 +128554,7 @@ var makeChatSetup = (core, deps) => {
128351
128554
  };
128352
128555
 
128353
128556
  // src/commands.ts
128354
- import { threadOrNull as threadOrNull3 } from "@ycodium-ai/plugin-api";
128557
+ import { threadOrNull as threadOrNull4 } from "@ycodium-ai/plugin-api";
128355
128558
 
128356
128559
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Pipeable.js
128357
128560
  var pipeArguments = (self2, args2) => {
@@ -133569,7 +133772,16 @@ var ParsedChatCommand = Union2([
133569
133772
  SimpleChatCommand,
133570
133773
  Struct({ kind: Literal2("sessions"), arg: optionalKey2(String4) }),
133571
133774
  Struct({
133572
- kind: Literals(["resume", "model", "reasoning", "repo", "version"]),
133775
+ kind: Literals([
133776
+ "resume",
133777
+ "model",
133778
+ "reasoning",
133779
+ "repo",
133780
+ "version",
133781
+ "permission",
133782
+ "compact",
133783
+ "archived"
133784
+ ]),
133573
133785
  arg: String4
133574
133786
  }),
133575
133787
  Struct({ kind: Literal2("history"), count: NullOr(Number5) }),
@@ -133619,6 +133831,15 @@ var parseChatCommand = (input) => {
133619
133831
  if (/^\/status$/i.test(trimmed)) return { kind: "status" };
133620
133832
  const versionArg = trimmed.match(/^\/version(?:\s+([\s\S]*))?$/iu);
133621
133833
  if (versionArg !== null) return { kind: "version", arg: (versionArg[1] ?? "").trim() };
133834
+ const compact = commandArgument(
133835
+ trimmed,
133836
+ input.chinese,
133837
+ /^\/compact(?:\s+([\s\S]*))?$/i,
133838
+ /^压缩(?:\s+([\s\S]*))?$/
133839
+ );
133840
+ if (compact !== void 0) return { kind: "compact", arg: compact };
133841
+ const archived = trimmed.match(/^\/archived(?:\s+([\s\S]*))?$/i);
133842
+ if (archived !== null) return { kind: "archived", arg: (archived[1] ?? "").trim() };
133622
133843
  if (/^\/new$/i.test(trimmed)) return { kind: "new" };
133623
133844
  if (/^\/help$/i.test(trimmed)) return { kind: "help" };
133624
133845
  if (/^\/panel$/i.test(trimmed)) return { kind: "panel" };
@@ -133649,6 +133870,13 @@ var parseChatCommand = (input) => {
133649
133870
  /^思考强度(?:\s+([\s\S]*))?$/
133650
133871
  );
133651
133872
  if (reasoning !== void 0) return { kind: "reasoning", arg: reasoning };
133873
+ const permission = commandArgument(
133874
+ trimmed,
133875
+ input.chinese,
133876
+ /^\/permission(?:\s+([\s\S]*))?$/i,
133877
+ /^权限(?:\s+([\s\S]*))?$/
133878
+ );
133879
+ if (permission !== void 0) return { kind: "permission", arg: permission };
133652
133880
  const preset = trimmed.match(/^\/preset(?:\s+([\s\S]*))?$/iu);
133653
133881
  if (preset !== null)
133654
133882
  return {
@@ -133678,16 +133906,19 @@ var helpText = (t, options = {}) => [
133678
133906
  t("help.title"),
133679
133907
  t("help.stop"),
133680
133908
  t("help.continue"),
133909
+ t("help.compact"),
133681
133910
  t("help.status"),
133682
133911
  t("help.version"),
133683
133912
  t("help.new"),
133684
133913
  t("help.sessions"),
133914
+ t("help.archived"),
133685
133915
  t("help.resume"),
133686
133916
  t("help.history", HISTORY_COUNTS),
133687
133917
  t("help.syncNote"),
133688
133918
  t("help.model"),
133689
133919
  t("help.reasoning"),
133690
133920
  t("help.preset"),
133921
+ t("help.permission"),
133691
133922
  t("help.plan"),
133692
133923
  t("help.repo"),
133693
133924
  t("help.group"),
@@ -133697,6 +133928,90 @@ var helpText = (t, options = {}) => [
133697
133928
  ...options.panel === true ? ["", t("help.panel")] : []
133698
133929
  ].join("\n");
133699
133930
 
133931
+ // src/compactCommand.ts
133932
+ import { threadOrNull as threadOrNull2 } from "@ycodium-ai/plugin-api";
133933
+ var turnState = (session) => isSessionRunning(session?.status) ? "running" : (session?.activeTurnId ?? null) !== null ? "stuck" : "idle";
133934
+ var makeCompactCommand = (core) => {
133935
+ const { host, t, deliver } = core;
133936
+ const pending = /* @__PURE__ */ new Map();
133937
+ host.onDispose(() => pending.clear());
133938
+ host.reactor({
133939
+ name: "im.compact-results",
133940
+ events: ["thread.activity-appended"],
133941
+ handle: (event) => {
133942
+ if (event.type !== "thread.activity-appended") return;
133943
+ const payload = event.payload;
133944
+ if (typeof payload.threadId !== "string") return;
133945
+ const activity = payload.activity;
133946
+ const key2 = activity?.kind === "provider.context.compact.failed" ? "compact.failed" : activity?.kind === "context-compaction" && activity.payload?.phase === "completed" ? "compact.done" : null;
133947
+ if (key2 === null) return;
133948
+ const chatKey = pending.get(payload.threadId);
133949
+ if (chatKey === void 0) return;
133950
+ pending.delete(payload.threadId);
133951
+ core.outbound(chatKey, async () => {
133952
+ const binding = core.bindingForChatKey(chatKey);
133953
+ if (binding === void 0) return;
133954
+ await deliver(binding, t(key2));
133955
+ });
133956
+ }
133957
+ });
133958
+ return async (binding, command) => {
133959
+ if (command.arg !== "") {
133960
+ await deliver(binding, t("compact.usage"));
133961
+ return;
133962
+ }
133963
+ const chatKey = chatKeyOf(binding);
133964
+ const threadId = chatOf(core.state, chatKey).threadId;
133965
+ if (threadId === null) {
133966
+ await deliver(binding, t("compact.noSession"));
133967
+ return;
133968
+ }
133969
+ const failed = async () => {
133970
+ host.log.warn("chat could not compact the context", { threadId });
133971
+ await deliver(binding, t("compact.failed"));
133972
+ };
133973
+ let thread;
133974
+ try {
133975
+ thread = await threadOrNull2(host.threads, threadId);
133976
+ } catch {
133977
+ await failed();
133978
+ return;
133979
+ }
133980
+ if (thread === null) {
133981
+ await deliver(binding, t("compact.noSession"));
133982
+ return;
133983
+ }
133984
+ const state = turnState(thread.session);
133985
+ if (state !== "idle") {
133986
+ await deliver(binding, t(state === "running" ? "compact.busy" : "compact.stuck"));
133987
+ return;
133988
+ }
133989
+ let supported = false;
133990
+ try {
133991
+ const listed = await host.machineSettings.providers.get({});
133992
+ const provider = listed.providers.find(
133993
+ (item) => item.instanceId === thread.modelSelection.instanceId
133994
+ );
133995
+ supported = provider?.supportsContextCompaction === true;
133996
+ } catch {
133997
+ await failed();
133998
+ return;
133999
+ }
134000
+ if (!supported) {
134001
+ await deliver(binding, t("compact.unsupported"));
134002
+ return;
134003
+ }
134004
+ try {
134005
+ await host.threads.context.compact({ requestId: crypto.randomUUID(), threadId });
134006
+ } catch {
134007
+ await failed();
134008
+ return;
134009
+ }
134010
+ pending.set(threadId, chatKey);
134011
+ await deliver(binding, t("compact.started"));
134012
+ };
134013
+ };
134014
+
133700
134015
  // src/deadline.ts
133701
134016
  var withDeadline = async (timeoutMs, load) => {
133702
134017
  const signal = AbortSignal.timeout(timeoutMs);
@@ -133870,7 +134185,7 @@ ${body}`);
133870
134185
  import { modelSelectionStringOption as modelSelectionStringOption2 } from "@ycodium-ai/plugin-api/executor";
133871
134186
 
133872
134187
  // src/currentModel.ts
133873
- import { threadOrNull as threadOrNull2 } from "@ycodium-ai/plugin-api";
134188
+ import { threadOrNull as threadOrNull3 } from "@ycodium-ai/plugin-api";
133874
134189
 
133875
134190
  // src/botModel.ts
133876
134191
  var resolveDefaultModel = async (bot, chatModel, projectDefault, providers, knownInstances) => {
@@ -133898,7 +134213,7 @@ var makeCurrentModelResolver = (core) => {
133898
134213
  } = {}) => {
133899
134214
  const chat = chatOf(core.state, chatKeyOf(binding));
133900
134215
  if (chat.threadId !== null) {
133901
- const thread = await threadOrNull2(host.threads, chat.threadId, { archived: "include" }).catch(
134216
+ const thread = await threadOrNull3(host.threads, chat.threadId, { archived: "include" }).catch(
133902
134217
  onReadError
133903
134218
  );
133904
134219
  if (threadUsable(binding, thread))
@@ -134044,10 +134359,97 @@ var makeReasoningCommand = (core, currentModel) => {
134044
134359
  };
134045
134360
  };
134046
134361
 
134362
+ // src/permissionCommand.ts
134363
+ var makePermissionCommand = (core) => {
134364
+ const { t, deliver } = core;
134365
+ return async (binding, argument, senderId) => {
134366
+ const chatKey = chatKeyOf(binding);
134367
+ const bot = core.bindingForChatKey(chatKey) ?? binding;
134368
+ const ceiling = bot.runtimeMode;
134369
+ const saved = chatOf(core.state, chatKey).runtimeMode;
134370
+ const current = effectiveRuntimeMode(ceiling, saved);
134371
+ const label = (mode) => t(RUNTIME_MODE_OPTIONS.find((option2) => option2.value === mode).labelKey);
134372
+ const identified = senderId !== null && senderId !== "";
134373
+ const canChange = identified && bot.allowedSenders.has(senderId);
134374
+ if (argument === "") {
134375
+ await deliver(
134376
+ binding,
134377
+ [
134378
+ t("permission.current", { mode: label(current) }),
134379
+ t("permission.ceiling", { mode: label(ceiling) }),
134380
+ // 聊天自己的选择总要说出来:等于上限时与跟随机器人看着一样,上限放宽后表现却不同。
134381
+ saved == null ? t("permission.following") : saved !== current ? t("permission.cappedChoice", { mode: label(saved) }) : t("permission.savedChoice", { mode: label(saved) }),
134382
+ "",
134383
+ ...RUNTIME_MODE_OPTIONS.map(
134384
+ ({ value: value2, labelKey }, index) => [
134385
+ chatChoiceLine(index, value2 === current, `${t(labelKey)} (${value2})`),
134386
+ ...value2 === ceiling ? [t("permission.ceilingMarker")] : [],
134387
+ ...effectiveRuntimeMode(ceiling, value2) !== value2 ? [t("permission.unavailable")] : []
134388
+ ].join(" \xB7 ")
134389
+ ),
134390
+ "",
134391
+ t(
134392
+ canChange ? "permission.usage" : identified ? "permission.readOnly" : "permission.readOnlyUnknown"
134393
+ )
134394
+ ].join("\n")
134395
+ );
134396
+ return;
134397
+ }
134398
+ if (!canChange) {
134399
+ await deliver(
134400
+ binding,
134401
+ t(identified ? "permission.notAllowed" : "permission.notAllowedUnknown")
134402
+ );
134403
+ return;
134404
+ }
134405
+ const requested = argument.toLowerCase();
134406
+ const reset = requested === "default" || t.language === "zh-CN" && requested === "\u9ED8\u8BA4";
134407
+ const selected = pickChatChoice(
134408
+ argument,
134409
+ RUNTIME_MODE_OPTIONS,
134410
+ (option2, needle) => option2.value === needle || t(option2.labelKey).toLowerCase() === needle
134411
+ )?.value;
134412
+ if (!reset && selected === void 0) {
134413
+ await deliver(
134414
+ binding,
134415
+ [
134416
+ /^\d+$/u.test(argument.trim()) ? t("permission.numberOutOfRange", {
134417
+ number: argument.trim(),
134418
+ max: RUNTIME_MODE_OPTIONS.length
134419
+ }) : t("permission.notFound", { query: argument.trim() }),
134420
+ t("permission.usage")
134421
+ ].join("\n")
134422
+ );
134423
+ return;
134424
+ }
134425
+ if (!reset && effectiveRuntimeMode(ceiling, selected) !== selected) {
134426
+ await deliver(binding, t("permission.exceedsCeiling", { mode: label(ceiling) }));
134427
+ return;
134428
+ }
134429
+ if (reset && saved == null) {
134430
+ await deliver(binding, t("permission.alreadyDefault", { mode: label(ceiling) }));
134431
+ return;
134432
+ }
134433
+ try {
134434
+ await core.commitDurably("chat-updated", { chatKey, runtimeMode: reset ? null : selected });
134435
+ } catch {
134436
+ await deliver(binding, t("permission.saveFailed"));
134437
+ return;
134438
+ }
134439
+ await deliver(
134440
+ binding,
134441
+ [
134442
+ reset ? t("permission.reset", { mode: label(ceiling) }) : t("permission.selected", { mode: label(selected) }),
134443
+ t("permission.nextTurn")
134444
+ ].join("\n")
134445
+ );
134446
+ };
134447
+ };
134448
+
134047
134449
  // package.json
134048
134450
  var package_default = {
134049
134451
  name: "@ycodium-ai/plugin-im",
134050
- version: "0.2.3254",
134452
+ version: "0.2.3265",
134051
134453
  description: "Talk to your agents from Telegram, WeCom, Lark, or WeChat (experimental).",
134052
134454
  keywords: [
134053
134455
  "im",
@@ -134115,7 +134517,8 @@ var package_default = {
134115
134517
  "thread.user-input.respond",
134116
134518
  "thread.turn-intent.run",
134117
134519
  "thread.turn-intent.cancel",
134118
- "thread.turn-queue.resume"
134520
+ "thread.turn-queue.resume",
134521
+ "thread.context.compact"
134119
134522
  ]
134120
134523
  },
134121
134524
  {
@@ -134147,6 +134550,8 @@ var makeCommands = (core, deps) => {
134147
134550
  const { openPanel } = deps;
134148
134551
  const handleHistory = makeHistoryCommand(core);
134149
134552
  const handleReasoning = makeReasoningCommand(core, deps.modelPlan.resolveCurrentModel);
134553
+ const handlePermission = makePermissionCommand(core);
134554
+ const handleCompact = makeCompactCommand(core);
134150
134555
  const targetOf = (binding) => chatOf(core.state, chatKeyOf(binding)).target;
134151
134556
  const executeCommand = async (binding, command, senderId = null, chatKind = void 0) => {
134152
134557
  const chatKey = chatKeyOf(binding);
@@ -134202,6 +134607,12 @@ var makeCommands = (core, deps) => {
134202
134607
  case "history":
134203
134608
  await handleHistory(binding, command, chatKind);
134204
134609
  return;
134610
+ case "permission":
134611
+ await handlePermission(binding, command.arg, senderId);
134612
+ return;
134613
+ case "compact":
134614
+ await handleCompact(binding, command);
134615
+ return;
134205
134616
  case "resume":
134206
134617
  await resumeSession(binding, command.arg);
134207
134618
  return;
@@ -134224,6 +134635,16 @@ var makeCommands = (core, deps) => {
134224
134635
  case "version":
134225
134636
  await deliver(binding, versionReply(t, command.arg));
134226
134637
  return;
134638
+ case "archived": {
134639
+ const arg = command.arg.toLowerCase();
134640
+ const toggles = arg === "on" || arg === "off";
134641
+ const enabled = toggles ? arg === "on" : chatOf(core.state, chatKey).includeArchived === true;
134642
+ if (toggles) commit("chat-updated", { chatKey, includeArchived: enabled });
134643
+ const status = t(enabled ? "archived.on" : "archived.off");
134644
+ await deliver(binding, toggles ? status : `${status}
134645
+ ${t("archived.usage")}`);
134646
+ return;
134647
+ }
134227
134648
  case "status": {
134228
134649
  const threadId = chatOf(core.state, chatKey).threadId ?? void 0;
134229
134650
  const routed = targetOf(binding);
@@ -134241,7 +134662,7 @@ var makeCommands = (core, deps) => {
134241
134662
  );
134242
134663
  return;
134243
134664
  }
134244
- const thread = await threadOrNull3(host.threads, threadId);
134665
+ const thread = await threadOrNull4(host.threads, threadId);
134245
134666
  const running = isSessionRunning(thread?.session?.status);
134246
134667
  await deliver(
134247
134668
  binding,
@@ -134291,7 +134712,7 @@ var makeCommands = (core, deps) => {
134291
134712
  await deliver(binding, t("command.stopNoSession"));
134292
134713
  return;
134293
134714
  }
134294
- const thread = await threadOrNull3(host.threads, threadId);
134715
+ const thread = await threadOrNull4(host.threads, threadId);
134295
134716
  const activeTurnId = thread?.session?.activeTurnId ?? null;
134296
134717
  if (activeTurnId === null) {
134297
134718
  await deliver(binding, t("command.stopNoTurn"));
@@ -135730,7 +136151,7 @@ var makeImCore = (input) => {
135730
136151
  };
135731
136152
 
135732
136153
  // src/conclusionDelivery.ts
135733
- import { threadOrNull as threadOrNull4 } from "@ycodium-ai/plugin-api";
136154
+ import { threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
135734
136155
 
135735
136156
  // src/abortableSleep.ts
135736
136157
  var abortableSleep = (ms, signal) => new Promise((resolve2, reject) => {
@@ -135858,13 +136279,13 @@ ${text3}` : text3;
135858
136279
  const sleep4 = (ms) => abortableSleep(ms, stopped.signal).catch(() => void 0);
135859
136280
  const retryFor = (threadId, owed = ALWAYS_OWED) => ({
135860
136281
  sleep: sleep4,
135861
- shouldContinue: async () => !disposed && owed() && await threadOrNull4(core.host.threads, threadId) !== null
136282
+ shouldContinue: async () => !disposed && owed() && await threadOrNull5(core.host.threads, threadId) !== null
135862
136283
  });
135863
136284
  return {
135864
136285
  completed,
135865
136286
  retryFor,
135866
136287
  prepareConclusion: async (threadId, turnId, successful) => {
135867
- const thread = await threadOrNull4(core.host.threads, threadId);
136288
+ const thread = await threadOrNull5(core.host.threads, threadId);
135868
136289
  const final = thread === null ? void 0 : await latestMessage(
135869
136290
  core.host.threads,
135870
136291
  threadId,
@@ -137390,7 +137811,7 @@ var makeInteractions = (core, deps) => {
137390
137811
  bindingOfThread,
137391
137812
  ready
137392
137813
  } = core;
137393
- const { settleApprovalCard, handleApprovalClick } = deps;
137814
+ const { handleApprovalClick } = deps;
137394
137815
  const flowHost = {
137395
137816
  t,
137396
137817
  state: () => core.state,
@@ -137497,10 +137918,7 @@ var makeInteractions = (core, deps) => {
137497
137918
  );
137498
137919
  return;
137499
137920
  }
137500
- const record4 = core.state.approvals[resolved.requestId];
137501
- if (record4 === void 0 || record4.resolved) return;
137502
- commit("approval-resolved", { requestId: resolved.requestId });
137503
- outbound(record4.chatKey, () => settleApprovalCard(record4, { kind: "elsewhere" }));
137921
+ deps.approvalResolved(resolved.requestId);
137504
137922
  });
137505
137923
  const handleButtonClick = (token, operatorId) => {
137506
137924
  const question = questionFlow.click(token, operatorId);
@@ -139608,14 +140026,8 @@ var splitMessage = (text3, limit) => {
139608
140026
  let rest = text3;
139609
140027
  while (rest.length > limit) {
139610
140028
  const window2 = rest.slice(0, limit);
139611
- const paragraph2 = window2.lastIndexOf("\n\n");
139612
- const line = window2.lastIndexOf("\n");
139613
- const half = limit / 2;
139614
- let cut;
139615
- if (paragraph2 >= half) cut = paragraph2 + 2;
139616
- else if (line >= half) cut = line + 1;
139617
- else if (paragraph2 > 0 || line > 0) cut = Math.max(paragraph2 > 0 ? paragraph2 + 2 : 0, line + 1);
139618
- else {
140029
+ let cut = boundaryCut(window2);
140030
+ if (cut === null) {
139619
140031
  cut = limit;
139620
140032
  const last = window2.charCodeAt(limit - 1);
139621
140033
  if (last >= 55296 && last <= 56319) cut -= 1;
@@ -139626,6 +140038,15 @@ var splitMessage = (text3, limit) => {
139626
140038
  if (rest.length > 0) chunks.push(rest);
139627
140039
  return chunks;
139628
140040
  };
140041
+ var boundaryCut = (window2) => {
140042
+ const paragraph2 = window2.lastIndexOf("\n\n");
140043
+ const line = window2.lastIndexOf("\n");
140044
+ const half = window2.length / 2;
140045
+ if (paragraph2 >= half) return paragraph2 + 2;
140046
+ if (line >= half) return line + 1;
140047
+ if (paragraph2 > 0 || line > 0) return Math.max(paragraph2 > 0 ? paragraph2 + 2 : 0, line + 1);
140048
+ return null;
140049
+ };
139629
140050
 
139630
140051
  // src/larkApi.ts
139631
140052
  var LARK_BASE = "https://open.feishu.cn/open-apis";
@@ -145262,7 +145683,7 @@ var buildApprovalCard = (input) => ({
145262
145683
  var buildApprovalSettledCard = (settlement, t) => ({
145263
145684
  config: { wide_screen_mode: true },
145264
145685
  header: {
145265
- title: { tag: "plain_text", content: t("card.approval.doneTitle") },
145686
+ title: { tag: "plain_text", content: approvalSettlementTitle(settlement, t) },
145266
145687
  template: "wathet"
145267
145688
  },
145268
145689
  elements: [
@@ -145271,7 +145692,7 @@ var buildApprovalSettledCard = (settlement, t) => ({
145271
145692
  elements: [
145272
145693
  {
145273
145694
  tag: "plain_text",
145274
- content: settlement.kind === "chosen" ? t("card.approval.chosen", { label: settlement.label }) : t("card.approval.elsewhere")
145695
+ content: approvalSettlementText(settlement, t)
145275
145696
  }
145276
145697
  ]
145277
145698
  }
@@ -146370,6 +146791,10 @@ var en_default = {
146370
146791
  label: "New messages while busy",
146371
146792
  description: "When a turn is already running, how to handle new plain-text messages. Queue waits for the current turn (Steer is still a button); Steer auto-joins the running turn."
146372
146793
  },
146794
+ approvalTimeoutMinutes: {
146795
+ label: "Approval timeout (minutes)",
146796
+ description: "Unanswered approvals are denied automatically after this many minutes. Defaults to 60; enter an integer from 0 to 1440, and 0 turns it off. An invalid value counts as 60."
146797
+ },
146373
146798
  larkPanelMode: {
146374
146799
  label: "Command panel",
146375
146800
  description: "Which commands /panel (or typing /) lists. Minimal hides low-frequency system items; typing the same command still works."
@@ -146480,6 +146905,7 @@ var en_default = {
146480
146905
  welcome: "Connected. Send text or images to get started, or send /help to see the available commands."
146481
146906
  },
146482
146907
  approval: {
146908
+ timedOut: "No one handled this within the {{minutes}}-minute limit, so it was denied automatically.",
146483
146909
  allow: "Allow",
146484
146910
  deny: "Deny",
146485
146911
  alreadyHandled: "Already handled",
@@ -146557,17 +146983,35 @@ var en_default = {
146557
146983
  current: "Ycodium Chat bridge v{{version}}",
146558
146984
  usage: "Usage: /version (no arguments)."
146559
146985
  },
146986
+ compact: {
146987
+ usage: "Usage: /compact (no arguments).",
146988
+ busy: "A reply is in progress. Wait until this turn finishes, then send /compact again.",
146989
+ unsupported: "The current model can't compact context. Use /model to switch to one that can.",
146990
+ started: "Started compacting the context. I'll post here when it finishes; you can keep sending messages meanwhile.",
146991
+ done: "Context compacted.",
146992
+ failed: "Couldn't compact the context. Try again in a moment.",
146993
+ stuck: "The previous reply failed and its turn is still open. Send /stop to end that turn, then send /compact again.",
146994
+ noSession: "This chat has no current session. Send a message to start one, or use /sessions and /resume to switch to one, then send /compact again."
146995
+ },
146996
+ archived: {
146997
+ on: "On: the session list includes archived sessions. They are listed so you can find them; open one on the web to read it. They can't be continued.",
146998
+ off: "Off: the session list shows only sessions that are not archived.",
146999
+ usage: "Usage: /archived on or /archived off."
147000
+ },
146560
147001
  help: {
147002
+ permission: "\xB7 /permission \u2014 list modes, the mode for new turns and the bot ceiling; /permission number or mode id selects; /permission default returns to the bot setting",
146561
147003
  title: "Commands:",
146562
147004
  continue: "\xB7 /continue \u2014 resume sending after /stop or a failure paused it; the waiting messages go out",
146563
147005
  stop: "\xB7 /stop \u2014 stop the turn that is running; the session stays",
147006
+ compact: "\xB7 /compact \u2014 compact the current session's context (no arguments)",
146564
147007
  status: "\xB7 /status \u2014 what is going on right now",
146565
147008
  version: "\xB7 /version \u2014 the chat bridge's version (no arguments)",
146566
147009
  new: "\xB7 /new \u2014 forget the current session; the next message starts a new one",
146567
147010
  team: "\xB7 /team \u2014 give this project's team a goal with each message; use /repo to pick the project first",
146568
147011
  thread: "\xB7 /thread \u2014 go back to a normal session",
146569
- aliases: "Chinese shortcuts (\u505C / \u7EE7\u7EED\u53D1\u9001 / \u72B6\u6001 / \u65B0\u5BF9\u8BDD / \u4F1A\u8BDD\u5217\u8868 / \u5207\u6362\u4F1A\u8BDD / \u4F1A\u8BDD\u5386\u53F2 / \u6362\u6A21\u578B / \u601D\u8003\u5F3A\u5EA6 / \u8BA1\u5212\u6A21\u5F0F / \u6362\u9879\u76EE / \u5EFA\u7FA4 / \u6362\u56E2\u961F / \u6362\u5BF9\u8BDD) work when the reply language is Chinese.",
147012
+ aliases: "Chinese shortcuts (\u505C / \u7EE7\u7EED\u53D1\u9001 / \u538B\u7F29 / \u72B6\u6001 / \u65B0\u5BF9\u8BDD / \u4F1A\u8BDD\u5217\u8868 / \u5207\u6362\u4F1A\u8BDD / \u4F1A\u8BDD\u5386\u53F2 / \u6362\u6A21\u578B / \u601D\u8003\u5F3A\u5EA6 / \u6743\u9650 / \u8BA1\u5212\u6A21\u5F0F / \u6362\u9879\u76EE / \u5EFA\u7FA4 / \u6362\u56E2\u961F / \u6362\u5BF9\u8BDD) work when the reply language is Chinese.",
146570
147013
  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",
147014
+ archived: "\xB7 /archived on|off \u2014 include archived sessions in this chat's session list; /archived alone shows the current setting",
146571
147015
  resume: "\xB7 /resume number-or-id-prefix \u2014 switch sessions; one from another project switches the project too",
146572
147016
  history: "\xB7 /history [count] \u2014 preview recent messages in the current session, in private chats only (default {{defaultCount}}, maximum {{maxCount}})",
146573
147017
  model: "\xB7 /model \u2014 see the model this chat uses; /model number or /model instance/model switches it",
@@ -146601,6 +147045,28 @@ var en_default = {
146601
147045
  switchReadFailed: "Could not read the new model's reasoning efforts, so the model was not switched. Try again in a moment.",
146602
147046
  listTitlePlain: "Available reasoning efforts:"
146603
147047
  },
147048
+ permission: {
147049
+ current: "Mode for new turns in this chat: {{mode}}",
147050
+ ceiling: "Bot ceiling: {{mode}}",
147051
+ cappedChoice: "This chat's choice: {{mode}} (above the bot ceiling, not in effect)",
147052
+ ceilingMarker: "bot ceiling",
147053
+ unavailable: "unavailable",
147054
+ usage: "Usage: /permission to list modes; /permission number or mode id to select; /permission default to use the bot setting.",
147055
+ nextTurn: "Applies from the next turn in this chat; a running turn keeps its mode.",
147056
+ notAllowed: "Only people on the bot's Allowed people list can change the mode. You can still send /permission to view the modes; ask the bot's admin to change it.",
147057
+ exceedsCeiling: "This mode exceeds the bot ceiling ({{mode}}). The chat setting was not changed. Send /permission to see the modes you can pick.",
147058
+ selected: "Mode for this chat set to: {{mode}}",
147059
+ reset: "Mode choice for this chat cleared; using the bot setting: {{mode}}",
147060
+ saveFailed: "Could not save the mode for this chat. The chat setting was not changed; try again.",
147061
+ alreadyDefault: "This chat has no mode of its own; it follows the bot setting: {{mode}}",
147062
+ numberOutOfRange: "Number {{number}} is out of range; the list is numbered 1\u2013{{max}}.",
147063
+ notFound: '"{{query}}" is not a mode this bot knows.',
147064
+ savedChoice: "This chat's choice: {{mode}}",
147065
+ following: "This chat has no mode of its own; it follows the bot setting.",
147066
+ readOnly: "You're not on the bot's Allowed people list, so you can only view the modes; ask the bot's admin to change them.",
147067
+ readOnlyUnknown: "Your identity couldn't be confirmed, so you can only view the modes; ask the bot's admin to change them.",
147068
+ notAllowedUnknown: "Your identity couldn't be confirmed, so you can't change the mode. You can still send /permission to view the modes; ask the bot's admin to change it."
147069
+ },
146604
147070
  history: {
146605
147071
  usage: "Usage: /history [count] \u2014 1 to {{maxCount}} (larger numbers count as {{maxCount}}), default {{defaultCount}}.",
146606
147072
  directOnly: "In a group this would show the session to everyone. Send /history in a private chat with the bot.",
@@ -146633,7 +147099,8 @@ var en_default = {
146633
147099
  current: "\u2605 current",
146634
147100
  running: "\u25CF running",
146635
147101
  elsewhere: "in another chat",
146636
- web: "web"
147102
+ web: "web",
147103
+ archived: "archived"
146637
147104
  },
146638
147105
  age: {
146639
147106
  justNow: "just now",
@@ -146648,6 +147115,7 @@ var en_default = {
146648
147115
  busy: "A turn is running in this chat \u2014 send /stop first, then switch.",
146649
147116
  already: "This chat is already on that session.",
146650
147117
  gone: "That session was deleted or archived, so it cannot be resumed.",
147118
+ archived: "That session is archived and can't be continued. To just read it, open it on the web; to continue it, unarchive it on the web or in another client first, then switch to it, or send /new to start a new one.",
146651
147119
  runningElsewhere: "That session is running a turn in another chat \u2014 stop it there first.",
146652
147120
  done: 'Switched to "{{title}}". Send a message to keep going from there.',
146653
147121
  doneInProject: 'Switched to project "{{project}}", session "{{title}}". Send a message to keep going from there.',
@@ -146717,6 +147185,7 @@ var en_default = {
146717
147185
  truncated: "\u2026 ({{count}} earlier characters not shown)",
146718
147186
  approval: {
146719
147187
  doneTitle: "Handled",
147188
+ timedOutTitle: "Denied automatically",
146720
147189
  chosen: "Chosen: {{label}}",
146721
147190
  elsewhere: "Already handled elsewhere (the web app or another client)."
146722
147191
  }
@@ -147323,6 +147792,10 @@ var zh_CN_default = {
147323
147792
  label: "\u5FD9\u65F6\u65B0\u6D88\u606F",
147324
147793
  description: "\u5F53\u524D\u4EFB\u52A1\u8FD8\u5728\u8DD1\u65F6\uFF0C\u65B0\u6765\u7684\u666E\u901A\u6587\u5B57\u600E\u4E48\u5904\u7406\u3002\u6392\u961F\u4F1A\u7B49\u5F53\u524D\u8F6E\u7ED3\u675F\uFF08\u53EF\u70B9\u63D2\u8BDD\uFF09\uFF1B\u63D2\u8BDD\u5219\u81EA\u52A8\u5E76\u5165\u5F53\u524D\u8F6E\uFF0C\u65E0\u9700\u6BCF\u6B21\u70B9\u6309\u94AE\u3002"
147325
147794
  },
147795
+ approvalTimeoutMinutes: {
147796
+ label: "\u5BA1\u6279\u8D85\u65F6\uFF08\u5206\u949F\uFF09",
147797
+ description: "\u8FD9\u4E48\u591A\u5206\u949F\u6CA1\u4EBA\u5904\u7406\u7684\u5BA1\u6279\u4F1A\u88AB\u81EA\u52A8\u62D2\u7EDD\u3002\u9ED8\u8BA4 60 \u5206\u949F\uFF1B\u8BF7\u8F93\u5165 0\u20131440 \u7684\u6574\u6570\uFF0C0 \u8868\u793A\u4E0D\u81EA\u52A8\u62D2\u7EDD\uFF1B\u586B\u5199\u65E0\u6548\u65F6\u6309 60 \u5206\u949F\u7B97\u3002"
147798
+ },
147326
147799
  larkPanelMode: {
147327
147800
  label: "\u6307\u4EE4\u9762\u677F",
147328
147801
  description: "\u804A\u5929\u91CC /panel \u6216\u6253 / \u65F6\u5217\u51FA\u7684\u547D\u4EE4\u8303\u56F4\u3002\u7CBE\u7B80\u9690\u85CF\u4F4E\u9891\u7CFB\u7EDF\u9879\uFF1B\u624B\u52A8\u8F93\u5165\u540C\u4E00\u547D\u4EE4\u4ECD\u53EF\u7528\u3002"
@@ -147433,6 +147906,7 @@ var zh_CN_default = {
147433
147906
  welcome: "\u5DF2\u8FDE\u4E0A\u3002\u76F4\u63A5\u53D1\u6587\u5B57\u6216\u56FE\u7247\u5C31\u80FD\u5F00\u59CB\uFF0C\u53D1 /help \u770B\u80FD\u7528\u7684\u547D\u4EE4\u3002"
147434
147907
  },
147435
147908
  approval: {
147909
+ timedOut: "{{minutes}} \u5206\u949F\u65F6\u9650\u5185\u6CA1\u4EBA\u5904\u7406\uFF0C\u5DF2\u81EA\u52A8\u62D2\u7EDD\u3002",
147436
147910
  allow: "\u5141\u8BB8",
147437
147911
  deny: "\u62D2\u7EDD",
147438
147912
  alreadyHandled: "\u5DF2\u5904\u7406",
@@ -147510,17 +147984,35 @@ var zh_CN_default = {
147510
147984
  current: "Ycodium \u804A\u5929\u6865 v{{version}}",
147511
147985
  usage: "\u7528\u6CD5\uFF1A/version\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09\u3002"
147512
147986
  },
147987
+ compact: {
147988
+ usage: "\u7528\u6CD5\uFF1A/compact\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09\u3002",
147989
+ busy: "\u6B63\u5728\u56DE\u590D\uFF0C\u7B49\u8FD9\u4E00\u8F6E\u7ED3\u675F\u540E\u518D\u53D1\u4E00\u6B21 /compact\u3002",
147990
+ unsupported: "\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u538B\u7F29\u4E0A\u4E0B\u6587\uFF0C\u53EF\u5148\u7528 /model \u6362\u4E00\u4E2A\u652F\u6301\u7684\u6A21\u578B\u3002",
147991
+ started: "\u5DF2\u5F00\u59CB\u538B\u7F29\u4E0A\u4E0B\u6587\uFF0C\u5B8C\u6210\u540E\u4F1A\u5728\u8FD9\u91CC\u8BF4\u4E00\u58F0\uFF0C\u671F\u95F4\u7167\u5E38\u53D1\u6D88\u606F\u5C31\u884C\u3002",
147992
+ done: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29\u3002",
147993
+ failed: "\u538B\u7F29\u4E0A\u4E0B\u6587\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
147994
+ stuck: "\u4E0A\u4E00\u8F6E\u56DE\u590D\u51FA\u9519\u4E86\uFF0C\u90A3\u4E00\u8F6E\u8FD8\u6CA1\u7ED3\u675F\u3002\u5148\u53D1 /stop \u7ED3\u675F\u5B83\uFF0C\u518D\u53D1 /compact\u3002",
147995
+ noSession: "\u8FD9\u4E2A\u804A\u5929\u8FD8\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u3002\u53D1\u4E00\u53E5\u8BDD\u5F00\u4E00\u4E2A\uFF0C\u6216\u7528 /sessions \u548C /resume \u5207\u8FC7\u53BB\uFF0C\u4E4B\u540E\u518D\u53D1 /compact\u3002"
147996
+ },
147997
+ archived: {
147998
+ on: "\u5DF2\u5F00\u542F\uFF1A\u4F1A\u8BDD\u5217\u8868\u4F1A\u5217\u51FA\u5DF2\u5F52\u6863\u7684\u4F1A\u8BDD\u3002\u5217\u51FA\u6765\u662F\u4E3A\u4E86\u627E\u5230\u5B83\uFF0C\u770B\u5185\u5BB9\u53BB\u7F51\u9875\uFF0C\u4E0D\u80FD\u63A5\u7740\u804A\u3002",
147999
+ off: "\u5DF2\u5173\u95ED\uFF1A\u4F1A\u8BDD\u5217\u8868\u53EA\u5217\u51FA\u672A\u5F52\u6863\u7684\u4F1A\u8BDD\u3002",
148000
+ usage: "\u7528\u6CD5\uFF1A/archived on \u6216 /archived off\u3002"
148001
+ },
147513
148002
  help: {
148003
+ permission: "\xB7 /permission \u2014\u2014 \u5217\u51FA\u6863\u4F4D\u3001\u65B0\u8F6E\u6B21\u7684\u6863\u4F4D\u4E0E\u673A\u5668\u4EBA\u4E0A\u9650\uFF1B/permission \u7F16\u53F7\u6216\u6863\u4F4D ID \u9009\u62E9\uFF1B/permission default\uFF08\u6216\u300C\u6743\u9650 \u9ED8\u8BA4\u300D\uFF09\u56DE\u5230\u673A\u5668\u4EBA\u8BBE\u7F6E",
147514
148004
  title: "\u80FD\u7528\u7684\u547D\u4EE4\uFF1A",
147515
148005
  continue: "\xB7 /continue \u2014\u2014 /stop \u6216\u51FA\u9519\u6682\u505C\u4E86\u53D1\u9001\u4E4B\u540E\uFF0C\u7528\u5B83\u6062\u590D\uFF0C\u6392\u7740\u7684\u6D88\u606F\u7EE7\u7EED\u53D1\u51FA",
147516
148006
  stop: "\xB7 /stop \u2014\u2014 \u505C\u6389\u6B63\u5728\u8DD1\u7684\u8FD9\u4E00\u8F6E\uFF0C\u4F1A\u8BDD\u7559\u7740",
148007
+ compact: "\xB7 /compact \u2014\u2014 \u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u7684\u4E0A\u4E0B\u6587\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",
147517
148008
  status: "\xB7 /status \u2014\u2014 \u73B0\u5728\u5728\u5E72\u4EC0\u4E48",
147518
148009
  version: "\xB7 /version \u2014\u2014 \u804A\u5929\u6865\u7684\u7248\u672C\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",
147519
148010
  new: "\xB7 /new \u2014\u2014 \u5FD8\u6389\u5F53\u524D\u4F1A\u8BDD\uFF0C\u4E0B\u4E00\u53E5\u5F00\u65B0\u7684",
147520
148011
  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",
147521
148012
  thread: "\xB7 /thread \u2014\u2014 \u5207\u56DE\u666E\u901A\u4F1A\u8BDD",
147522
- aliases: "\u4E2D\u6587\u4E5F\u884C\uFF1A\u505C / \u7EE7\u7EED\u53D1\u9001 / \u72B6\u6001 / \u65B0\u5BF9\u8BDD / \u4F1A\u8BDD\u5217\u8868 / \u5207\u6362\u4F1A\u8BDD / \u4F1A\u8BDD\u5386\u53F2 / \u6362\u6A21\u578B / \u601D\u8003\u5F3A\u5EA6 / \u8BA1\u5212\u6A21\u5F0F / \u6362\u9879\u76EE / \u5EFA\u7FA4 / \u6362\u56E2\u961F / \u6362\u5BF9\u8BDD\u3002",
148013
+ aliases: "\u4E2D\u6587\u4E5F\u884C\uFF1A\u505C / \u7EE7\u7EED\u53D1\u9001 / \u538B\u7F29 / \u72B6\u6001 / \u65B0\u5BF9\u8BDD / \u4F1A\u8BDD\u5217\u8868 / \u5207\u6362\u4F1A\u8BDD / \u4F1A\u8BDD\u5386\u53F2 / \u6362\u6A21\u578B / \u601D\u8003\u5F3A\u5EA6 / \u6743\u9650 / \u8BA1\u5212\u6A21\u5F0F / \u6362\u9879\u76EE / \u5EFA\u7FA4 / \u6362\u56E2\u961F / \u6362\u5BF9\u8BDD\u3002",
147523
148014
  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",
148015
+ archived: "\xB7 /archived on|off \u2014\u2014 \u8BA9\u8FD9\u4E2A\u804A\u5929\u7684\u4F1A\u8BDD\u5217\u8868\u5E26\u4E0A\uFF08on\uFF09\u6216\u4E0D\u5E26\uFF08off\uFF09\u5DF2\u5F52\u6863\u7684\u4F1A\u8BDD\uFF1B/archived \u770B\u5F53\u524D\u5F00\u5173",
147524
148016
  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",
147525
148017
  history: "\xB7 /history [\u6570\u91CF] \u2014\u2014 \u4EC5\u79C1\u804A\uFF1A\u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u6700\u8FD1\u7684\u6D88\u606F\uFF08\u9ED8\u8BA4 {{defaultCount}} \u6761\uFF0C\u6700\u591A {{maxCount}} \u6761\uFF09",
147526
148018
  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",
@@ -147554,6 +148046,28 @@ var zh_CN_default = {
147554
148046
  switchReadFailed: "\u6682\u65F6\u8BFB\u4E0D\u5230\u65B0\u6A21\u578B\u7684\u601D\u8003\u5F3A\u5EA6\u9009\u9879\uFF0C\u6A21\u578B\u6CA1\u6709\u5207\u6362\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
147555
148047
  listTitlePlain: "\u53EF\u9009\u601D\u8003\u5F3A\u5EA6\uFF1A"
147556
148048
  },
148049
+ permission: {
148050
+ current: "\u8FD9\u4E2A\u804A\u5929\u65B0\u8F6E\u6B21\u7684\u6863\u4F4D\uFF1A{{mode}}",
148051
+ ceiling: "\u673A\u5668\u4EBA\u4E0A\u9650\uFF1A{{mode}}",
148052
+ cappedChoice: "\u8FD9\u4E2A\u804A\u5929\u7684\u9009\u62E9\uFF1A{{mode}}\uFF08\u8D85\u8FC7\u673A\u5668\u4EBA\u4E0A\u9650\uFF0C\u6682\u4E0D\u751F\u6548\uFF09",
148053
+ ceilingMarker: "\u673A\u5668\u4EBA\u4E0A\u9650",
148054
+ unavailable: "\u4E0D\u53EF\u9009",
148055
+ usage: "\u7528\u6CD5\uFF1A/permission \u67E5\u770B\u6863\u4F4D\uFF1B/permission \u7F16\u53F7\u6216\u6863\u4F4D ID \u9009\u62E9\uFF1B/permission default\uFF08\u6216\u300C\u6743\u9650 \u9ED8\u8BA4\u300D\uFF09\u56DE\u5230\u673A\u5668\u4EBA\u8BBE\u7F6E\u3002",
148056
+ nextTurn: "\u4ECE\u8FD9\u4E2A\u804A\u5929\u7684\u4E0B\u4E00\u8F6E\u8D77\u751F\u6548\uFF1B\u6B63\u5728\u8FD0\u884C\u7684\u8F6E\u6B21\u4FDD\u6301\u539F\u6863\u4F4D\u3002",
148057
+ notAllowed: "\u53EA\u6709\u673A\u5668\u4EBA\u5141\u8BB8\u540D\u5355\u91CC\u7684\u4EBA\u80FD\u6539\u6863\u4F4D\u3002\u4ECD\u53EF\u53D1 /permission \u67E5\u770B\u6863\u4F4D\uFF1B\u8981\u6539\u8BF7\u8054\u7CFB\u673A\u5668\u4EBA\u7BA1\u7406\u5458\u3002",
148058
+ exceedsCeiling: "\u8FD9\u4E2A\u6863\u4F4D\u8D85\u8FC7\u673A\u5668\u4EBA\u4E0A\u9650\uFF08{{mode}}\uFF09\uFF0C\u6CA1\u6709\u4FEE\u6539\u804A\u5929\u8BBE\u7F6E\u3002\u53D1 /permission \u770B\u53EF\u9009\u6863\u4F4D\u3002",
148059
+ selected: "\u8FD9\u4E2A\u804A\u5929\u7684\u6863\u4F4D\u5DF2\u8BBE\u4E3A\uFF1A{{mode}}",
148060
+ reset: "\u5DF2\u6E05\u9664\u8FD9\u4E2A\u804A\u5929\u7684\u6863\u4F4D\u9009\u62E9\uFF0C\u56DE\u5230\u673A\u5668\u4EBA\u8BBE\u7F6E\uFF1A{{mode}}",
148061
+ saveFailed: "\u65E0\u6CD5\u4FDD\u5B58\u8FD9\u4E2A\u804A\u5929\u7684\u6863\u4F4D\uFF0C\u6CA1\u6709\u4FEE\u6539\u804A\u5929\u8BBE\u7F6E\uFF0C\u8BF7\u91CD\u8BD5\u3002",
148062
+ alreadyDefault: "\u8FD9\u4E2A\u804A\u5929\u672A\u5355\u72EC\u8BBE\u7F6E\u6863\u4F4D\uFF0C\u6B63\u8DDF\u968F\u673A\u5668\u4EBA\u8BBE\u7F6E\uFF1A{{mode}}",
148063
+ numberOutOfRange: "\u7F16\u53F7 {{number}} \u8D85\u51FA\u8303\u56F4\uFF0C\u5217\u8868\u7F16\u53F7\u4E3A 1\u2013{{max}}\u3002",
148064
+ notFound: "\u300C{{query}}\u300D\u4E0D\u662F\u53EF\u9009\u7684\u6863\u4F4D\u3002",
148065
+ savedChoice: "\u8FD9\u4E2A\u804A\u5929\u7684\u9009\u62E9\uFF1A{{mode}}",
148066
+ following: "\u8FD9\u4E2A\u804A\u5929\u672A\u5355\u72EC\u8BBE\u7F6E\u6863\u4F4D\uFF0C\u8DDF\u968F\u673A\u5668\u4EBA\u8BBE\u7F6E\u3002",
148067
+ readOnly: "\u4F60\u4E0D\u5728\u673A\u5668\u4EBA\u5141\u8BB8\u540D\u5355\u91CC\uFF0C\u53EA\u80FD\u67E5\u770B\u6863\u4F4D\uFF1B\u8981\u6539\u8BF7\u8054\u7CFB\u673A\u5668\u4EBA\u7BA1\u7406\u5458\u3002",
148068
+ readOnlyUnknown: "\u65E0\u6CD5\u786E\u8BA4\u4F60\u7684\u8EAB\u4EFD\uFF0C\u53EA\u80FD\u67E5\u770B\u6863\u4F4D\uFF1B\u8981\u6539\u8BF7\u8054\u7CFB\u673A\u5668\u4EBA\u7BA1\u7406\u5458\u3002",
148069
+ notAllowedUnknown: "\u65E0\u6CD5\u786E\u8BA4\u4F60\u7684\u8EAB\u4EFD\uFF0C\u4E0D\u80FD\u6539\u6863\u4F4D\u3002\u4ECD\u53EF\u53D1 /permission \u67E5\u770B\u6863\u4F4D\uFF1B\u8981\u6539\u8BF7\u8054\u7CFB\u673A\u5668\u4EBA\u7BA1\u7406\u5458\u3002"
148070
+ },
147557
148071
  history: {
147558
148072
  usage: "\u7528\u6CD5\uFF1A/history [\u6570\u91CF] \u2014\u2014 1\u2013{{maxCount}} \u6761\uFF08\u8D85\u51FA\u6309 {{maxCount}} \u6761\u7B97\uFF09\uFF0C\u9ED8\u8BA4 {{defaultCount}} \u6761\u3002",
147559
148073
  directOnly: "\u5728\u7FA4\u91CC\u4F1A\u628A\u4F1A\u8BDD\u5185\u5BB9\u53D1\u7ED9\u6240\u6709\u4EBA\u3002\u5728\u4E0E\u673A\u5668\u4EBA\u7684\u79C1\u804A\u91CC\u53D1 /history\u3002",
@@ -147586,7 +148100,8 @@ var zh_CN_default = {
147586
148100
  current: "\u2605 \u5F53\u524D",
147587
148101
  running: "\u25CF \u5728\u8DD1",
147588
148102
  elsewhere: "\u63A5\u5728\u522B\u7684\u804A\u5929",
147589
- web: "\u7F51\u9875"
148103
+ web: "\u7F51\u9875",
148104
+ archived: "\u5DF2\u5F52\u6863"
147590
148105
  },
147591
148106
  age: {
147592
148107
  justNow: "\u521A\u521A",
@@ -147601,6 +148116,7 @@ var zh_CN_default = {
147601
148116
  busy: "\u8FD9\u4E2A\u804A\u5929\u6709\u4E00\u8F6E\u6B63\u5728\u8DD1\u2014\u2014\u5148 /stop\uFF0C\u518D\u5207\u6362\u3002",
147602
148117
  already: "\u8FD9\u4E2A\u804A\u5929\u5DF2\u7ECF\u5728\u90A3\u6761\u4F1A\u8BDD\u91CC\u4E86\u3002",
147603
148118
  gone: "\u90A3\u6761\u4F1A\u8BDD\u5DF2\u88AB\u5220\u9664\u6216\u5F52\u6863\uFF0C\u63A5\u4E0D\u4E0A\u4E86\u3002",
148119
+ archived: "\u90A3\u6761\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF0C\u4E0D\u80FD\u63A5\u7740\u804A\u3002\u53EA\u60F3\u770B\u5185\u5BB9\uFF0C\u53BB\u7F51\u9875\u6253\u5F00\uFF1B\u60F3\u63A5\u7740\u804A\uFF0C\u5148\u5728\u7F51\u9875\u6216\u5176\u4ED6\u5BA2\u6237\u7AEF\u91CC\u53D6\u6D88\u5F52\u6863\uFF0C\u518D\u5207\u8FC7\u53BB\uFF0C\u6216\u53D1 /new \u5F00\u65B0\u4F1A\u8BDD\u3002",
147604
148120
  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",
147605
148121
  done: "\u5DF2\u5207\u5230\u300C{{title}}\u300D\u3002\u53D1\u4E00\u53E5\u8BDD\u5C31\u4ECE\u90A3\u91CC\u63A5\u7740\u804A\u3002",
147606
148122
  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",
@@ -147670,6 +148186,7 @@ var zh_CN_default = {
147670
148186
  truncated: "\u2026\uFF08\u524D\u9762 {{count}} \u4E2A\u5B57\u7B26\u6CA1\u6709\u663E\u793A\uFF09",
147671
148187
  approval: {
147672
148188
  doneTitle: "\u5DF2\u5904\u7406",
148189
+ timedOutTitle: "\u5DF2\u81EA\u52A8\u62D2\u7EDD",
147673
148190
  chosen: "\u5DF2\u9009\uFF1A{{label}}",
147674
148191
  elsewhere: "\u5DF2\u5728\u522B\u5904\u5904\u7406\uFF08\u7F51\u9875\u6216\u5176\u4ED6\u5BA2\u6237\u7AEF\uFF09\u3002"
147675
148192
  }
@@ -148994,7 +149511,7 @@ var makeModelPlan = (core) => {
148994
149511
  };
148995
149512
 
148996
149513
  // src/notifications.ts
148997
- import { readThreadFailed, threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
149514
+ import { readThreadFailed, threadOrNull as threadOrNull6 } from "@ycodium-ai/plugin-api";
148998
149515
 
148999
149516
  // src/conclusionOutbox.ts
149000
149517
  var enqueueConclusion = (core, ledger, binding, selection, send) => core.outbound(
@@ -149156,7 +149673,7 @@ var makeNotifications = (core, deps) => {
149156
149673
  const projectOfThread = async (threadId) => {
149157
149674
  const known = projectByThread.get(threadId);
149158
149675
  if (known !== void 0) return known;
149159
- const thread = await threadOrNull5(host.threads, threadId);
149676
+ const thread = await threadOrNull6(host.threads, threadId);
149160
149677
  if (thread === null) return null;
149161
149678
  if (projectByThread.size >= THREAD_PROJECT_CACHE_LIMIT) {
149162
149679
  const oldest = projectByThread.keys().next().value;
@@ -149862,7 +150379,7 @@ var makeFailureNotices = (core) => {
149862
150379
  // src/replyCards.ts
149863
150380
  import {
149864
150381
  readTurnProgress,
149865
- threadOrNull as threadOrNull6,
150382
+ threadOrNull as threadOrNull7,
149866
150383
  TURN_PROGRESS_TRIGGER_KIND
149867
150384
  } from "@ycodium-ai/plugin-api";
149868
150385
 
@@ -150235,12 +150752,12 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
150235
150752
  (message) => message.role === "user" && message.text.trim() !== ""
150236
150753
  ).catch(() => void 0);
150237
150754
  if (asked !== void 0) return turnTitleOf(asked.text);
150238
- const thread = await threadOrNull6(host.threads, threadId).catch(() => null);
150755
+ const thread = await threadOrNull7(host.threads, threadId).catch(() => null);
150239
150756
  return turnTitleOf(thread?.title ?? "");
150240
150757
  },
150241
150758
  endOf: async (threadId, turnId) => {
150242
150759
  const stopped = stopRequested.delete(threadId);
150243
- const thread = await threadOrNull6(host.threads, threadId).catch(() => null);
150760
+ const thread = await threadOrNull7(host.threads, threadId).catch(() => null);
150244
150761
  const session = thread?.session;
150245
150762
  const outcome = outcomeOfSession(session?.status);
150246
150763
  const errorText2 = failures.forCard(
@@ -150508,7 +151025,7 @@ var makeSendFileTool = (deps) => ({
150508
151025
  });
150509
151026
 
150510
151027
  // src/sessions.ts
150511
- import { threadOrNull as threadOrNull7 } from "@ycodium-ai/plugin-api";
151028
+ import { threadOrNull as threadOrNull8 } from "@ycodium-ai/plugin-api";
150512
151029
  var WEB_LIST_PAGE = 100;
150513
151030
  var shellOf = (thread) => ({
150514
151031
  threadId: thread.id,
@@ -150521,8 +151038,9 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150521
151038
  const { host, t, commit, deliver } = core;
150522
151039
  const listedByChat = /* @__PURE__ */ new Map();
150523
151040
  const pendingAttachByChat = /* @__PURE__ */ new Map();
150524
- const rowFromThread = (thread, chatKey, origin) => {
150525
- if (thread.archivedAt != null) return null;
151041
+ const rowFromThread = (thread, chatKey, origin, includeArchived) => {
151042
+ const archived = thread.archivedAt != null;
151043
+ if (archived && !includeArchived) return null;
150526
151044
  const owner = chatOfThread(core.state, thread.threadId);
150527
151045
  return {
150528
151046
  threadId: thread.threadId,
@@ -150531,10 +151049,11 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150531
151049
  running: isSessionRunning(thread.sessionStatus),
150532
151050
  current: chatOf(core.state, chatKey).threadId === thread.threadId,
150533
151051
  elsewhere: owner !== void 0 && owner !== chatKey,
150534
- origin
151052
+ origin,
151053
+ ...archived ? { archived: true } : {}
150535
151054
  };
150536
151055
  };
150537
- const projectShells = async (projectId) => {
151056
+ const projectShells = async (projectId, includeArchived) => {
150538
151057
  if (projectId === "") return [];
150539
151058
  const shells = [];
150540
151059
  let cursor = null;
@@ -150542,7 +151061,7 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150542
151061
  const result2 = await host.threads.list({
150543
151062
  projectId,
150544
151063
  limit: WEB_LIST_PAGE,
150545
- archived: "exclude",
151064
+ archived: includeArchived ? "include" : "exclude",
150546
151065
  cursor
150547
151066
  });
150548
151067
  for (const shell of result2.items) {
@@ -150562,20 +151081,31 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150562
151081
  const isPluginThread = (threadId, projectId) => originOf(threadId, projectId) === "plugin";
150563
151082
  const sessionRowsFor = async (binding, projectId = binding.projectId, pluginOnly = false) => {
150564
151083
  const chatKey = chatKeyOf(binding);
150565
- const shells = await projectShells(projectId);
151084
+ const includeArchived = chatOf(core.state, chatKey).includeArchived === true;
151085
+ const shells = await projectShells(projectId, includeArchived);
150566
151086
  return shells.flatMap((shell) => {
150567
151087
  if (pluginOnly && !isPluginThread(shell.threadId, projectId)) return [];
150568
- const row = rowFromThread(shell, chatKey, originOf(shell.threadId, projectId));
151088
+ const row = rowFromThread(
151089
+ shell,
151090
+ chatKey,
151091
+ originOf(shell.threadId, projectId),
151092
+ includeArchived
151093
+ );
150569
151094
  return row === null ? [] : [row];
150570
151095
  });
150571
151096
  };
150572
151097
  const rowForThread = async (binding, chatKey, threadId) => {
150573
- const thread = await threadOrNull7(host.threads, threadId, { archived: "include" });
151098
+ const thread = await threadOrNull8(host.threads, threadId, { archived: "include" });
150574
151099
  if (thread === null) return null;
150575
151100
  if (thread.projectId !== binding.projectId && !isPluginThread(threadId, thread.projectId)) {
150576
151101
  return null;
150577
151102
  }
150578
- const row = rowFromThread(shellOf(thread), chatKey, originOf(threadId, thread.projectId));
151103
+ const row = rowFromThread(
151104
+ shellOf(thread),
151105
+ chatKey,
151106
+ originOf(threadId, thread.projectId),
151107
+ chatOf(core.state, chatKey).includeArchived === true
151108
+ );
150579
151109
  return row === null ? null : { ...row, projectId: thread.projectId };
150580
151110
  };
150581
151111
  const resumeRowsFor = async (binding, wanted, listed) => {
@@ -150725,6 +151255,11 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150725
151255
  await deliver(binding, t("resume.gone"));
150726
151256
  return;
150727
151257
  }
151258
+ if (target2.archived === true) {
151259
+ if (confirming) pendingAttachByChat.delete(chatKey);
151260
+ await deliver(binding, t("resume.archived"));
151261
+ return;
151262
+ }
150728
151263
  if (currentBusy) {
150729
151264
  await deliver(binding, t("resume.busy"));
150730
151265
  return;
@@ -151881,12 +152416,12 @@ var makeTelegramAdapter = (deps) => {
151881
152416
  },
151882
152417
  settleApproval: async (binding, cardId, settlement) => {
151883
152418
  const api = await apiFor(binding);
151884
- const line = settlement.kind === "chosen" ? t("card.approval.chosen", { label: settlement.label }) : t("card.approval.elsewhere");
152419
+ const line = approvalSettlementText(settlement, t);
151885
152420
  await withFloodWait(
151886
152421
  () => api.editMessage(
151887
152422
  binding.chatId,
151888
152423
  cardId,
151889
- `${t("card.approval.doneTitle")}
152424
+ `${approvalSettlementTitle(settlement, t)}
151890
152425
  ${line}`
151891
152426
  )
151892
152427
  );
@@ -151975,7 +152510,7 @@ ${line}`
151975
152510
  };
151976
152511
 
151977
152512
  // src/turns.ts
151978
- import { projectOrNull as projectOrNull2, threadOrNull as threadOrNull8 } from "@ycodium-ai/plugin-api";
152513
+ import { projectOrNull as projectOrNull2, threadOrNull as threadOrNull9 } from "@ycodium-ai/plugin-api";
151979
152514
 
151980
152515
  // src/botPreset.ts
151981
152516
  var resolveDefaultPreset = async (chatPreset, bot, host) => {
@@ -152187,7 +152722,7 @@ var makeTurns = (core, deps) => {
152187
152722
  };
152188
152723
  const threadStillUsable = async (binding, threadId) => {
152189
152724
  try {
152190
- const thread = await threadOrNull8(host.threads, threadId, { archived: "include" });
152725
+ const thread = await threadOrNull9(host.threads, threadId, { archived: "include" });
152191
152726
  return threadUsable(binding, thread);
152192
152727
  } catch {
152193
152728
  return true;
@@ -152198,6 +152733,13 @@ var makeTurns = (core, deps) => {
152198
152733
  return waiting !== void 0 && waiting.threadId === threadId ? waiting : void 0;
152199
152734
  };
152200
152735
  const interactionModeOf = (chatKey) => chatOf(core.state, chatKey).plan ? "plan" : "default";
152736
+ const runtimeModeOf = (binding) => {
152737
+ const chatKey = chatKeyOf(binding);
152738
+ return effectiveRuntimeMode(
152739
+ core.bindingForChatKey(chatKey)?.runtimeMode ?? binding.runtimeMode,
152740
+ chatOf(core.state, chatKey).runtimeMode
152741
+ );
152742
+ };
152201
152743
  const ensureThread = async (binding, message) => {
152202
152744
  const chatKey = chatKeyOf(binding);
152203
152745
  let threadId = chatOf(core.state, chatKey).threadId ?? void 0;
@@ -152254,7 +152796,7 @@ var makeTurns = (core, deps) => {
152254
152796
  title: titleFrom(message.text || message.resources[0]?.name || "Chat"),
152255
152797
  modelSelection,
152256
152798
  ...presetId === void 0 ? {} : { presetId },
152257
- runtimeMode: binding.runtimeMode,
152799
+ runtimeMode: runtimeModeOf(binding),
152258
152800
  interactionMode: interactionModeOf(chatKey),
152259
152801
  branch: null,
152260
152802
  worktreePath: null
@@ -152389,7 +152931,7 @@ var makeTurns = (core, deps) => {
152389
152931
  )
152390
152932
  },
152391
152933
  titleSeed: titleFrom(text3 || attachments[0]?.name || "Chat"),
152392
- runtimeMode: binding.runtimeMode,
152934
+ runtimeMode: runtimeModeOf(binding),
152393
152935
  interactionMode: interactionModeOf(chatKey),
152394
152936
  ...intentAction !== void 0 ? { intentAction } : {}
152395
152937
  },
@@ -152935,7 +153477,8 @@ var buildSettingsView = (config2, adapters) => {
152935
153477
  const secrets = {};
152936
153478
  for (const column of columns) {
152937
153479
  if (column.kind === "password") secrets[column.key] = hasStoredSecret(raw, column.key);
152938
- else values[column.key] = readString(raw, column.key);
153480
+ else
153481
+ values[column.key] = column.kind === "number" && typeof raw[column.key] === "number" ? String(raw[column.key]) : readString(raw, column.key) || column.defaultValue || "";
152939
153482
  }
152940
153483
  const adapter = adapters.find((candidate) => candidate.platform === platform);
152941
153484
  if (adapter === void 0) {
@@ -152951,13 +153494,14 @@ var buildSettingsView = (config2, adapters) => {
152951
153494
  continue;
152952
153495
  }
152953
153496
  const bot = readBotView(rowId, raw, adapter);
153497
+ const issues = validateBotSettings(bot, adapter);
152954
153498
  bots.push({
152955
153499
  rowId,
152956
153500
  platform,
152957
153501
  configured: adapter.isConfigured(bot),
152958
153502
  values,
152959
153503
  secrets,
152960
- issues: adapter.validate?.(bot) ?? [],
153504
+ issues,
152961
153505
  publicFields: adapter.publicFields?.(bot) ?? []
152962
153506
  });
152963
153507
  }
@@ -154459,24 +155003,32 @@ var normalizeApiBaseUrl = (value2) => {
154459
155003
  return raw.replace(/\/+$/u, "");
154460
155004
  };
154461
155005
  var byteLength = (value2) => Buffer.byteLength(value2, "utf8");
155006
+ var prefixWithinBytes = (value2, maxBytes) => {
155007
+ let bytes = 0;
155008
+ let end = 0;
155009
+ for (const char of value2) {
155010
+ const size = byteLength(char);
155011
+ if (bytes + size > maxBytes && end > 0) break;
155012
+ bytes += size;
155013
+ end += char.length;
155014
+ }
155015
+ return value2.slice(0, end);
155016
+ };
154462
155017
  var splitUtf8ByBytes = (text3, maxBytes = WECOM_APP_TEXT_MAX_BYTES) => {
154463
155018
  const value2 = typeof text3 === "string" ? text3 : "";
154464
155019
  if (value2 === "") return [""];
154465
155020
  const chunks = [];
154466
- let current = "";
154467
- let currentBytes = 0;
154468
- for (const char of value2) {
154469
- const size = byteLength(char);
154470
- if (currentBytes + size > maxBytes && current.length > 0) {
154471
- chunks.push(current);
154472
- current = char;
154473
- currentBytes = size;
154474
- continue;
155021
+ let rest = value2;
155022
+ while (rest.length > 0) {
155023
+ const window2 = prefixWithinBytes(rest, maxBytes);
155024
+ if (window2.length === rest.length) {
155025
+ chunks.push(rest);
155026
+ break;
154475
155027
  }
154476
- current += char;
154477
- currentBytes += size;
155028
+ const cut = boundaryCut(window2) ?? window2.length;
155029
+ chunks.push(rest.slice(0, cut));
155030
+ rest = rest.slice(cut);
154478
155031
  }
154479
- if (current.length > 0) chunks.push(current);
154480
155032
  return chunks;
154481
155033
  };
154482
155034
  var parseInvalidUsers = (value2) => typeof value2 === "string" ? value2.split("|").map((user) => user.trim()).filter(Boolean) : [];
@@ -155941,7 +156493,7 @@ var makeWecomAppAdapter = (deps) => {
155941
156493
  // src/index.ts
155942
156494
  var definition = {
155943
156495
  id: "im",
155944
- apiVersion: 30,
156496
+ apiVersion: 31,
155945
156497
  // 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
155946
156498
  activate: async (ctx, testing = {}) => {
155947
156499
  const { dispatchSleep } = testing;
@@ -156173,6 +156725,7 @@ var definition = {
156173
156725
  setLanguage(parseReplyLanguage(config2[REPLY_LANGUAGE_FIELD]));
156174
156726
  inbound.connectionTests.retainRows(Object.keys(config2[BOTS_FIELD] ?? {}));
156175
156727
  connections.reconcile();
156728
+ await approvals.reconcileTimeouts();
156176
156729
  await deliveryHttp.apply(config2);
156177
156730
  });
156178
156731
  }