@modelzen/feishu-codex-bridge 0.3.10 → 0.3.11

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.
Files changed (2) hide show
  1. package/dist/cli.js +75 -23
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2871,7 +2871,9 @@ function gaugeEl(state) {
2871
2871
  return state.usage ? runCardGauge(state.usage.used, state.usage.window) : null;
2872
2872
  }
2873
2873
  var RC = {
2874
- stop: "run.stop"
2874
+ stop: "run.stop",
2875
+ /** goal-only: clear the goal but let the in-flight turn finish (no auto-continue). */
2876
+ endGoal: "goal.end"
2875
2877
  };
2876
2878
  var ANSWER_EID = "answer";
2877
2879
  var REASONING_MAX = 1500;
@@ -2901,7 +2903,21 @@ function renderRunning(state, rc) {
2901
2903
  const answer = textParts.join("\n\n");
2902
2904
  if (answer) elements.push(mdStream(answer, ANSWER_EID));
2903
2905
  if (state.footer) elements.push(footerStatus(state.footer));
2904
- if (rc.cardKey && !rc.hideStop) elements.push(actions([button("\u23F9 \u7EC8\u6B62", { a: RC.stop, m: rc.cardKey }, "danger")]));
2906
+ if (rc.cardKey && rc.goalControls) {
2907
+ if (rc.goalEnding) {
2908
+ elements.push(noteMd("_\u{1F3AF} \u76EE\u6807\u5DF2\u89E3\u9664\uFF0C\u672C\u8F6E\u8F93\u51FA\u5B8C\u6210\u540E\u505C\u6B62_"));
2909
+ elements.push(actions([button("\u23F9 \u7EC8\u6B62", { a: RC.stop, m: rc.cardKey }, "danger")]));
2910
+ } else {
2911
+ elements.push(
2912
+ actions([
2913
+ button("\u23F9 \u7EC8\u6B62", { a: RC.stop, m: rc.cardKey }, "danger"),
2914
+ button("\u{1F3AF} \u7ED3\u675F\u76EE\u6807", { a: RC.endGoal, m: rc.cardKey }, "default")
2915
+ ])
2916
+ );
2917
+ }
2918
+ } else if (rc.cardKey && !rc.hideStop) {
2919
+ elements.push(actions([button("\u23F9 \u7EC8\u6B62", { a: RC.stop, m: rc.cardKey }, "danger")]));
2920
+ }
2905
2921
  const gauge = gaugeEl(state);
2906
2922
  if (gauge) elements.push(gauge);
2907
2923
  return elements;
@@ -6990,7 +7006,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
6990
7006
  }
6991
7007
  const dispatcher = new CardDispatcher(channel, cfg);
6992
7008
  const PENDING_TTL_MS = 30 * 6e4;
6993
- const GOAL_MAX_MS = 30 * 6e4;
7009
+ const GOAL_IDLE_MS = 30 * 6e4;
6994
7010
  const CARD_SETTLE_MS = 500;
6995
7011
  const settleUpdate = (msgId, c, fallbackChatId) => {
6996
7012
  const armedAt = Date.now();
@@ -7072,6 +7088,12 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
7072
7088
  if (!st || !runOwnerOrAdmin(evt, st.requesterOpenId)) return;
7073
7089
  st.interrupt?.();
7074
7090
  log.info("card", "action", { actionId: "run.stop", stopped: Boolean(st.interrupt) });
7091
+ }).on(RC.endGoal, ({ evt, value }) => {
7092
+ const key = typeof value.m === "string" ? value.m : evt.messageId;
7093
+ const st = runsByCard.get(key);
7094
+ if (!st || !runOwnerOrAdmin(evt, st.requesterOpenId)) return;
7095
+ st.endGoal?.();
7096
+ log.info("card", "action", { actionId: "goal.end", ended: Boolean(st.endGoal) });
7075
7097
  });
7076
7098
  const dmAdmin = (openId) => isAdmin(cfg, openId ?? "");
7077
7099
  const patch = (evt, c) => settleUpdate(evt.messageId, c, evt.chatId);
@@ -7853,7 +7875,7 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
7853
7875
  const startTurn = () => {
7854
7876
  const render = new RunRender();
7855
7877
  render.showTools = getShowToolCalls(cfg);
7856
- const rc = { rs: render.snapshot(), requesterOpenId: opts.requesterOpenId, showTools: render.showTools, hideStop: true };
7878
+ const rc = { rs: render.snapshot(), requesterOpenId: opts.requesterOpenId, showTools: render.showTools, goalControls: true };
7857
7879
  return { render, rc, stream: null, cardMsgId: null };
7858
7880
  };
