@ycodium-ai/plugin-im 0.2.3252 → 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 = {};
@@ -126699,11 +126717,25 @@ var readChatPatch = (data) => {
126699
126717
  else if (isRecord(data["model"])) {
126700
126718
  const instanceId = nonEmpty3(data["model"]["instanceId"]);
126701
126719
  const model = nonEmpty3(data["model"]["model"]);
126702
- if (instanceId !== null && model !== null) patch.model = { instanceId, model };
126720
+ if (instanceId !== null && model !== null) {
126721
+ const options = data["model"]["options"];
126722
+ patch.model = {
126723
+ instanceId,
126724
+ model,
126725
+ ...Array.isArray(options) ? {
126726
+ options: options.flatMap(
126727
+ (entry) => isRecord(entry) && nonEmpty3(entry["id"]) !== null && (typeof entry["value"] === "string" || typeof entry["value"] === "boolean") ? [{ id: String(entry["id"]), value: entry["value"] }] : []
126728
+ )
126729
+ } : {}
126730
+ };
126731
+ }
126703
126732
  }
126704
126733
  if (data["preset"] === null) patch.preset = null;
126705
126734
  else if (nonEmpty3(data["preset"]) !== null) patch.preset = nonEmpty3(data["preset"]);
126706
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"];
126707
126739
  if (data["chatKind"] === null) patch.chatKind = null;
126708
126740
  else if (data["chatKind"] === "private" || data["chatKind"] === "group")
126709
126741
  patch.chatKind = data["chatKind"];
@@ -127439,14 +127471,13 @@ var pickChatChoice = (arg, choices, matches, qualified) => {
127439
127471
  const named = choices.filter((choice) => matches(choice, needle));
127440
127472
  return named.length === 1 ? named[0] ?? null : null;
127441
127473
  };
127474
+ var chatChoiceLine = (index, current, label) => `${String(index + 1)}. ${current ? "\u2605 " : ""}${label}`;
127442
127475
  var formatChatChoices = (choices, label, isCurrent, text3) => {
127443
127476
  const shown = choices.slice(0, CHAT_CHOICE_LIST_MAX);
127444
127477
  return [
127445
127478
  "",
127446
127479
  text3.title,
127447
- ...shown.map(
127448
- (choice, index) => `${String(index + 1)}. ${isCurrent(choice) ? "\u2605 " : ""}${label(choice)}`
127449
- ),
127480
+ ...shown.map((choice, index) => chatChoiceLine(index, isCurrent(choice), label(choice))),
127450
127481
  ...choices.length > shown.length ? [text3.more(choices.length - shown.length)] : [],
127451
127482
  "",
127452
127483
  text3.footer
@@ -127509,15 +127540,22 @@ var pickModel = (arg, choices) => {
127509
127540
  };
127510
127541
 
127511
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
+ ];
127512
127556
  var BOTS_FIELD = "bots";
127513
127557
  var REPLY_LANGUAGE_FIELD = "replyLanguage";
127514
127558
  var parseGroupMention = (value2) => value2 === "never" || value2 === "ambient" ? value2 : "always";
127515
- var INBOUND_RUNTIME_MODES = /* @__PURE__ */ new Set([
127516
- "approval-required",
127517
- "auto-accept-edits",
127518
- "auto",
127519
- "full-access"
127520
- ]);
127521
127559
  var DEFAULT_INBOUND_RUNTIME_MODE = "auto-accept-edits";
127522
127560
  var QUEUE_MODE_OPTIONS = [
127523
127561
  { value: "queue", labelKey: "settings.queueMode.queue" },
@@ -127652,12 +127690,7 @@ var buildBotColumns = (adapters, models = [], catalogComplete = true, presets =
127652
127690
  kind: "select",
127653
127691
  labelKey: "column.runtimeMode.label",
127654
127692
  descriptionKey: "column.runtimeMode.description",
127655
- options: [
127656
- { value: "approval-required", labelKey: "settings.runtimeMode.approvalRequired" },
127657
- { value: "auto-accept-edits", labelKey: "settings.runtimeMode.autoAcceptEdits" },
127658
- { value: "auto", labelKey: "settings.runtimeMode.auto" },
127659
- { value: "full-access", labelKey: "settings.runtimeMode.fullAccess" }
127660
- ]
127693
+ options: RUNTIME_MODE_OPTIONS
127661
127694
  },
127662
127695
  {
127663
127696
  key: "groupMention",
@@ -127679,6 +127712,16 @@ var buildBotColumns = (adapters, models = [], catalogComplete = true, presets =
127679
127712
  descriptionKey: "column.queueMode.description",
127680
127713
  options: [...QUEUE_MODE_OPTIONS]
127681
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
+ },
127682
127725
  {
127683
127726
  key: "larkPanelMode",
127684
127727
  group: "more",
@@ -127728,6 +127771,8 @@ var buildSettingsCard = (adapters) => {
127728
127771
  var readBotView = (rowId, row, adapter) => {
127729
127772
  const values = {};
127730
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"]);
127731
127776
  const proxyValue = readString(row, "proxy");
127732
127777
  return {
127733
127778
  rowId,
@@ -127754,7 +127799,7 @@ var readBot = (adapterFor, rowId, row) => {
127754
127799
  ...defaultPreset === "" ? {} : { defaultPreset },
127755
127800
  accessMode: parseAccessMode(readString(row, "accessMode")),
127756
127801
  allowedSenders,
127757
- runtimeMode: INBOUND_RUNTIME_MODES.has(requested) ? requested : DEFAULT_INBOUND_RUNTIME_MODE,
127802
+ runtimeMode: isRuntimeMode(requested) ? requested : DEFAULT_INBOUND_RUNTIME_MODE,
127758
127803
  queueMode: parseQueueMode(readString(row, "queueMode")),
127759
127804
  larkPanelMode: parseLarkPanelMode(readString(row, "larkPanelMode")),
127760
127805
  wecomNumberedHints: parseOnOff(readString(row, "wecomNumberedHints"), true)
@@ -127787,6 +127832,145 @@ var isAllowedSender = (bot, sender, chatKind) => {
127787
127832
  return bot.allowedSenders.has(sender);
127788
127833
  };
127789
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
+
127790
127974
  // src/numberedReply.ts
127791
127975
  var optionIndexFrom = (text3, count) => {
127792
127976
  const trimmed = text3.trim();
@@ -127891,34 +128075,52 @@ var makeReplyAnchors = (host) => {
127891
128075
  var makeApprovals = (core, anchors) => {
127892
128076
  const { host, t, adapterFor, commit, outbound, bindingForChatKey, bindingOfThread, ready } = core;
127893
128077
  const settleApprovalCard = async (approval, settlement) => {
127894
- if (approval.cardId === void 0) return;
127895
128078
  const binding = bindingForChatKey(approval.chatKey);
127896
128079
  const settle = binding === void 0 ? void 0 : adapterFor(binding.platform)?.settleApproval;
127897
- if (binding === void 0 || settle === void 0) return;
127898
- try {
127899
- await settle(binding, approval.cardId, settlement);
127900
- } catch (error) {
127901
- 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));
127902
128091
  }
127903
128092
  };
127904
- const answerApproval = async (approval, decision, label) => {
128093
+ const finishApproval = async (approval, settlement, respond) => {
127905
128094
  commit("approval-resolved", { requestId: approval.requestId });
127906
- await host.approvals.respond({
127907
- requestId: crypto.randomUUID(),
127908
- threadId: approval.threadId,
127909
- approvalRequestId: approval.requestId,
127910
- // 落盘按钮保留 string 形状;合法决定仍由端口 schema 校验。
127911
- decision
127912
- });
127913
- host.log.info("chat answered an approval", { threadId: approval.threadId, decision });
127914
- await settleApprovalCard(core.state.approvals[approval.requestId] ?? approval, {
127915
- kind: "chosen",
127916
- 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 });
127917
128113
  });
127918
128114
  };
128115
+ const settled = (approval) => approval.resolved || timeouts.responding(approval.requestId);
127919
128116
  const answerByToken = async (token) => {
127920
128117
  const found = findApprovalByToken(core.state, token);
127921
- 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
+ }
127922
128124
  await answerApproval(found.approval, found.button.decision, found.button.label);
127923
128125
  };
127924
128126
  const sendApprovalCard = async (binding, adapter, threadId, offer) => {
@@ -127935,10 +128137,7 @@ var makeApprovals = (core, anchors) => {
127935
128137
  replyHint: approvalReplyHint(t, binding)
127936
128138
  });
127937
128139
  host.log.info("chat asked for an approval", { threadId, requestId: offer.requestId });
127938
- if (cardId === null) return;
127939
- commit("approval-carded", { requestId: offer.requestId, cardId });
127940
- const record4 = core.state.approvals[offer.requestId];
127941
- if (record4?.resolved === true) await settleApprovalCard(record4, { kind: "elsewhere" });
128140
+ if (cardId !== null) commit("approval-carded", { requestId: offer.requestId, cardId });
127942
128141
  } catch (error) {
127943
128142
  host.log.warn("chat could not ask for the approval", { error: describeError(error) });
127944
128143
  }
@@ -127977,6 +128176,7 @@ var makeApprovals = (core, anchors) => {
127977
128176
  });
127978
128177
  return {
127979
128178
  requestId: approval.requestId,
128179
+ createdAt: approval.createdAt,
127980
128180
  title: approval.appName ?? "Ycodium",
127981
128181
  // 正文用这道题自己的说明;事件的 summary 只是宿主拼的「哪条会话在等批准」,最后才用。
127982
128182
  body: approval.detail ?? approval.summary ?? event.summary,
@@ -127986,12 +128186,14 @@ var makeApprovals = (core, anchors) => {
127986
128186
  );
127987
128187
  for (const offer of offers) {
127988
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);
127989
128191
  }
127990
128192
  });
127991
128193
  const handleApprovalClick = (token, operatorId) => {
127992
128194
  const found = findApprovalByToken(core.state, token);
127993
128195
  if (found === void 0) return null;
127994
- if (found.approval.resolved) return t("approval.alreadyHandled");
128196
+ if (settled(found.approval)) return t("approval.alreadyHandled");
127995
128197
  const target2 = bindingForChatKey(found.approval.chatKey);
127996
128198
  if (target2 === void 0 || !isAllowedSender(target2, operatorId, chatOf(core.state, found.approval.chatKey).chatKind)) {
127997
128199
  host.log.warn("chat ignored an approval click from someone not on the allow list", {
@@ -128007,7 +128209,18 @@ var makeApprovals = (core, anchors) => {
128007
128209
  );
128008
128210
  return null;
128009
128211
  };
128010
- 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
+ };
128011
128224
  };
128012
128225
 
128013
128226
  // src/chatQueue.ts
@@ -128133,7 +128346,8 @@ var sessionLine = (row, position, now, t) => {
128133
128346
  row.current ? t("sessions.badge.current") : null,
128134
128347
  row.running ? t("sessions.badge.running") : null,
128135
128348
  row.elsewhere ? t("sessions.badge.elsewhere") : null,
128136
- 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
128137
128351
  ].filter((badge) => badge !== null);
128138
128352
  return [`${String(position)}. ${title}`, age, ...badges, shortId(row.threadId)].filter((part) => part !== "").join(" \xB7 ");
128139
128353
  };
@@ -128340,7 +128554,7 @@ var makeChatSetup = (core, deps) => {
128340
128554
  };
128341
128555
 
128342
128556
  // src/commands.ts
128343
- import { threadOrNull as threadOrNull2 } from "@ycodium-ai/plugin-api";
128557
+ import { threadOrNull as threadOrNull4 } from "@ycodium-ai/plugin-api";
128344
128558
 
128345
128559
  // ../../node_modules/.pnpm/effect@4.0.0-beta.103_patch_hash=78523ded7c3dfc3efd3cec0247f4960d00eaa3d3c96a2077457694a4d8b453c6/node_modules/effect/dist/Pipeable.js
128346
128560
  var pipeArguments = (self2, args2) => {
@@ -133558,7 +133772,16 @@ var ParsedChatCommand = Union2([
133558
133772
  SimpleChatCommand,
133559
133773
  Struct({ kind: Literal2("sessions"), arg: optionalKey2(String4) }),
133560
133774
  Struct({
133561
- kind: Literals(["resume", "model", "repo", "version"]),
133775
+ kind: Literals([
133776
+ "resume",
133777
+ "model",
133778
+ "reasoning",
133779
+ "repo",
133780
+ "version",
133781
+ "permission",
133782
+ "compact",
133783
+ "archived"
133784
+ ]),
133562
133785
  arg: String4
133563
133786
  }),
133564
133787
  Struct({ kind: Literal2("history"), count: NullOr(Number5) }),
@@ -133608,6 +133831,15 @@ var parseChatCommand = (input) => {
133608
133831
  if (/^\/status$/i.test(trimmed)) return { kind: "status" };
133609
133832
  const versionArg = trimmed.match(/^\/version(?:\s+([\s\S]*))?$/iu);
133610
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() };
133611
133843
  if (/^\/new$/i.test(trimmed)) return { kind: "new" };
133612
133844
  if (/^\/help$/i.test(trimmed)) return { kind: "help" };
133613
133845
  if (/^\/panel$/i.test(trimmed)) return { kind: "panel" };
@@ -133631,6 +133863,20 @@ var parseChatCommand = (input) => {
133631
133863
  }
133632
133864
  const model = modelArgument(input.text, input.chinese);
133633
133865
  if (model !== void 0) return { kind: "model", arg: model };
133866
+ const reasoning = commandArgument(
133867
+ trimmed,
133868
+ input.chinese,
133869
+ /^\/reasoning(?:\s+([\s\S]*))?$/i,
133870
+ /^思考强度(?:\s+([\s\S]*))?$/
133871
+ );
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 };
133634
133880
  const preset = trimmed.match(/^\/preset(?:\s+([\s\S]*))?$/iu);
133635
133881
  if (preset !== null)
133636
133882
  return {
@@ -133660,15 +133906,19 @@ var helpText = (t, options = {}) => [
133660
133906
  t("help.title"),
133661
133907
  t("help.stop"),
133662
133908
  t("help.continue"),
133909
+ t("help.compact"),
133663
133910
  t("help.status"),
133664
133911
  t("help.version"),
133665
133912
  t("help.new"),
133666
133913
  t("help.sessions"),
133914
+ t("help.archived"),
133667
133915
  t("help.resume"),
133668
133916
  t("help.history", HISTORY_COUNTS),
133669
133917
  t("help.syncNote"),
133670
133918
  t("help.model"),
133919
+ t("help.reasoning"),
133671
133920
  t("help.preset"),
133921
+ t("help.permission"),
133672
133922
  t("help.plan"),
133673
133923
  t("help.repo"),
133674
133924
  t("help.group"),
@@ -133678,6 +133928,90 @@ var helpText = (t, options = {}) => [
133678
133928
  ...options.panel === true ? ["", t("help.panel")] : []
133679
133929
  ].join("\n");
133680
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
+
133681
134015
  // src/deadline.ts
133682
134016
  var withDeadline = async (timeoutMs, load) => {
133683
134017
  const signal = AbortSignal.timeout(timeoutMs);
@@ -133847,10 +134181,275 @@ ${body}`);
133847
134181
  };
133848
134182
  };
133849
134183
 
134184
+ // src/reasoningCommand.ts
134185
+ import { modelSelectionStringOption as modelSelectionStringOption2 } from "@ycodium-ai/plugin-api/executor";
134186
+
134187
+ // src/currentModel.ts
134188
+ import { threadOrNull as threadOrNull3 } from "@ycodium-ai/plugin-api";
134189
+
134190
+ // src/botModel.ts
134191
+ var resolveDefaultModel = async (bot, chatModel, projectDefault, providers, knownInstances) => {
134192
+ if (chatModel !== null) return chatModel;
134193
+ if (bot.defaultModel !== void 0) {
134194
+ const instances = knownInstances === void 0 ? await providers.list({}).catch(() => null) : knownInstances;
134195
+ if (!isModelCatalogComplete(instances) || modelCatalogHas(modelChoices(instances ?? []), bot.defaultModel)) {
134196
+ return bot.defaultModel;
134197
+ }
134198
+ }
134199
+ return projectDefault;
134200
+ };
134201
+
134202
+ // src/threadUsable.ts
134203
+ var threadUsable = (binding, thread) => thread !== null && thread.archivedAt == null && thread.projectId === binding.projectId;
134204
+
134205
+ // src/currentModel.ts
134206
+ var makeCurrentModelResolver = (core) => {
134207
+ const { host } = core;
134208
+ return async (binding, {
134209
+ instances,
134210
+ onReadError = (error) => {
134211
+ throw error;
134212
+ }
134213
+ } = {}) => {
134214
+ const chat = chatOf(core.state, chatKeyOf(binding));
134215
+ if (chat.threadId !== null) {
134216
+ const thread = await threadOrNull3(host.threads, chat.threadId, { archived: "include" }).catch(
134217
+ onReadError
134218
+ );
134219
+ if (threadUsable(binding, thread))
134220
+ return { selection: thread.modelSelection, threadId: chat.threadId };
134221
+ }
134222
+ if (chat.model !== null) return { selection: chat.model, threadId: null };
134223
+ const project = binding.projectId === "" ? null : await host.projects.get({ projectId: binding.projectId }).catch(onReadError);
134224
+ return {
134225
+ selection: await resolveDefaultModel(
134226
+ binding,
134227
+ chat.model,
134228
+ project?.defaultModelSelection ?? null,
134229
+ host.providers.instances,
134230
+ instances
134231
+ ),
134232
+ threadId: null
134233
+ };
134234
+ };
134235
+ };
134236
+ var saveModelSelection = async (core, binding, threadId, model) => {
134237
+ if (threadId !== null) {
134238
+ try {
134239
+ await core.host.threads.update({
134240
+ requestId: crypto.randomUUID(),
134241
+ threadId,
134242
+ modelSelection: model
134243
+ });
134244
+ } catch (error) {
134245
+ core.host.log.warn("chat could not switch the thread model", {
134246
+ error: describeError(error)
134247
+ });
134248
+ return false;
134249
+ }
134250
+ }
134251
+ core.commit("chat-updated", { chatKey: chatKeyOf(binding), model });
134252
+ return true;
134253
+ };
134254
+
134255
+ // src/reasoningOption.ts
134256
+ import { modelOptionLabelKind, modelSelectionStringOption } from "@ycodium-ai/plugin-api/executor";
134257
+ var isReasoningOption = (id) => modelOptionLabelKind(id) !== void 0;
134258
+ var reasoningOption = (model) => model.options.find((option2) => isReasoningOption(option2.id) && option2.values.length > 0);
134259
+ var selectedReasoning = (selection) => {
134260
+ const option2 = selection?.options?.find((entry) => isReasoningOption(entry.id));
134261
+ if (option2 === void 0) return void 0;
134262
+ const value2 = modelSelectionStringOption(selection, option2.id);
134263
+ return value2 === void 0 ? void 0 : { id: option2.id, value: value2 };
134264
+ };
134265
+
134266
+ // src/reasoningCommand.ts
134267
+ var isDefaultArgument = (arg, chinese) => arg.toLowerCase() === "default" || chinese && arg === "\u9ED8\u8BA4";
134268
+ var makeReasoningCommand = (core, currentModel) => {
134269
+ const { host, t, deliver } = core;
134270
+ const readOptions = async (binding) => {
134271
+ const resolved = await currentModel(binding);
134272
+ const selection = resolved.selection;
134273
+ const catalog = selection === null ? null : await host.providers.models.list({ instanceId: selection.instanceId });
134274
+ const model = catalog?.models.find((entry) => entry.slug === selection?.model);
134275
+ return { ...resolved, option: model === void 0 ? void 0 : reasoningOption(model) };
134276
+ };
134277
+ return async (binding, arg) => {
134278
+ const resolved = await readOptions(binding).catch(() => null);
134279
+ if (resolved === null) {
134280
+ host.log.warn("chat could not read reasoning options");
134281
+ await deliver(binding, t("reasoning.readFailed"));
134282
+ return;
134283
+ }
134284
+ const { selection, threadId, option: option2 } = resolved;
134285
+ if (selection === null) {
134286
+ await deliver(binding, t("reasoning.noModel"));
134287
+ return;
134288
+ }
134289
+ const wanted = arg.trim();
134290
+ if (isDefaultArgument(wanted, t.language === "zh-CN")) {
134291
+ if (selectedReasoning(selection) === void 0) {
134292
+ await deliver(binding, t(threadId === null ? "reasoning.resetNext" : "reasoning.resetNow"));
134293
+ return;
134294
+ }
134295
+ const next2 = {
134296
+ ...selection,
134297
+ options: (selection.options ?? []).filter((entry) => !isReasoningOption(entry.id))
134298
+ };
134299
+ if (!await saveModelSelection(core, binding, threadId, next2)) {
134300
+ await deliver(binding, t("reasoning.setFailed"));
134301
+ return;
134302
+ }
134303
+ await deliver(binding, t(threadId === null ? "reasoning.resetNext" : "reasoning.resetNow"));
134304
+ return;
134305
+ }
134306
+ if (option2 === void 0) {
134307
+ await deliver(binding, t("reasoning.unsupported"));
134308
+ return;
134309
+ }
134310
+ if (arg === "") {
134311
+ const current = modelSelectionStringOption2(selection, option2.id);
134312
+ const listed = current !== void 0 && option2.values.includes(current);
134313
+ const title = current === void 0 ? "reasoning.listTitleDefault" : listed ? "reasoning.listTitle" : "reasoning.listTitlePlain";
134314
+ const text3 = [
134315
+ ...current === void 0 ? [] : [t(listed ? "reasoning.current" : "reasoning.currentUnlisted", { value: current })],
134316
+ ...formatChatChoices(
134317
+ option2.values,
134318
+ (entry) => entry,
134319
+ (entry) => entry === current,
134320
+ {
134321
+ title: t(title),
134322
+ more: (count) => t("reasoning.more", { count }),
134323
+ footer: t("reasoning.usage")
134324
+ }
134325
+ )
134326
+ ].join("\n").trim();
134327
+ await deliver(binding, text3);
134328
+ return;
134329
+ }
134330
+ const value2 = option2.values.find((entry) => entry === wanted) ?? pickChatChoice(wanted, option2.values, (entry, needle) => entry.toLowerCase() === needle);
134331
+ if (value2 === null) {
134332
+ const shown = option2.values.slice(0, CHAT_CHOICE_LIST_MAX);
134333
+ await deliver(
134334
+ binding,
134335
+ [
134336
+ /^\d+$/.test(wanted) ? t("reasoning.numberOutOfRange", { number: wanted, max: shown.length }) : t("reasoning.notFound", { query: arg }),
134337
+ t("reasoning.usage"),
134338
+ t("reasoning.choices", { values: shown.join(" / ") }),
134339
+ ...option2.values.length > shown.length ? [t("reasoning.more", { count: option2.values.length - shown.length })] : []
134340
+ ].join("\n")
134341
+ );
134342
+ return;
134343
+ }
134344
+ const next = {
134345
+ ...selection,
134346
+ options: [
134347
+ ...(selection.options ?? []).filter((entry) => !isReasoningOption(entry.id)),
134348
+ { id: option2.id, value: value2 }
134349
+ ]
134350
+ };
134351
+ if (!await saveModelSelection(core, binding, threadId, next)) {
134352
+ await deliver(binding, t("reasoning.setFailed"));
134353
+ return;
134354
+ }
134355
+ await deliver(
134356
+ binding,
134357
+ t(threadId === null ? "reasoning.setNext" : "reasoning.setNow", { value: value2 })
134358
+ );
134359
+ };
134360
+ };
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
+
133850
134449
  // package.json
133851
134450
  var package_default = {
133852
134451
  name: "@ycodium-ai/plugin-im",
133853
- version: "0.2.3252",
134452
+ version: "0.2.3265",
133854
134453
  description: "Talk to your agents from Telegram, WeCom, Lark, or WeChat (experimental).",
133855
134454
  keywords: [
133856
134455
  "im",
@@ -133918,7 +134517,8 @@ var package_default = {
133918
134517
  "thread.user-input.respond",
133919
134518
  "thread.turn-intent.run",
133920
134519
  "thread.turn-intent.cancel",
133921
- "thread.turn-queue.resume"
134520
+ "thread.turn-queue.resume",
134521
+ "thread.context.compact"
133922
134522
  ]
133923
134523
  },
133924
134524
  {
@@ -133949,6 +134549,9 @@ var makeCommands = (core, deps) => {
133949
134549
  const { handleModel, handlePlan } = deps.modelPlan;
133950
134550
  const { openPanel } = deps;
133951
134551
  const handleHistory = makeHistoryCommand(core);
134552
+ const handleReasoning = makeReasoningCommand(core, deps.modelPlan.resolveCurrentModel);
134553
+ const handlePermission = makePermissionCommand(core);
134554
+ const handleCompact = makeCompactCommand(core);
133952
134555
  const targetOf = (binding) => chatOf(core.state, chatKeyOf(binding)).target;
133953
134556
  const executeCommand = async (binding, command, senderId = null, chatKind = void 0) => {
133954
134557
  const chatKey = chatKeyOf(binding);
@@ -134004,12 +134607,21 @@ var makeCommands = (core, deps) => {
134004
134607
  case "history":
134005
134608
  await handleHistory(binding, command, chatKind);
134006
134609
  return;
134610
+ case "permission":
134611
+ await handlePermission(binding, command.arg, senderId);
134612
+ return;
134613
+ case "compact":
134614
+ await handleCompact(binding, command);
134615
+ return;
134007
134616
  case "resume":
134008
134617
  await resumeSession(binding, command.arg);
134009
134618
  return;
134010
134619
  case "model":
134011
134620
  await handleModel(binding, command.arg);
134012
134621
  return;
134622
+ case "reasoning":
134623
+ await handleReasoning(binding, command.arg);
134624
+ return;
134013
134625
  case "preset":
134014
134626
  await deps.handlePreset(binding, command);
134015
134627
  return;
@@ -134023,6 +134635,16 @@ var makeCommands = (core, deps) => {
134023
134635
  case "version":
134024
134636
  await deliver(binding, versionReply(t, command.arg));
134025
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
+ }
134026
134648
  case "status": {
134027
134649
  const threadId = chatOf(core.state, chatKey).threadId ?? void 0;
134028
134650
  const routed = targetOf(binding);
@@ -134040,7 +134662,7 @@ var makeCommands = (core, deps) => {
134040
134662
  );
134041
134663
  return;
134042
134664
  }
134043
- const thread = await threadOrNull2(host.threads, threadId);
134665
+ const thread = await threadOrNull4(host.threads, threadId);
134044
134666
  const running = isSessionRunning(thread?.session?.status);
134045
134667
  await deliver(
134046
134668
  binding,
@@ -134090,7 +134712,7 @@ var makeCommands = (core, deps) => {
134090
134712
  await deliver(binding, t("command.stopNoSession"));
134091
134713
  return;
134092
134714
  }
134093
- const thread = await threadOrNull2(host.threads, threadId);
134715
+ const thread = await threadOrNull4(host.threads, threadId);
134094
134716
  const activeTurnId = thread?.session?.activeTurnId ?? null;
134095
134717
  if (activeTurnId === null) {
134096
134718
  await deliver(binding, t("command.stopNoTurn"));
@@ -135529,7 +136151,7 @@ var makeImCore = (input) => {
135529
136151
  };
135530
136152
 
135531
136153
  // src/conclusionDelivery.ts
135532
- import { threadOrNull as threadOrNull3 } from "@ycodium-ai/plugin-api";
136154
+ import { threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
135533
136155
 
135534
136156
  // src/abortableSleep.ts
135535
136157
  var abortableSleep = (ms, signal) => new Promise((resolve2, reject) => {
@@ -135657,13 +136279,13 @@ ${text3}` : text3;
135657
136279
  const sleep4 = (ms) => abortableSleep(ms, stopped.signal).catch(() => void 0);
135658
136280
  const retryFor = (threadId, owed = ALWAYS_OWED) => ({
135659
136281
  sleep: sleep4,
135660
- shouldContinue: async () => !disposed && owed() && await threadOrNull3(core.host.threads, threadId) !== null
136282
+ shouldContinue: async () => !disposed && owed() && await threadOrNull5(core.host.threads, threadId) !== null
135661
136283
  });
135662
136284
  return {
135663
136285
  completed,
135664
136286
  retryFor,
135665
136287
  prepareConclusion: async (threadId, turnId, successful) => {
135666
- const thread = await threadOrNull3(core.host.threads, threadId);
136288
+ const thread = await threadOrNull5(core.host.threads, threadId);
135667
136289
  const final = thread === null ? void 0 : await latestMessage(
135668
136290
  core.host.threads,
135669
136291
  threadId,
@@ -137189,7 +137811,7 @@ var makeInteractions = (core, deps) => {
137189
137811
  bindingOfThread,
137190
137812
  ready
137191
137813
  } = core;
137192
- const { settleApprovalCard, handleApprovalClick } = deps;
137814
+ const { handleApprovalClick } = deps;
137193
137815
  const flowHost = {
137194
137816
  t,
137195
137817
  state: () => core.state,
@@ -137296,10 +137918,7 @@ var makeInteractions = (core, deps) => {
137296
137918
  );
137297
137919
  return;
137298
137920
  }
137299
- const record4 = core.state.approvals[resolved.requestId];
137300
- if (record4 === void 0 || record4.resolved) return;
137301
- commit("approval-resolved", { requestId: resolved.requestId });
137302
- outbound(record4.chatKey, () => settleApprovalCard(record4, { kind: "elsewhere" }));
137921
+ deps.approvalResolved(resolved.requestId);
137303
137922
  });
137304
137923
  const handleButtonClick = (token, operatorId) => {
137305
137924
  const question = questionFlow.click(token, operatorId);
@@ -139407,14 +140026,8 @@ var splitMessage = (text3, limit) => {
139407
140026
  let rest = text3;
139408
140027
  while (rest.length > limit) {
139409
140028
  const window2 = rest.slice(0, limit);
139410
- const paragraph2 = window2.lastIndexOf("\n\n");
139411
- const line = window2.lastIndexOf("\n");
139412
- const half = limit / 2;
139413
- let cut;
139414
- if (paragraph2 >= half) cut = paragraph2 + 2;
139415
- else if (line >= half) cut = line + 1;
139416
- else if (paragraph2 > 0 || line > 0) cut = Math.max(paragraph2 > 0 ? paragraph2 + 2 : 0, line + 1);
139417
- else {
140029
+ let cut = boundaryCut(window2);
140030
+ if (cut === null) {
139418
140031
  cut = limit;
139419
140032
  const last = window2.charCodeAt(limit - 1);
139420
140033
  if (last >= 55296 && last <= 56319) cut -= 1;
@@ -139425,6 +140038,15 @@ var splitMessage = (text3, limit) => {
139425
140038
  if (rest.length > 0) chunks.push(rest);
139426
140039
  return chunks;
139427
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
+ };
139428
140050
 
139429
140051
  // src/larkApi.ts
139430
140052
  var LARK_BASE = "https://open.feishu.cn/open-apis";
@@ -145061,7 +145683,7 @@ var buildApprovalCard = (input) => ({
145061
145683
  var buildApprovalSettledCard = (settlement, t) => ({
145062
145684
  config: { wide_screen_mode: true },
145063
145685
  header: {
145064
- title: { tag: "plain_text", content: t("card.approval.doneTitle") },
145686
+ title: { tag: "plain_text", content: approvalSettlementTitle(settlement, t) },
145065
145687
  template: "wathet"
145066
145688
  },
145067
145689
  elements: [
@@ -145070,7 +145692,7 @@ var buildApprovalSettledCard = (settlement, t) => ({
145070
145692
  elements: [
145071
145693
  {
145072
145694
  tag: "plain_text",
145073
- content: settlement.kind === "chosen" ? t("card.approval.chosen", { label: settlement.label }) : t("card.approval.elsewhere")
145695
+ content: approvalSettlementText(settlement, t)
145074
145696
  }
145075
145697
  ]
145076
145698
  }
@@ -146169,6 +146791,10 @@ var en_default = {
146169
146791
  label: "New messages while busy",
146170
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."
146171
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
+ },
146172
146798
  larkPanelMode: {
146173
146799
  label: "Command panel",
146174
146800
  description: "Which commands /panel (or typing /) lists. Minimal hides low-frequency system items; typing the same command still works."
@@ -146279,6 +146905,7 @@ var en_default = {
146279
146905
  welcome: "Connected. Send text or images to get started, or send /help to see the available commands."
146280
146906
  },
146281
146907
  approval: {
146908
+ timedOut: "No one handled this within the {{minutes}}-minute limit, so it was denied automatically.",
146282
146909
  allow: "Allow",
146283
146910
  deny: "Deny",
146284
146911
  alreadyHandled: "Already handled",
@@ -146356,20 +146983,39 @@ var en_default = {
146356
146983
  current: "Ycodium Chat bridge v{{version}}",
146357
146984
  usage: "Usage: /version (no arguments)."
146358
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
+ },
146359
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",
146360
147003
  title: "Commands:",
146361
147004
  continue: "\xB7 /continue \u2014 resume sending after /stop or a failure paused it; the waiting messages go out",
146362
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)",
146363
147007
  status: "\xB7 /status \u2014 what is going on right now",
146364
147008
  version: "\xB7 /version \u2014 the chat bridge's version (no arguments)",
146365
147009
  new: "\xB7 /new \u2014 forget the current session; the next message starts a new one",
146366
147010
  team: "\xB7 /team \u2014 give this project's team a goal with each message; use /repo to pick the project first",
146367
147011
  thread: "\xB7 /thread \u2014 go back to a normal session",
146368
- 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 / \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.",
146369
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",
146370
147015
  resume: "\xB7 /resume number-or-id-prefix \u2014 switch sessions; one from another project switches the project too",
146371
147016
  history: "\xB7 /history [count] \u2014 preview recent messages in the current session, in private chats only (default {{defaultCount}}, maximum {{maxCount}})",
146372
147017
  model: "\xB7 /model \u2014 see the model this chat uses; /model number or /model instance/model switches it",
147018
+ reasoning: "\xB7 /reasoning \u2014 see the reasoning effort for this chat's current model; /reasoning value or /reasoning number switches it; /reasoning default resets it to the model default",
146373
147019
  preset: "\xB7 /preset \u2014 see the preset this chat uses for new sessions; /preset number or /preset name-or-id switches it; /preset none clears the chat choice",
146374
147020
  plan: "\xB7 /plan \u2014 toggle plan mode (the agent proposes a plan instead of acting); /plan on, /plan off set it",
146375
147021
  panel: "Send /panel to open the command panel with a button for every command.",
@@ -146377,6 +147023,50 @@ var en_default = {
146377
147023
  group: "\xB7 /group name \u2014 Lark only: the bot creates a group and adds you (bots on other platforms cannot)",
146378
147024
  syncNote: "\xB7 After a private chat attaches a session, web user text and final assistant text sync here (groups never sync)."
146379
147025
  },
147026
+ reasoning: {
147027
+ current: "Current reasoning effort: {{value}}",
147028
+ currentUnlisted: "Current reasoning effort: {{value}} (no longer in the list; it stays in effect until you change it)",
147029
+ resetNow: "Reasoning effort reset to the model default. The current session uses it from the next turn; new sessions in this chat use it too.",
147030
+ resetNext: "Reasoning effort reset to the model default. New sessions in this chat will use it.",
147031
+ notFound: '"{{query}}" is not an available reasoning effort.',
147032
+ numberOutOfRange: "Number {{number}} is out of range; choose 1\u2013{{max}}.",
147033
+ listTitle: "Available reasoning efforts (\u2605 current):",
147034
+ listTitleDefault: "Available reasoning efforts (none chosen; the model default is used):",
147035
+ more: "{{count}} more not shown.",
147036
+ usage: "Usage: /reasoning \u2014 see the reasoning effort for this chat's current model; /reasoning value or /reasoning number switches it; /reasoning default resets it to the model default.",
147037
+ choices: "Choices: {{values}}.",
147038
+ unsupported: "The current model does not support switching the reasoning effort.",
147039
+ readFailed: "Could not read the reasoning options. Try again in a moment.",
147040
+ setFailed: "Switching the reasoning effort failed. Try again in a moment.",
147041
+ setNow: "Reasoning effort set to {{value}}. The current session uses it from the next turn; new sessions in this chat use it too.",
147042
+ setNext: "Reasoning effort set to {{value}}. New sessions in this chat will use it.",
147043
+ cleared: "The new model does not support the previous reasoning effort ({{value}}). It was cleared; the model default will be used.",
147044
+ noModel: "This chat has no model yet. Pick one with /model first.",
147045
+ switchReadFailed: "Could not read the new model's reasoning efforts, so the model was not switched. Try again in a moment.",
147046
+ listTitlePlain: "Available reasoning efforts:"
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
+ },
146380
147070
  history: {
146381
147071
  usage: "Usage: /history [count] \u2014 1 to {{maxCount}} (larger numbers count as {{maxCount}}), default {{defaultCount}}.",
146382
147072
  directOnly: "In a group this would show the session to everyone. Send /history in a private chat with the bot.",
@@ -146409,7 +147099,8 @@ var en_default = {
146409
147099
  current: "\u2605 current",
146410
147100
  running: "\u25CF running",
146411
147101
  elsewhere: "in another chat",
146412
- web: "web"
147102
+ web: "web",
147103
+ archived: "archived"
146413
147104
  },
146414
147105
  age: {
146415
147106
  justNow: "just now",
@@ -146424,6 +147115,7 @@ var en_default = {
146424
147115
  busy: "A turn is running in this chat \u2014 send /stop first, then switch.",
146425
147116
  already: "This chat is already on that session.",
146426
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.",
146427
147119
  runningElsewhere: "That session is running a turn in another chat \u2014 stop it there first.",
146428
147120
  done: 'Switched to "{{title}}". Send a message to keep going from there.',
146429
147121
  doneInProject: 'Switched to project "{{project}}", session "{{title}}". Send a message to keep going from there.',
@@ -146493,6 +147185,7 @@ var en_default = {
146493
147185
  truncated: "\u2026 ({{count}} earlier characters not shown)",
146494
147186
  approval: {
146495
147187
  doneTitle: "Handled",
147188
+ timedOutTitle: "Denied automatically",
146496
147189
  chosen: "Chosen: {{label}}",
146497
147190
  elsewhere: "Already handled elsewhere (the web app or another client)."
146498
147191
  }
@@ -147099,6 +147792,10 @@ var zh_CN_default = {
147099
147792
  label: "\u5FD9\u65F6\u65B0\u6D88\u606F",
147100
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"
147101
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
+ },
147102
147799
  larkPanelMode: {
147103
147800
  label: "\u6307\u4EE4\u9762\u677F",
147104
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"
@@ -147209,6 +147906,7 @@ var zh_CN_default = {
147209
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"
147210
147907
  },
147211
147908
  approval: {
147909
+ timedOut: "{{minutes}} \u5206\u949F\u65F6\u9650\u5185\u6CA1\u4EBA\u5904\u7406\uFF0C\u5DF2\u81EA\u52A8\u62D2\u7EDD\u3002",
147212
147910
  allow: "\u5141\u8BB8",
147213
147911
  deny: "\u62D2\u7EDD",
147214
147912
  alreadyHandled: "\u5DF2\u5904\u7406",
@@ -147286,20 +147984,39 @@ var zh_CN_default = {
147286
147984
  current: "Ycodium \u804A\u5929\u6865 v{{version}}",
147287
147985
  usage: "\u7528\u6CD5\uFF1A/version\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09\u3002"
147288
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
+ },
147289
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",
147290
148004
  title: "\u80FD\u7528\u7684\u547D\u4EE4\uFF1A",
147291
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",
147292
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",
147293
148008
  status: "\xB7 /status \u2014\u2014 \u73B0\u5728\u5728\u5E72\u4EC0\u4E48",
147294
148009
  version: "\xB7 /version \u2014\u2014 \u804A\u5929\u6865\u7684\u7248\u672C\uFF08\u4E0D\u5E26\u53C2\u6570\uFF09",
147295
148010
  new: "\xB7 /new \u2014\u2014 \u5FD8\u6389\u5F53\u524D\u4F1A\u8BDD\uFF0C\u4E0B\u4E00\u53E5\u5F00\u65B0\u7684",
147296
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",
147297
148012
  thread: "\xB7 /thread \u2014\u2014 \u5207\u56DE\u666E\u901A\u4F1A\u8BDD",
147298
- 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 / \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",
147299
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",
147300
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",
147301
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",
147302
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",
148019
+ reasoning: "\xB7 /reasoning \u2014\u2014 \u770B\u8FD9\u4E2A\u804A\u5929\u5F53\u524D\u6A21\u578B\u7684\u601D\u8003\u5F3A\u5EA6\uFF1B/reasoning \u53D6\u503C \u6216 /reasoning \u7F16\u53F7 \u5207\u6362\uFF1B/reasoning default \u6062\u590D\u6A21\u578B\u9ED8\u8BA4",
147303
148020
  preset: "\xB7 /preset \u2014\u2014 \u770B\u8FD9\u4E2A\u804A\u5929\u65B0\u4F1A\u8BDD\u7528\u7684\u9884\u8BBE\uFF1B/preset \u7F16\u53F7 \u6216 /preset \u540D\u5B57\u6216 id \u5207\u6362\uFF1B/preset none \u6E05\u6389\u804A\u5929\u9009\u62E9",
147304
148021
  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",
147305
148022
  panel: "\u53D1 /panel \u6253\u5F00\u53EF\u70B9\u7684\u547D\u4EE4\u9762\u677F\u3002",
@@ -147307,6 +148024,50 @@ var zh_CN_default = {
147307
148024
  group: "\xB7 /group \u7FA4\u540D \u2014\u2014 \u4EC5\u98DE\u4E66\uFF1A\u673A\u5668\u4EBA\u5EFA\u4E00\u4E2A\u7FA4\u5E76\u628A\u4F60\u62C9\u8FDB\u53BB\uFF08\u522B\u7684\u5E73\u53F0\u7684\u673A\u5668\u4EBA\u5EFA\u4E0D\u4E86\u7FA4\uFF09",
147308
148025
  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"
147309
148026
  },
148027
+ reasoning: {
148028
+ current: "\u5F53\u524D\u601D\u8003\u5F3A\u5EA6\uFF1A{{value}}",
148029
+ currentUnlisted: "\u5F53\u524D\u601D\u8003\u5F3A\u5EA6\uFF1A{{value}}\uFF08\u5DF2\u4E0D\u5728\u53EF\u9009\u5217\u8868\uFF0C\u6362\u6389\u4E4B\u524D\u4ECD\u751F\u6548\uFF09",
148030
+ resetNow: "\u601D\u8003\u5F3A\u5EA6\u5DF2\u6062\u590D\u4E3A\u6A21\u578B\u9ED8\u8BA4\uFF0C\u5F53\u524D\u4F1A\u8BDD\u4ECE\u4E0B\u4E00\u8F6E\u8D77\u4F7F\u7528\u5B83\uFF0C\u8FD9\u4E2A\u804A\u5929\u7684\u65B0\u4F1A\u8BDD\u4E5F\u4F7F\u7528\u5B83\u3002",
148031
+ resetNext: "\u601D\u8003\u5F3A\u5EA6\u5DF2\u6062\u590D\u4E3A\u6A21\u578B\u9ED8\u8BA4\uFF0C\u8FD9\u4E2A\u804A\u5929\u7684\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u5B83\u3002",
148032
+ notFound: "\u300C{{query}}\u300D\u4E0D\u662F\u53EF\u9009\u7684\u601D\u8003\u5F3A\u5EA6\u3002",
148033
+ numberOutOfRange: "\u7F16\u53F7 {{number}} \u8D85\u51FA\u8303\u56F4\uFF0C\u53EF\u9009 1\u2013{{max}}\u3002",
148034
+ listTitle: "\u53EF\u9009\u601D\u8003\u5F3A\u5EA6\uFF08\u2605 \u5F53\u524D\uFF09\uFF1A",
148035
+ listTitleDefault: "\u53EF\u9009\u601D\u8003\u5F3A\u5EA6\uFF08\u672A\u624B\u52A8\u9009\u62E9\uFF0C\u5F53\u524D\u7528\u6A21\u578B\u9ED8\u8BA4\uFF09\uFF1A",
148036
+ more: "\u8FD8\u6709 {{count}} \u9879\u672A\u5217\u51FA\u3002",
148037
+ usage: "\u7528\u6CD5\uFF1A/reasoning \u2014\u2014 \u770B\u8FD9\u4E2A\u804A\u5929\u5F53\u524D\u6A21\u578B\u7684\u601D\u8003\u5F3A\u5EA6\uFF1B/reasoning \u53D6\u503C \u6216 /reasoning \u7F16\u53F7 \u5207\u6362\uFF1B/reasoning default \u6062\u590D\u6A21\u578B\u9ED8\u8BA4\u3002",
148038
+ choices: "\u53EF\u9009\uFF1A{{values}}\u3002",
148039
+ unsupported: "\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u5207\u6362\u601D\u8003\u5F3A\u5EA6\u3002",
148040
+ readFailed: "\u6682\u65F6\u8BFB\u4E0D\u5230\u601D\u8003\u5F3A\u5EA6\u9009\u9879\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
148041
+ setFailed: "\u5207\u6362\u601D\u8003\u5F3A\u5EA6\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
148042
+ setNow: "\u601D\u8003\u5F3A\u5EA6\u5DF2\u8BBE\u4E3A {{value}}\uFF0C\u5F53\u524D\u4F1A\u8BDD\u4ECE\u4E0B\u4E00\u8F6E\u8D77\u4F7F\u7528\u5B83\uFF0C\u8FD9\u4E2A\u804A\u5929\u7684\u65B0\u4F1A\u8BDD\u4E5F\u4F7F\u7528\u5B83\u3002",
148043
+ setNext: "\u601D\u8003\u5F3A\u5EA6\u5DF2\u8BBE\u4E3A {{value}}\uFF0C\u8FD9\u4E2A\u804A\u5929\u7684\u65B0\u4F1A\u8BDD\u5C06\u4F7F\u7528\u5B83\u3002",
148044
+ cleared: "\u65B0\u6A21\u578B\u4E0D\u652F\u6301\u539F\u6765\u7684\u601D\u8003\u5F3A\u5EA6\uFF08{{value}}\uFF09\uFF0C\u5DF2\u6E05\u9664\uFF0C\u6539\u7528\u6A21\u578B\u9ED8\u8BA4\u3002",
148045
+ noModel: "\u8FD9\u4E2A\u804A\u5929\u8FD8\u6CA1\u6709\u6A21\u578B\uFF0C\u5148\u7528 /model \u9009\u4E00\u4E2A\u3002",
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",
148047
+ listTitlePlain: "\u53EF\u9009\u601D\u8003\u5F3A\u5EA6\uFF1A"
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
+ },
147310
148071
  history: {
147311
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",
147312
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",
@@ -147339,7 +148100,8 @@ var zh_CN_default = {
147339
148100
  current: "\u2605 \u5F53\u524D",
147340
148101
  running: "\u25CF \u5728\u8DD1",
147341
148102
  elsewhere: "\u63A5\u5728\u522B\u7684\u804A\u5929",
147342
- web: "\u7F51\u9875"
148103
+ web: "\u7F51\u9875",
148104
+ archived: "\u5DF2\u5F52\u6863"
147343
148105
  },
147344
148106
  age: {
147345
148107
  justNow: "\u521A\u521A",
@@ -147354,6 +148116,7 @@ var zh_CN_default = {
147354
148116
  busy: "\u8FD9\u4E2A\u804A\u5929\u6709\u4E00\u8F6E\u6B63\u5728\u8DD1\u2014\u2014\u5148 /stop\uFF0C\u518D\u5207\u6362\u3002",
147355
148117
  already: "\u8FD9\u4E2A\u804A\u5929\u5DF2\u7ECF\u5728\u90A3\u6761\u4F1A\u8BDD\u91CC\u4E86\u3002",
147356
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",
147357
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",
147358
148121
  done: "\u5DF2\u5207\u5230\u300C{{title}}\u300D\u3002\u53D1\u4E00\u53E5\u8BDD\u5C31\u4ECE\u90A3\u91CC\u63A5\u7740\u804A\u3002",
147359
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",
@@ -147423,6 +148186,7 @@ var zh_CN_default = {
147423
148186
  truncated: "\u2026\uFF08\u524D\u9762 {{count}} \u4E2A\u5B57\u7B26\u6CA1\u6709\u663E\u793A\uFF09",
147424
148187
  approval: {
147425
148188
  doneTitle: "\u5DF2\u5904\u7406",
148189
+ timedOutTitle: "\u5DF2\u81EA\u52A8\u62D2\u7EDD",
147426
148190
  chosen: "\u5DF2\u9009\uFF1A{{label}}",
147427
148191
  elsewhere: "\u5DF2\u5728\u522B\u5904\u5904\u7406\uFF08\u7F51\u9875\u6216\u5176\u4ED6\u5BA2\u6237\u7AEF\uFF09\u3002"
147428
148192
  }
@@ -148665,47 +149429,17 @@ var makeLarkAdapter = (deps) => {
148665
149429
  };
148666
149430
 
148667
149431
  // src/modelPlan.ts
148668
- import { threadOrNull as threadOrNull4 } from "@ycodium-ai/plugin-api";
148669
-
148670
- // src/botModel.ts
148671
- var resolveDefaultModel = async (bot, chatModel, projectDefault, providers, knownInstances) => {
148672
- if (chatModel !== null) return chatModel;
148673
- if (bot.defaultModel !== void 0) {
148674
- const instances = knownInstances === void 0 ? await providers.list({}).catch(() => null) : knownInstances;
148675
- if (!isModelCatalogComplete(instances) || modelCatalogHas(modelChoices(instances ?? []), bot.defaultModel)) {
148676
- return bot.defaultModel;
148677
- }
148678
- }
148679
- return projectDefault;
148680
- };
148681
-
148682
- // src/modelPlan.ts
148683
- var makeModelPlan = (core, deps) => {
149432
+ var makeModelPlan = (core) => {
148684
149433
  const { host, t, commit, deliver } = core;
148685
- const { threadStillUsable } = deps;
149434
+ const resolveCurrentModel = makeCurrentModelResolver(core);
148686
149435
  const currentModelOf = async (binding, instances) => {
148687
- const chat = chatOf(core.state, chatKeyOf(binding));
148688
- if (chat.threadId !== null) {
148689
- const thread = await threadOrNull4(host.threads, chat.threadId).catch(() => null);
148690
- if (thread !== null) {
148691
- return {
148692
- instanceId: thread.modelSelection.instanceId,
148693
- model: thread.modelSelection.model
148694
- };
148695
- }
148696
- }
148697
- if (chat.model !== null) return chat.model;
148698
- const project = binding.projectId === "" ? null : await host.projects.get({ projectId: binding.projectId }).catch(() => null);
148699
- return resolveDefaultModel(
148700
- binding,
148701
- chat.model,
148702
- project?.defaultModelSelection ?? null,
148703
- host.providers.instances,
148704
- instances
148705
- );
149436
+ const { selection } = await resolveCurrentModel(binding, {
149437
+ instances,
149438
+ onReadError: () => null
149439
+ });
149440
+ return selection;
148706
149441
  };
148707
149442
  const handleModel = async (binding, arg) => {
148708
- const chatKey = chatKeyOf(binding);
148709
149443
  const instances = await host.providers.instances.list({}).catch(() => null);
148710
149444
  const choices = instances === null ? [] : modelChoices(instances);
148711
149445
  if (arg === "") {
@@ -148721,25 +149455,42 @@ var makeModelPlan = (core, deps) => {
148721
149455
  await deliver(binding, t("model.notFound", { query: arg }));
148722
149456
  return;
148723
149457
  }
148724
- const selection = { instanceId: picked.instanceId, model: picked.model };
148725
- commit("chat-updated", { chatKey, model: selection });
148726
- const threadId = chatOf(core.state, chatKey).threadId;
148727
- if (threadId !== null && await threadStillUsable(binding, threadId)) {
149458
+ const { selection: current, threadId } = await resolveCurrentModel(binding, {
149459
+ instances,
149460
+ onReadError: () => null
149461
+ });
149462
+ const previous = selectedReasoning(current);
149463
+ let carried;
149464
+ if (previous !== void 0) {
148728
149465
  try {
148729
- await host.threads.update({
148730
- requestId: crypto.randomUUID(),
148731
- threadId,
148732
- modelSelection: selection
148733
- });
148734
- } catch (error) {
148735
- host.log.warn("chat could not switch the thread model", { error: describeError(error) });
148736
- await deliver(binding, t("model.setFailed"));
149466
+ const catalog = await host.providers.models.list({ instanceId: picked.instanceId });
149467
+ const model = catalog.models.find((entry) => entry.slug === picked.model);
149468
+ const option2 = model === void 0 ? void 0 : reasoningOption(model);
149469
+ if (option2 !== void 0 && option2.values.includes(previous.value))
149470
+ carried = { id: option2.id, value: previous.value };
149471
+ } catch {
149472
+ host.log.warn("chat could not read reasoning options for a model switch");
149473
+ await deliver(binding, t("reasoning.switchReadFailed"));
148737
149474
  return;
148738
149475
  }
148739
- await deliver(binding, t("model.setNow", { model: modelLabel(picked) }));
149476
+ }
149477
+ const selection = {
149478
+ instanceId: picked.instanceId,
149479
+ model: picked.model,
149480
+ ...carried === void 0 ? {} : { options: [carried] }
149481
+ };
149482
+ const cleared = previous !== void 0 && carried === void 0 ? [t("reasoning.cleared", { value: previous.value })] : [];
149483
+ if (!await saveModelSelection(core, binding, threadId, selection)) {
149484
+ await deliver(binding, t("model.setFailed"));
148740
149485
  return;
148741
149486
  }
148742
- await deliver(binding, t("model.setNext", { model: modelLabel(picked) }));
149487
+ await deliver(
149488
+ binding,
149489
+ [
149490
+ t(threadId === null ? "model.setNext" : "model.setNow", { model: modelLabel(picked) }),
149491
+ ...cleared
149492
+ ].join("\n")
149493
+ );
148743
149494
  };
148744
149495
  const handlePlan = async (binding, mode) => {
148745
149496
  if (mode === "invalid") {
@@ -148756,11 +149507,11 @@ var makeModelPlan = (core, deps) => {
148756
149507
  commit("chat-updated", { chatKey, plan: next });
148757
149508
  await deliver(binding, t(next ? "plan.on" : "plan.off"));
148758
149509
  };
148759
- return { currentModelOf, handleModel, handlePlan };
149510
+ return { currentModelOf, resolveCurrentModel, handleModel, handlePlan };
148760
149511
  };
148761
149512
 
148762
149513
  // src/notifications.ts
148763
- import { readThreadFailed, threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
149514
+ import { readThreadFailed, threadOrNull as threadOrNull6 } from "@ycodium-ai/plugin-api";
148764
149515
 
148765
149516
  // src/conclusionOutbox.ts
148766
149517
  var enqueueConclusion = (core, ledger, binding, selection, send) => core.outbound(
@@ -148922,7 +149673,7 @@ var makeNotifications = (core, deps) => {
148922
149673
  const projectOfThread = async (threadId) => {
148923
149674
  const known = projectByThread.get(threadId);
148924
149675
  if (known !== void 0) return known;
148925
- const thread = await threadOrNull5(host.threads, threadId);
149676
+ const thread = await threadOrNull6(host.threads, threadId);
148926
149677
  if (thread === null) return null;
148927
149678
  if (projectByThread.size >= THREAD_PROJECT_CACHE_LIMIT) {
148928
149679
  const oldest = projectByThread.keys().next().value;
@@ -149628,7 +150379,7 @@ var makeFailureNotices = (core) => {
149628
150379
  // src/replyCards.ts
149629
150380
  import {
149630
150381
  readTurnProgress,
149631
- threadOrNull as threadOrNull6,
150382
+ threadOrNull as threadOrNull7,
149632
150383
  TURN_PROGRESS_TRIGGER_KIND
149633
150384
  } from "@ycodium-ai/plugin-api";
149634
150385
 
@@ -150001,12 +150752,12 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
150001
150752
  (message) => message.role === "user" && message.text.trim() !== ""
150002
150753
  ).catch(() => void 0);
150003
150754
  if (asked !== void 0) return turnTitleOf(asked.text);
150004
- const thread = await threadOrNull6(host.threads, threadId).catch(() => null);
150755
+ const thread = await threadOrNull7(host.threads, threadId).catch(() => null);
150005
150756
  return turnTitleOf(thread?.title ?? "");
150006
150757
  },
150007
150758
  endOf: async (threadId, turnId) => {
150008
150759
  const stopped = stopRequested.delete(threadId);
150009
- const thread = await threadOrNull6(host.threads, threadId).catch(() => null);
150760
+ const thread = await threadOrNull7(host.threads, threadId).catch(() => null);
150010
150761
  const session = thread?.session;
150011
150762
  const outcome = outcomeOfSession(session?.status);
150012
150763
  const errorText2 = failures.forCard(
@@ -150274,7 +151025,7 @@ var makeSendFileTool = (deps) => ({
150274
151025
  });
150275
151026
 
150276
151027
  // src/sessions.ts
150277
- import { threadOrNull as threadOrNull7 } from "@ycodium-ai/plugin-api";
151028
+ import { threadOrNull as threadOrNull8 } from "@ycodium-ai/plugin-api";
150278
151029
  var WEB_LIST_PAGE = 100;
150279
151030
  var shellOf = (thread) => ({
150280
151031
  threadId: thread.id,
@@ -150287,8 +151038,9 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150287
151038
  const { host, t, commit, deliver } = core;
150288
151039
  const listedByChat = /* @__PURE__ */ new Map();
150289
151040
  const pendingAttachByChat = /* @__PURE__ */ new Map();
150290
- const rowFromThread = (thread, chatKey, origin) => {
150291
- 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;
150292
151044
  const owner = chatOfThread(core.state, thread.threadId);
150293
151045
  return {
150294
151046
  threadId: thread.threadId,
@@ -150297,10 +151049,11 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150297
151049
  running: isSessionRunning(thread.sessionStatus),
150298
151050
  current: chatOf(core.state, chatKey).threadId === thread.threadId,
150299
151051
  elsewhere: owner !== void 0 && owner !== chatKey,
150300
- origin
151052
+ origin,
151053
+ ...archived ? { archived: true } : {}
150301
151054
  };
150302
151055
  };
150303
- const projectShells = async (projectId) => {
151056
+ const projectShells = async (projectId, includeArchived) => {
150304
151057
  if (projectId === "") return [];
150305
151058
  const shells = [];
150306
151059
  let cursor = null;
@@ -150308,7 +151061,7 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150308
151061
  const result2 = await host.threads.list({
150309
151062
  projectId,
150310
151063
  limit: WEB_LIST_PAGE,
150311
- archived: "exclude",
151064
+ archived: includeArchived ? "include" : "exclude",
150312
151065
  cursor
150313
151066
  });
150314
151067
  for (const shell of result2.items) {
@@ -150328,20 +151081,31 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150328
151081
  const isPluginThread = (threadId, projectId) => originOf(threadId, projectId) === "plugin";
150329
151082
  const sessionRowsFor = async (binding, projectId = binding.projectId, pluginOnly = false) => {
150330
151083
  const chatKey = chatKeyOf(binding);
150331
- const shells = await projectShells(projectId);
151084
+ const includeArchived = chatOf(core.state, chatKey).includeArchived === true;
151085
+ const shells = await projectShells(projectId, includeArchived);
150332
151086
  return shells.flatMap((shell) => {
150333
151087
  if (pluginOnly && !isPluginThread(shell.threadId, projectId)) return [];
150334
- 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
+ );
150335
151094
  return row === null ? [] : [row];
150336
151095
  });
150337
151096
  };
150338
151097
  const rowForThread = async (binding, chatKey, threadId) => {
150339
- const thread = await threadOrNull7(host.threads, threadId, { archived: "include" });
151098
+ const thread = await threadOrNull8(host.threads, threadId, { archived: "include" });
150340
151099
  if (thread === null) return null;
150341
151100
  if (thread.projectId !== binding.projectId && !isPluginThread(threadId, thread.projectId)) {
150342
151101
  return null;
150343
151102
  }
150344
- 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
+ );
150345
151109
  return row === null ? null : { ...row, projectId: thread.projectId };
150346
151110
  };
150347
151111
  const resumeRowsFor = async (binding, wanted, listed) => {
@@ -150491,6 +151255,11 @@ var makeSessions = (core, projects = makeProjectRows(core)) => {
150491
151255
  await deliver(binding, t("resume.gone"));
150492
151256
  return;
150493
151257
  }
151258
+ if (target2.archived === true) {
151259
+ if (confirming) pendingAttachByChat.delete(chatKey);
151260
+ await deliver(binding, t("resume.archived"));
151261
+ return;
151262
+ }
150494
151263
  if (currentBusy) {
150495
151264
  await deliver(binding, t("resume.busy"));
150496
151265
  return;
@@ -151647,12 +152416,12 @@ var makeTelegramAdapter = (deps) => {
151647
152416
  },
151648
152417
  settleApproval: async (binding, cardId, settlement) => {
151649
152418
  const api = await apiFor(binding);
151650
- const line = settlement.kind === "chosen" ? t("card.approval.chosen", { label: settlement.label }) : t("card.approval.elsewhere");
152419
+ const line = approvalSettlementText(settlement, t);
151651
152420
  await withFloodWait(
151652
152421
  () => api.editMessage(
151653
152422
  binding.chatId,
151654
152423
  cardId,
151655
- `${t("card.approval.doneTitle")}
152424
+ `${approvalSettlementTitle(settlement, t)}
151656
152425
  ${line}`
151657
152426
  )
151658
152427
  );
@@ -151741,7 +152510,7 @@ ${line}`
151741
152510
  };
151742
152511
 
151743
152512
  // src/turns.ts
151744
- 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";
151745
152514
 
151746
152515
  // src/botPreset.ts
151747
152516
  var resolveDefaultPreset = async (chatPreset, bot, host) => {
@@ -151953,8 +152722,8 @@ var makeTurns = (core, deps) => {
151953
152722
  };
151954
152723
  const threadStillUsable = async (binding, threadId) => {
151955
152724
  try {
151956
- const thread = await threadOrNull8(host.threads, threadId, { archived: "include" });
151957
- return thread !== null && thread.archivedAt == null && thread.projectId === binding.projectId;
152725
+ const thread = await threadOrNull9(host.threads, threadId, { archived: "include" });
152726
+ return threadUsable(binding, thread);
151958
152727
  } catch {
151959
152728
  return true;
151960
152729
  }
@@ -151964,6 +152733,13 @@ var makeTurns = (core, deps) => {
151964
152733
  return waiting !== void 0 && waiting.threadId === threadId ? waiting : void 0;
151965
152734
  };
151966
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
+ };
151967
152743
  const ensureThread = async (binding, message) => {
151968
152744
  const chatKey = chatKeyOf(binding);
151969
152745
  let threadId = chatOf(core.state, chatKey).threadId ?? void 0;
@@ -152020,7 +152796,7 @@ var makeTurns = (core, deps) => {
152020
152796
  title: titleFrom(message.text || message.resources[0]?.name || "Chat"),
152021
152797
  modelSelection,
152022
152798
  ...presetId === void 0 ? {} : { presetId },
152023
- runtimeMode: binding.runtimeMode,
152799
+ runtimeMode: runtimeModeOf(binding),
152024
152800
  interactionMode: interactionModeOf(chatKey),
152025
152801
  branch: null,
152026
152802
  worktreePath: null
@@ -152155,7 +152931,7 @@ var makeTurns = (core, deps) => {
152155
152931
  )
152156
152932
  },
152157
152933
  titleSeed: titleFrom(text3 || attachments[0]?.name || "Chat"),
152158
- runtimeMode: binding.runtimeMode,
152934
+ runtimeMode: runtimeModeOf(binding),
152159
152935
  interactionMode: interactionModeOf(chatKey),
152160
152936
  ...intentAction !== void 0 ? { intentAction } : {}
152161
152937
  },
@@ -152219,7 +152995,6 @@ var makeTurns = (core, deps) => {
152219
152995
  const hasWaitingAttachments = (chatKey) => (waitingFor(chatKey, chatOf(core.state, chatKey).threadId)?.attachments.length ?? 0) > 0;
152220
152996
  return {
152221
152997
  stop: () => dispatchAbort.abort(),
152222
- threadStillUsable,
152223
152998
  ensureThread,
152224
152999
  startTurn,
152225
153000
  receiveAttachmentsOnly,
@@ -152702,7 +153477,8 @@ var buildSettingsView = (config2, adapters) => {
152702
153477
  const secrets = {};
152703
153478
  for (const column of columns) {
152704
153479
  if (column.kind === "password") secrets[column.key] = hasStoredSecret(raw, column.key);
152705
- 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 || "";
152706
153482
  }
152707
153483
  const adapter = adapters.find((candidate) => candidate.platform === platform);
152708
153484
  if (adapter === void 0) {
@@ -152718,13 +153494,14 @@ var buildSettingsView = (config2, adapters) => {
152718
153494
  continue;
152719
153495
  }
152720
153496
  const bot = readBotView(rowId, raw, adapter);
153497
+ const issues = validateBotSettings(bot, adapter);
152721
153498
  bots.push({
152722
153499
  rowId,
152723
153500
  platform,
152724
153501
  configured: adapter.isConfigured(bot),
152725
153502
  values,
152726
153503
  secrets,
152727
- issues: adapter.validate?.(bot) ?? [],
153504
+ issues,
152728
153505
  publicFields: adapter.publicFields?.(bot) ?? []
152729
153506
  });
152730
153507
  }
@@ -154226,24 +155003,32 @@ var normalizeApiBaseUrl = (value2) => {
154226
155003
  return raw.replace(/\/+$/u, "");
154227
155004
  };
154228
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
+ };
154229
155017
  var splitUtf8ByBytes = (text3, maxBytes = WECOM_APP_TEXT_MAX_BYTES) => {
154230
155018
  const value2 = typeof text3 === "string" ? text3 : "";
154231
155019
  if (value2 === "") return [""];
154232
155020
  const chunks = [];
154233
- let current = "";
154234
- let currentBytes = 0;
154235
- for (const char of value2) {
154236
- const size = byteLength(char);
154237
- if (currentBytes + size > maxBytes && current.length > 0) {
154238
- chunks.push(current);
154239
- current = char;
154240
- currentBytes = size;
154241
- 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;
154242
155027
  }
154243
- current += char;
154244
- currentBytes += size;
155028
+ const cut = boundaryCut(window2) ?? window2.length;
155029
+ chunks.push(rest.slice(0, cut));
155030
+ rest = rest.slice(cut);
154245
155031
  }
154246
- if (current.length > 0) chunks.push(current);
154247
155032
  return chunks;
154248
155033
  };
154249
155034
  var parseInvalidUsers = (value2) => typeof value2 === "string" ? value2.split("|").map((user) => user.trim()).filter(Boolean) : [];
@@ -155708,7 +156493,7 @@ var makeWecomAppAdapter = (deps) => {
155708
156493
  // src/index.ts
155709
156494
  var definition = {
155710
156495
  id: "im",
155711
- apiVersion: 29,
156496
+ apiVersion: 31,
155712
156497
  // 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
155713
156498
  activate: async (ctx, testing = {}) => {
155714
156499
  const { dispatchSleep } = testing;
@@ -155853,7 +156638,7 @@ var definition = {
155853
156638
  });
155854
156639
  const projectRows = makeProjectRows(core);
155855
156640
  const sessions = makeSessions(core, projectRows);
155856
- const modelPlan = makeModelPlan(core, turns);
156641
+ const modelPlan = makeModelPlan(core);
155857
156642
  const chatQueue = makeChatQueue(core);
155858
156643
  const panelWiring = makePanelWiring(core, {
155859
156644
  enqueueForChat: chatQueue.enqueueForChat,
@@ -155940,6 +156725,7 @@ var definition = {
155940
156725
  setLanguage(parseReplyLanguage(config2[REPLY_LANGUAGE_FIELD]));
155941
156726
  inbound.connectionTests.retainRows(Object.keys(config2[BOTS_FIELD] ?? {}));
155942
156727
  connections.reconcile();
156728
+ await approvals.reconcileTimeouts();
155943
156729
  await deliveryHttp.apply(config2);
155944
156730
  });
155945
156731
  }