@hwj123weijian/pi-feishu 0.11.1 → 0.11.2

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/package.json +1 -1
  2. package/src/extension.ts +39 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hwj123weijian/pi-feishu",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "description": "Feishu private-chat and managed-group bridge for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/extension.ts CHANGED
@@ -100,8 +100,13 @@ function getSharedState(): SharedFeishuState {
100
100
  steer: (text) => {
101
101
  // 仅当确有飞书轮次在跑时并入:发到当前活跃会话(轮次就在那里),绝不触发会话切换。
102
102
  if (!state.activeBridge || !state.sendCurrentMessage) return false;
103
- state.sendCurrentMessage(text, { deliverAs: "steer" });
104
- return true;
103
+ try {
104
+ state.sendCurrentMessage(text, { deliverAs: "steer" });
105
+ return true;
106
+ } catch {
107
+ // pi 已失效等场景:并入失败,回落到正常排队。
108
+ return false;
109
+ }
105
110
  },
106
111
  };
107
112
  const proxyRuntime: PiRuntime = {
@@ -122,6 +127,8 @@ function getSharedState(): SharedFeishuState {
122
127
  },
123
128
  });
124
129
  return !result.cancelled;
130
+ } catch (error) {
131
+ rethrowStaleAsHint(state, error);
125
132
  } finally {
126
133
  state.switchingSession = false;
127
134
  }
@@ -165,6 +172,24 @@ function tryGetSessionFile(context: ExtensionContext): string | undefined {
165
172
  }
166
173
  }
167
174
 
175
+ /** 缓存的命令上下文已失效(Pi 侧发生过会话替换)时的可操作提示。 */
176
+ export const SESSION_CONTROL_HINT =
177
+ "本地 Pi 会话控制已过期(本地发生过会话切换)。请在本地 Pi 执行一次 /feishu status 刷新,然后重发这条消息。";
178
+
179
+ function isStaleCtxError(error: unknown): boolean {
180
+ const message = error instanceof Error ? error.message : String(error);
181
+ return message.includes("extension ctx is stale") || message.includes("ctx is stale after session replacement");
182
+ }
183
+
184
+ /** 把 Pi 的英文 stale 内部错误转译成可操作指引,其余错误原样上抛。 */
185
+ function rethrowStaleAsHint(state: SharedFeishuState, error: unknown): never {
186
+ if (isStaleCtxError(error)) {
187
+ state.latestCommandContext = undefined;
188
+ throw new Error(SESSION_CONTROL_HINT);
189
+ }
190
+ throw error instanceof Error ? error : new Error(String(error));
191
+ }
192
+
168
193
  /**
169
194
  * p2p 私聊消息永远落在主会话:群消息处理会把 Pi 切进群的会话,
170
195
  * 这里负责在下一条私聊到来时切回去,避免把 Owner 的提问发进群会话。
@@ -182,7 +207,7 @@ async function sendToMainSession(state: SharedFeishuState, prompt: string): Prom
182
207
  const context = state.latestCommandContext;
183
208
  if (!context) {
184
209
  // 主会话与当前会话不一致却没有命令上下文:直发会串进群会话,给出可操作的错误更安全。
185
- throw new Error("Pi 会话控制尚未就绪,请先在本地执行一次 /feishu status。");
210
+ throw new Error(SESSION_CONTROL_HINT);
186
211
  }
187
212
  state.switchingSession = true;
188
213
  try {
@@ -193,19 +218,26 @@ async function sendToMainSession(state: SharedFeishuState, prompt: string): Prom
193
218
  },
194
219
  });
195
220
  if (result.cancelled) throw new Error("切回主 Pi 会话已取消。");
221
+ } catch (error) {
222
+ rethrowStaleAsHint(state, error);
196
223
  } finally {
197
224
  state.switchingSession = false;
198
225
  }
199
226
  }
200
227
 
201
228
  async function sendToChatSession(state: SharedFeishuState, chatId: string, prompt: string): Promise<void> {
229
+ const sessionFile = state.controller.getChatSessionFile(chatId);
230
+ // Pi 侧切换后当前会话可能恰好就是目标群会话:此时无需命令上下文,直接投递。
231
+ if (sessionFile && sessionFile === state.currentSessionFile && state.sendCurrentMessage) {
232
+ state.sendCurrentMessage(prompt);
233
+ return;
234
+ }
202
235
  const context = state.latestCommandContext;
203
- if (!context) throw new Error("Pi 会话控制尚未就绪,请先在本地执行一次 /feishu status。");
236
+ if (!context) throw new Error(SESSION_CONTROL_HINT);
204
237
 
205
238
  const directory = state.controller.getChatDirectory(chatId);
206
239
  state.switchingSession = true;
207
240
  try {
208
- const sessionFile = state.controller.getChatSessionFile(chatId);
209
241
  if (sessionFile) {
210
242
  // Pi 的 TUI switchSession 不透传 cwdOverride,改为把 cwd 写进会话头:
211
243
  // SessionManager.open 切换时从 header 读取 cwd,runtime 随之在新目录重建。
@@ -247,6 +279,8 @@ async function sendToChatSession(state: SharedFeishuState, chatId: string, promp
247
279
  },
248
280
  });
249
281
  if (result.cancelled) throw new Error("创建飞书群专属 Pi 会话已取消。");
282
+ } catch (error) {
283
+ rethrowStaleAsHint(state, error);
250
284
  } finally {
251
285
  state.switchingSession = false;
252
286
  }