@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.
- package/dist/main.mjs +86 -60
- 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(
|
|
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
|
|
7372
|
-
|
|
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
|
|
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) {
|