7859
7881
  const ensureCard = async (ctx) => {
@@ -7873,19 +7895,44 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
7873
7895
  let goalTokens = 0;
7874
7896
  let goalSeconds = 0;
7875
7897
  let goalErrorMsg;
7876
- let capped = false;
7877
- let resolveCap;
7878
- const capSignal = new Promise((res) => {
7879
- resolveCap = res;
7898
+ let interrupted = false;
7899
+ let goalEnded = false;
7900
+ let idledOut = false;
7901
+ let resolveStop;
7902
+ let resolveEnd;
7903
+ const stopSignal = new Promise((res) => {
7904
+ resolveStop = res;
7905
+ });
7906
+ const endSignal = new Promise((res) => {
7907
+ resolveEnd = res;
7880
7908
  });
7881
- const capTimer = setTimeout(() => {
7882
- capped = true;
7883
- resolveCap();
7884
- }, GOAL_MAX_MS);
7885
7909
  try {
7886
7910
  const run = opts.thread.runGoal(objective);
7887
7911
  state.run = run;
7888
- const guarded = withIdleTimeout(run.events, 0, () => void 0, capSignal);
7912
+ state.interrupt = () => {
7913
+ if (interrupted) return;
7914
+ interrupted = true;
7915
+ void opts.thread.clearGoal().catch(() => void 0);
7916
+ resolveStop();
7917
+ };
7918
+ state.endGoal = () => {
7919
+ if (goalEnded || interrupted) return;
7920
+ goalEnded = true;
7921
+ void opts.thread.clearGoal().catch(() => void 0);
7922
+ if (cur) {
7923
+ cur.rc.goalEnding = true;
7924
+ if (cur.stream) {
7925
+ cur.rc.rs = cur.render.snapshot();
7926
+ cur.stream.streamCoalesced(channel, buildRunCard(cur.rc), ANSWER_EID);
7927
+ }
7928
+ } else {
7929
+ resolveEnd();
7930
+ }
7931
+ };
7932
+ const stop = Promise.race([stopSignal, endSignal]);
7933
+ const guarded = withIdleTimeout(run.events, GOAL_IDLE_MS, () => {
7934
+ idledOut = true;
7935
+ }, stop);
7889
7936
  for await (const ev of guarded) {
7890
7937
  if (ev.type === "goal_update") {
7891
7938
  lastStatus = ev.status;
@@ -7918,6 +7965,7 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
7918
7965
  await finalizeCard(cur);
7919
7966
  cur = null;
7920
7967
  }
7968
+ if (goalEnded) break;
7921
7969
  continue;
7922
7970
  }
7923
7971
  if (ev.type === "error") {
@@ -7937,24 +7985,28 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
7937
7985
  cur.rc.rs = cur.render.snapshot();
7938
7986
  cur.stream.streamCoalesced(channel, buildRunCard(cur.rc), ANSWER_EID);
7939
7987
  }
7988
+ if (interrupted && cur) cur.render.interrupt();
7940
7989
  await finalizeCard(cur);
7941
7990
  cur = null;
7942
7991
  await opts.thread.clearGoal().catch(() => void 0);
7943
- const status = capped ? "timeout" : goalErrorMsg && !isGoalTerminal(lastStatus) ? "error" : lastStatus;
7944
- await sendManagedCard(
7945
- channel,
7946
- opts.chatId,
7947
- buildGoalDoneCard({ objective, status, tokensUsed: goalTokens, timeUsedSeconds: goalSeconds, errorMessage: goalErrorMsg }),
7948
- replyTo,
7949
- !opts.flat
7950
- ).catch((err) => log.fail("card", err, { phase: "goal-done" }));
7992
+ if (!interrupted && !goalEnded) {
7993
+ const status = idledOut ? "timeout" : goalErrorMsg && !isGoalTerminal(lastStatus) ? "error" : lastStatus;
7994
+ await sendManagedCard(
7995
+ channel,
7996
+ opts.chatId,
7997
+ buildGoalDoneCard({ objective, status, tokensUsed: goalTokens, timeUsedSeconds: goalSeconds, errorMessage: goalErrorMsg }),
7998
+ replyTo,
7999
+ !opts.flat
8000
+ ).catch((err) => log.fail("card", err, { phase: "goal-done" }));
8001
+ log.info("card", "goal-final", { status, tokens: goalTokens, seconds: goalSeconds });
8002
+ } else {
8003
+ log.info("card", "goal-final", { status: interrupted ? "interrupted" : "ended", tokens: goalTokens, seconds: goalSeconds });
8004
+ }
7951
8005
  if (topicThreadId) await patchSession(topicThreadId, { updatedAt: Date.now() }).catch(() => void 0);
7952
- log.info("card", "goal-final", { status, tokens: goalTokens, seconds: goalSeconds });
7953
8006
  } catch (err) {
7954
8007
  log.fail("intake", err);
7955
8008
  await channel.send(opts.chatId, { markdown: `\u274C ${err instanceof Error ? err.message : String(err)}` }, { replyTo: opts.replyTo, replyInThread: !opts.flat }).catch(() => void 0);
7956
8009
  } finally {
7957
- clearTimeout(capTimer);
7958
8010
  active.delete(activeKey);
7959
8011
  if (cur?.cardMsgId) runsByCard.delete(cur.cardMsgId);
7960
8012
  void opts.thread.close().catch(() => void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelzen/feishu-codex-bridge",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "description": "Bridge Feishu/Lark messenger with local Codex via app-server (project=group, thread=session)",
5
5
  "type": "module",
6
6
  "bin": {