@vibe-lark/larkpal 0.1.21 → 0.1.23

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/main.mjs +64 -3
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -859,8 +859,18 @@ const log$25 = larkLogger("cc-runtime/process-manager");
859
859
  * 此值不可更改,否则所有用户的历史会话将无法恢复。
860
860
  */
861
861
  const LARKPAL_SESSION_NAMESPACE = "7c9e6679-7425-40de-944b-e07fc1f90ae7";
862
- /** 空闲超时时间(10 分钟) */
863
- const IDLE_TIMEOUT_MS = 600 * 1e3;
862
+ /**
863
+ * CC 进程空闲超时时间(毫秒)。
864
+ * 通过 LARKPAL_CC_IDLE_TIMEOUT 环境变量配置(单位:秒),默认 0 表示永不超时。
865
+ * 多用户场景建议设为 0(进程常驻),避免 --resume 冷启动延迟。
866
+ */
867
+ const IDLE_TIMEOUT_MS = (() => {
868
+ const envVal = process.env.LARKPAL_CC_IDLE_TIMEOUT;
869
+ if (envVal === void 0 || envVal === "") return 0;
870
+ const seconds = parseInt(envVal, 10);
871
+ if (isNaN(seconds) || seconds < 0) return 0;
872
+ return seconds * 1e3;
873
+ })();
864
874
  /** 优雅关闭等待时间(5 秒) */
865
875
  const GRACEFUL_SHUTDOWN_MS = 5e3;
866
876
  var SessionProcessManager = class {
@@ -947,10 +957,15 @@ var SessionProcessManager = class {
947
957
  */
948
958
  async executePrompt(config, callbacks) {
949
959
  const { sessionId } = config;
960
+ const t0 = Date.now();
950
961
  const prevLock = this.sessionLocks.get(sessionId);
951
962
  if (prevLock) {
952
963
  log$25.info("等待上一条消息处理完成", { sessionId });
953
964
  await prevLock;
965
+ log$25.info("[perf] 串行锁等待完成", {
966
+ sessionId,
967
+ waitMs: Date.now() - t0
968
+ });
954
969
  }
955
970
  const completionPromise = this.createMessageCompletionPromise(sessionId);
956
971
  this.sessionLocks.set(sessionId, completionPromise);
@@ -960,11 +975,16 @@ var SessionProcessManager = class {
960
975
  log$25.info("向常驻进程发送消息", {
961
976
  sessionId,
962
977
  promptLength: config.prompt.length,
963
- pid: existing.childProcess.pid
978
+ pid: existing.childProcess.pid,
979
+ prepareMs: Date.now() - t0
964
980
  });
965
981
  this.sendMessage(sessionId, config.prompt);
966
982
  this.resetIdleTimer(sessionId);
967
983
  await completionPromise;
984
+ log$25.info("[perf] executePrompt 完成(热进程)", {
985
+ sessionId,
986
+ totalMs: Date.now() - t0
987
+ });
968
988
  return;
969
989
  }
970
990
  if (existing && (existing.status === "stopped" || existing.status === "crashed")) {
@@ -980,8 +1000,17 @@ var SessionProcessManager = class {
980
1000
  await this.stopProcess(sessionId);
981
1001
  this.sessionLocks.set(sessionId, completionPromise);
982
1002
  }
1003
+ const tStart = Date.now();
983
1004
  await this.startPersistentProcess(config);
1005
+ log$25.info("[perf] 冷启动进程完成", {
1006
+ sessionId,
1007
+ coldStartMs: Date.now() - tStart
1008
+ });
984
1009
  await completionPromise;
1010
+ log$25.info("[perf] executePrompt 完成(冷启动)", {
1011
+ sessionId,
1012
+ totalMs: Date.now() - t0
1013
+ });
985
1014
  }
986
1015
  /**
987
1016
  * 启动常驻 CC 进程
@@ -1197,10 +1226,31 @@ var SessionProcessManager = class {
1197
1226
  */
1198
1227
  bindParserToCallbackProxy(parser, sessionId) {
1199
1228
  const getCallbacks = () => this.activeCallbacks.get(sessionId);
1229
+ let firstTokenLogged = false;
1230
+ let messageSentAt = Date.now();
1231
+ const proc = this.processes.get(sessionId);
1232
+ if (proc) proc._resetPerfTimer = () => {
1233
+ firstTokenLogged = false;
1234
+ messageSentAt = Date.now();
1235
+ };
1200
1236
  parser.on("textDelta", (text) => {
1237
+ if (!firstTokenLogged) {
1238
+ firstTokenLogged = true;
1239
+ log$25.info("[perf] 首 token (text)", {
1240
+ sessionId,
1241
+ ttftMs: Date.now() - messageSentAt
1242
+ });
1243
+ }
1201
1244
  getCallbacks()?.onTextDelta?.(text);
1202
1245
  });
1203
1246
  parser.on("thinkingDelta", (text) => {
1247
+ if (!firstTokenLogged) {
1248
+ firstTokenLogged = true;
1249
+ log$25.info("[perf] 首 token (thinking)", {
1250
+ sessionId,
1251
+ ttftMs: Date.now() - messageSentAt
1252
+ });
1253
+ }
1204
1254
  getCallbacks()?.onThinkingDelta?.(text);
1205
1255
  });
1206
1256
  parser.on("toolUseStart", (toolName, toolInput) => {
@@ -1213,9 +1263,18 @@ var SessionProcessManager = class {
1213
1263
  getCallbacks()?.onToolProgress?.(toolName, elapsedSeconds);
1214
1264
  });
1215
1265
  parser.on("turnEnd", (stopReason) => {
1266
+ log$25.info("[perf] turnEnd", {
1267
+ sessionId,
1268
+ stopReason,
1269
+ sinceMessageMs: Date.now() - messageSentAt
1270
+ });
1216
1271
  getCallbacks()?.onTurnEnd?.(stopReason);
1217
1272
  });
1218
1273
  parser.on("result", (result) => {
1274
+ log$25.info("[perf] result 事件", {
1275
+ sessionId,
1276
+ sinceMessageMs: Date.now() - messageSentAt
1277
+ });
1219
1278
  getCallbacks()?.onResult?.(result);
1220
1279
  this.resolveMessageCompletion(sessionId);
1221
1280
  });
@@ -1239,6 +1298,7 @@ var SessionProcessManager = class {
1239
1298
  log$25.error("无法发送消息:进程不存在或 stdin 不可用", { sessionId });
1240
1299
  return;
1241
1300
  }
1301
+ proc._resetPerfTimer?.();
1242
1302
  const userMessageId = v4();
1243
1303
  this.lastUserMessageIds.set(sessionId, userMessageId);
1244
1304
  const payload = JSON.stringify({
@@ -1442,6 +1502,7 @@ var SessionProcessManager = class {
1442
1502
  resetIdleTimer(sessionId) {
1443
1503
  const existing = this.idleTimers.get(sessionId);
1444
1504
  if (existing) clearTimeout(existing);
1505
+ if (IDLE_TIMEOUT_MS <= 0) return;
1445
1506
  const timer = setTimeout(() => {
1446
1507
  log$25.info("CC 常驻进程空闲超时,自动停止", {
1447
1508
  sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-lark/larkpal",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "LarkPal - Lark/Feishu bot service",
5
5
  "type": "module",
6
6
  "main": "./dist/main.mjs",