@vibe-lark/larkpal 0.1.69 → 0.1.71

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 +86 -60
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -3011,7 +3011,7 @@ function wrapLarkpalAgentAdapter(adapter) {
3011
3011
  return adapter.name;
3012
3012
  },
3013
3013
  executePrompt(config, callbacks) {
3014
- return adapter.executePrompt(normalizeLarkpalAgentConfig(config), callbacks);
3014
+ return adapter.executePrompt(config, callbacks);
3015
3015
  },
3016
3016
  stopProcess(sessionId) {
3017
3017
  return adapter.stopProcess(sessionId);
@@ -3030,59 +3030,6 @@ function wrapLarkpalAgentAdapter(adapter) {
3030
3030
  }
3031
3031
  };
3032
3032
  }
3033
- function normalizeLarkpalAgentConfig(config) {
3034
- if (!Array.isArray(config.prompt)) return config;
3035
- const textParts = [];
3036
- const imageArtifacts = [];
3037
- const imageLines = [];
3038
- config.prompt.forEach((block, index) => {
3039
- if (block.type === "text") {
3040
- if (block.text.trim()) textParts.push(block.text);
3041
- return;
3042
- }
3043
- if (block.type !== "image") return;
3044
- const artifact = toAgentImageArtifact(block, index);
3045
- const label = artifact?.assetId ?? block.fileKey ?? `image_${index + 1}`;
3046
- if (artifact) imageArtifacts.push(artifact);
3047
- imageLines.push(artifact ? `- ${label}: ${artifact.path} (${artifact.mimeType})` : `- ${label}: inline image omitted from text prompt (${block.source.media_type})`);
3048
- });
3049
- if (imageLines.length === 0) return config;
3050
- const prompt = [
3051
- textParts.join("\n\n").trim(),
3052
- "[Images]",
3053
- ...imageLines,
3054
- "The user is asking about these image attachments. Use visual or file tools to inspect the image pixels when needed; do not infer image content from the file name alone."
3055
- ].filter(Boolean).join("\n");
3056
- return {
3057
- ...config,
3058
- prompt,
3059
- sourceArtifacts: mergeSourceArtifacts(config.sourceArtifacts, imageArtifacts),
3060
- metadata: {
3061
- ...config.metadata,
3062
- inboundImageCount: imageLines.length,
3063
- visualAllowedDirs: [config.cwd]
3064
- }
3065
- };
3066
- }
3067
- function toAgentImageArtifact(block, index) {
3068
- if (!block.filePath) return null;
3069
- return {
3070
- type: "image_asset",
3071
- assetId: block.fileKey ?? `inbound_image_${index + 1}`,
3072
- path: block.filePath,
3073
- mimeType: block.source.media_type.split(";", 1)[0] || "image/png",
3074
- metadata: {
3075
- source: "feishu-message",
3076
- fileKey: block.fileKey
3077
- }
3078
- };
3079
- }
3080
- function mergeSourceArtifacts(existing, imageArtifacts) {
3081
- if (imageArtifacts.length === 0) return existing;
3082
- if (existing === void 0) return imageArtifacts;
3083
- if (Array.isArray(existing)) return [...existing, ...imageArtifacts];
3084
- return [existing, ...imageArtifacts];
3085
- }
3086
3033
  function getArtifactId(artifact) {
3087
3034
  const artifactId = typeof artifact.artifactId === "string" ? artifact.artifactId.trim() : "";
3088
3035
  if (artifactId.startsWith("art_")) return artifactId;
@@ -7268,6 +7215,7 @@ function createStatusRouter(config) {
7268
7215
  const router = express.Router();
7269
7216
  const sessionsDir = getWorkspaceChatsRoot(config?.workspaceRoot ?? resolveWorkspaceRoot("/workspace"));
7270
7217
  const processManager = config?.processManager;
7218
+ const messageStore = config?.messageStore;
7271
7219
  router.get("/sessions", async (req, res) => {
7272
7220
  logger$2.info("list sessions request", {
7273
7221
  method: req.method,
@@ -7368,12 +7316,14 @@ function createStatusRouter(config) {
7368
7316
  try {
7369
7317
  const uptimeSeconds = process.uptime();
7370
7318
  const memory = process.memoryUsage();
7371
- const processes = processManager?.getAllProcessInfo().map((processInfo) => {
7372
- return serializeRuntimeProcess(processInfo, processManager);
7373
- }) ?? [];
7319
+ const { processes, diagnostics } = processManager ? await serializeRuntimeProcesses(processManager, messageStore) : {
7320
+ processes: [],
7321
+ diagnostics: []
7322
+ };
7374
7323
  const statusData = {
7375
7324
  uptime: uptimeSeconds,
7376
7325
  processes,
7326
+ diagnostics,
7377
7327
  memory
7378
7328
  };
7379
7329
  logger$2.info("get status response", {
@@ -7423,6 +7373,48 @@ function createStatusRouter(config) {
7423
7373
  });
7424
7374
  return router;
7425
7375
  }
7376
+ async function serializeRuntimeProcesses(processManager, messageStore) {
7377
+ const processes = [];
7378
+ const diagnostics = [];
7379
+ for (const processInfo of processManager.getAllProcessInfo()) {
7380
+ const busy = processManager.isSessionBusy?.(processInfo.sessionId) ?? processInfo.status === "running";
7381
+ const terminalRunStatus = messageStore ? await getLatestTerminalRunStatus(messageStore, processInfo.sessionId) : null;
7382
+ if (terminalRunStatus && !busy && (processInfo.status === "running" || processInfo.status === "starting")) {
7383
+ try {
7384
+ await processManager.stopProcess(processInfo.sessionId);
7385
+ logger$2.warn("status reconcile cleaned terminal non-busy process", {
7386
+ sessionId: processInfo.sessionId,
7387
+ processStatus: processInfo.status,
7388
+ runStatus: terminalRunStatus.status,
7389
+ finalStatus: terminalRunStatus.finalStatus
7390
+ });
7391
+ } catch (err) {
7392
+ logger$2.error("status reconcile failed to stop terminal non-busy process", {
7393
+ sessionId: processInfo.sessionId,
7394
+ processStatus: processInfo.status,
7395
+ runStatus: terminalRunStatus.status,
7396
+ finalStatus: terminalRunStatus.finalStatus,
7397
+ error: err instanceof Error ? err.message : String(err)
7398
+ });
7399
+ }
7400
+ diagnostics.push({
7401
+ sessionId: processInfo.sessionId,
7402
+ reasonCode: "TERMINAL_RUN_PROCESS_STALE",
7403
+ reason: "Runtime process still appeared running after a persisted terminal chat run-status.",
7404
+ runStatus: terminalRunStatus.status,
7405
+ finalStatus: terminalRunStatus.finalStatus,
7406
+ processStatus: processInfo.status,
7407
+ busy
7408
+ });
7409
+ continue;
7410
+ }
7411
+ processes.push(serializeRuntimeProcess(processInfo, processManager));
7412
+ }
7413
+ return {
7414
+ processes,
7415
+ diagnostics
7416
+ };
7417
+ }
7426
7418
  function serializeRuntimeProcess(processInfo, processManager) {
7427
7419
  return {
7428
7420
  ...processInfo,
@@ -7431,6 +7423,31 @@ function serializeRuntimeProcess(processInfo, processManager) {
7431
7423
  busy: processManager.isSessionBusy?.(processInfo.sessionId) ?? processInfo.status === "running"
7432
7424
  };
7433
7425
  }
7426
+ async function getLatestTerminalRunStatus(messageStore, sessionId) {
7427
+ const history = await messageStore.getMessages(sessionId, { limit: 1e3 });
7428
+ for (const message of [...history.messages].reverse()) {
7429
+ if (message.metadata?.runtimeEventType !== "run-status") continue;
7430
+ const parsed = parseRunStatusContent(message.content);
7431
+ const status = parsed?.status ?? (typeof message.metadata.status === "string" ? message.metadata.status : void 0);
7432
+ const finalStatus = parsed?.finalStatus ?? message.metadata.finalStatus;
7433
+ if (status && status !== "running") return {
7434
+ status,
7435
+ finalStatus: typeof finalStatus === "string" ? finalStatus : void 0
7436
+ };
7437
+ }
7438
+ return null;
7439
+ }
7440
+ function parseRunStatusContent(content) {
7441
+ try {
7442
+ const parsed = JSON.parse(content);
7443
+ return {
7444
+ status: typeof parsed.run?.status === "string" ? parsed.run.status : void 0,
7445
+ finalStatus: typeof parsed.run?.finalStatus === "string" ? parsed.run.finalStatus : void 0
7446
+ };
7447
+ } catch {
7448
+ return null;
7449
+ }
7450
+ }
7434
7451
  //#endregion
7435
7452
  //#region src/gateway/server.ts
7436
7453
  const logger$1 = larkLogger("gateway");
@@ -7496,7 +7513,8 @@ function registerRoutes(app, processManager, scheduledTaskManager, appCredential
7496
7513
  }
7497
7514
  app.use("/api", createStatusRouter({
7498
7515
  workspaceRoot,
7499
- processManager
7516
+ processManager,
7517
+ messageStore
7500
7518
  }));
7501
7519
  app.use("/hooks", createHooksRouter());
7502
7520
  app.use("/api/mcp", createMcpCallbackRouter());
@@ -9121,6 +9139,7 @@ function getUserFacingErrorMessage(error) {
9121
9139
  const log$21 = larkLogger("card/cc-stream-bridge");
9122
9140
  const CC_INTERNAL_PLACEHOLDER = "No response requested.";
9123
9141
  const CLAUDE_INVALID_PARAMETER_PATTERN = /API Error:\s*400\b.*parameter specified in the request is not valid/i;
9142
+ const TOOL_PROGRESS_CARD_REFRESH_INTERVAL_MS = 15e3;
9124
9143
  function buildClaudeCodeUserFacingError(errorMessage, hasImages) {
9125
9144
  if (hasImages && CLAUDE_INVALID_PARAMETER_PATTERN.test(errorMessage)) return createUserFacingError([
9126
9145
  "当前运行时无法处理这条图片消息。",
@@ -9144,6 +9163,8 @@ var CCStreamBridge = class {
9144
9163
  activeTools = /* @__PURE__ */ new Map();
9145
9164
  /** 最近一次 toolUseStart 的工具名(用于 toolResult 时查找) */
9146
9165
  lastToolName = null;
9166
+ /** 最近一次由 tool_progress 推动卡片刷新的时间 */
9167
+ lastToolProgressCardRefreshAt = 0;
9147
9168
  controller;
9148
9169
  options;
9149
9170
  constructor(controller, options) {
@@ -9232,13 +9253,18 @@ var CCStreamBridge = class {
9232
9253
  });
9233
9254
  this.controller.onToolPayload({ text: `✅ ${displayName} 完成` });
9234
9255
  }
9235
- /** 工具执行进度 → 记录日志 */
9256
+ /** 工具执行进度 → 节流刷新卡片,避免长工具执行期间卡片长时间无可见更新 */
9236
9257
  onToolProgress(toolName, elapsedSeconds) {
9258
+ const displayName = getToolDisplayName(toolName);
9237
9259
  log$21.debug("toolProgress 事件", {
9238
9260
  toolName,
9239
- displayName: getToolDisplayName(toolName),
9261
+ displayName,
9240
9262
  elapsedSeconds
9241
9263
  });
9264
+ const now = Date.now();
9265
+ if (now - this.lastToolProgressCardRefreshAt < TOOL_PROGRESS_CARD_REFRESH_INTERVAL_MS) return;
9266
+ this.lastToolProgressCardRefreshAt = now;
9267
+ this.controller.onToolPayload({ text: `${displayName} running for ${Math.max(0, Math.floor(elapsedSeconds))}s` });
9242
9268
  }
9243
9269
  /** 轮次结束 → 仅在 end_turn 时标记完成 + 触发 onIdle */
9244
9270
  onTurnEnd(stopReason) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-lark/larkpal",
3
- "version": "0.1.69",
3
+ "version": "0.1.71",
4
4
  "description": "LarkPal - Lark/Feishu bot service",
5
5
  "type": "module",
6
6
  "main": "./dist/main.mjs",