dsh-lark-bot 0.9.2 → 0.10.0
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/README.md +21 -7
- package/SECURITY.md +3 -2
- package/dist/cli.js +431 -92
- package/dist/cli.js.map +1 -1
- package/dist/plugin.d.ts +2 -0
- package/dist/plugin.js +87 -38
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.d.ts
CHANGED
|
@@ -221,6 +221,8 @@ declare class PendingQueue<T> {
|
|
|
221
221
|
unblock(scope: string): void;
|
|
222
222
|
flushNow(scope: string): Promise<void>;
|
|
223
223
|
hasPending(scope: string): boolean;
|
|
224
|
+
/** Number of messages waiting in the queue for a scope (not yet flushing). */
|
|
225
|
+
size(scope: string): number;
|
|
224
226
|
isBlocked(scope: string): boolean;
|
|
225
227
|
isFlushing(scope: string): boolean;
|
|
226
228
|
activeFlushes(scope: string): number;
|
package/dist/plugin.js
CHANGED
|
@@ -1299,9 +1299,10 @@ function packageJsonFor(profile) {
|
|
|
1299
1299
|
)}
|
|
1300
1300
|
`;
|
|
1301
1301
|
}
|
|
1302
|
-
function patchYamlFor() {
|
|
1302
|
+
function patchYamlFor(options) {
|
|
1303
1303
|
const own = ownPackageInfo();
|
|
1304
|
-
|
|
1304
|
+
const bridgeTools = options?.bridgeTools ?? true;
|
|
1305
|
+
const lines = [
|
|
1305
1306
|
"# dsh-lark SDK JSON-RPC runtime overlay (managed by dsh-lark-bot).",
|
|
1306
1307
|
"# stdout is reserved for SDK JSON-RPC frames; no console logger may load.",
|
|
1307
1308
|
"- insert:",
|
|
@@ -1322,26 +1323,31 @@ function patchYamlFor() {
|
|
|
1322
1323
|
"",
|
|
1323
1324
|
"- id: hmr",
|
|
1324
1325
|
" disabled: true",
|
|
1325
|
-
"",
|
|
1326
|
-
// In-process bridge callback tool: lets the agent mention users and push
|
|
1327
|
-
// messages to other chats/topics through the running bridge process.
|
|
1328
|
-
"- insert:",
|
|
1329
|
-
" - id: lark-notify",
|
|
1330
|
-
` name: '${own.name}/notify'`,
|
|
1331
|
-
" config:",
|
|
1332
|
-
" endpoint: !!js process.env.DSH_LARK_NOTIFY_URL",
|
|
1333
|
-
" token: !!js process.env.DSH_LARK_NOTIFY_TOKEN",
|
|
1334
|
-
"",
|
|
1335
|
-
// Question-card tool: the agent asks the user for decisions / missing
|
|
1336
|
-
// information; the bridge shows a card and returns the answer.
|
|
1337
|
-
"- insert:",
|
|
1338
|
-
" - id: lark-ask",
|
|
1339
|
-
` name: '${own.name}/ask'`,
|
|
1340
|
-
" config:",
|
|
1341
|
-
" endpoint: !!js process.env.DSH_LARK_ASK_URL",
|
|
1342
|
-
" token: !!js process.env.DSH_LARK_NOTIFY_TOKEN",
|
|
1343
1326
|
""
|
|
1344
|
-
]
|
|
1327
|
+
];
|
|
1328
|
+
if (bridgeTools) {
|
|
1329
|
+
lines.push(
|
|
1330
|
+
// In-process bridge callback tool: lets the agent mention users and push
|
|
1331
|
+
// messages to other chats/topics through the running bridge process.
|
|
1332
|
+
"- insert:",
|
|
1333
|
+
" - id: lark-notify",
|
|
1334
|
+
` name: '${own.name}/notify'`,
|
|
1335
|
+
" config:",
|
|
1336
|
+
" endpoint: !!js process.env.DSH_LARK_NOTIFY_URL",
|
|
1337
|
+
" token: !!js process.env.DSH_LARK_NOTIFY_TOKEN",
|
|
1338
|
+
"",
|
|
1339
|
+
// Question-card tool: the agent asks the user for decisions / missing
|
|
1340
|
+
// information; the bridge shows a card and returns the answer.
|
|
1341
|
+
"- insert:",
|
|
1342
|
+
" - id: lark-ask",
|
|
1343
|
+
` name: '${own.name}/ask'`,
|
|
1344
|
+
" config:",
|
|
1345
|
+
" endpoint: !!js process.env.DSH_LARK_ASK_URL",
|
|
1346
|
+
" token: !!js process.env.DSH_LARK_NOTIFY_TOKEN",
|
|
1347
|
+
""
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
return lines.join("\n");
|
|
1345
1351
|
}
|
|
1346
1352
|
function sdkServerInstalled(profileRoot) {
|
|
1347
1353
|
const candidates = [
|
|
@@ -1399,7 +1405,13 @@ async function ensureSdkProfile(options) {
|
|
|
1399
1405
|
await mkdir(root, { recursive: true });
|
|
1400
1406
|
await writeFile(join3(root, "package.json"), packageJsonFor(profile), "utf8");
|
|
1401
1407
|
await writeFile(join3(root, "cordis.yml"), "[]\n", "utf8");
|
|
1402
|
-
await writeFile(
|
|
1408
|
+
await writeFile(
|
|
1409
|
+
join3(root, "cordis.patch.yml"),
|
|
1410
|
+
patchYamlFor(
|
|
1411
|
+
options.bridgeTools === void 0 ? {} : { bridgeTools: options.bridgeTools }
|
|
1412
|
+
),
|
|
1413
|
+
"utf8"
|
|
1414
|
+
);
|
|
1403
1415
|
const install = options.install ?? runPnpmInstall;
|
|
1404
1416
|
if (!isSdkProfileReady(root)) {
|
|
1405
1417
|
await install(root);
|
|
@@ -1763,6 +1775,10 @@ var PendingQueue = class {
|
|
|
1763
1775
|
hasPending(scope) {
|
|
1764
1776
|
return (this.pending.get(scope)?.length ?? 0) > 0;
|
|
1765
1777
|
}
|
|
1778
|
+
/** Number of messages waiting in the queue for a scope (not yet flushing). */
|
|
1779
|
+
size(scope) {
|
|
1780
|
+
return this.pending.get(scope)?.length ?? 0;
|
|
1781
|
+
}
|
|
1766
1782
|
isBlocked(scope) {
|
|
1767
1783
|
return this.blocked.has(scope);
|
|
1768
1784
|
}
|
|
@@ -2052,7 +2068,9 @@ var initialState = {
|
|
|
2052
2068
|
footer: "thinking",
|
|
2053
2069
|
terminal: "running",
|
|
2054
2070
|
errorMsg: void 0,
|
|
2055
|
-
idleTimeoutMinutes: void 0
|
|
2071
|
+
idleTimeoutMinutes: void 0,
|
|
2072
|
+
startedAtMs: void 0,
|
|
2073
|
+
lastActivityMs: void 0
|
|
2056
2074
|
};
|
|
2057
2075
|
function closeStreamingText(blocks) {
|
|
2058
2076
|
return blocks.map(
|
|
@@ -2198,8 +2216,16 @@ function markdown(content) {
|
|
|
2198
2216
|
function noteMd(content) {
|
|
2199
2217
|
return { tag: "markdown", content, text_size: "notation" };
|
|
2200
2218
|
}
|
|
2201
|
-
function footerStatus(status) {
|
|
2202
|
-
|
|
2219
|
+
function footerStatus(status, state, now) {
|
|
2220
|
+
let text = status === "thinking" ? "\u{1F9E0} \u6B63\u5728\u601D\u8003" : status === "tool_running" ? "\u{1F9F0} \u6B63\u5728\u8C03\u7528\u5DE5\u5177" : "\u270D\uFE0F \u6B63\u5728\u8F93\u51FA";
|
|
2221
|
+
if (state.startedAtMs !== void 0) {
|
|
2222
|
+
const elapsed = Math.max(0, Math.round((now - state.startedAtMs) / 1e3));
|
|
2223
|
+
text += ` \u23F1 ${elapsed}s`;
|
|
2224
|
+
}
|
|
2225
|
+
if (state.lastActivityMs !== void 0) {
|
|
2226
|
+
const idle = Math.max(0, Math.round((now - state.lastActivityMs) / 1e3));
|
|
2227
|
+
if (idle >= 60) text += ` \xB7 \u23F8 \u65E0\u54CD\u5E94 ${idle}s`;
|
|
2228
|
+
}
|
|
2203
2229
|
return noteMd(text);
|
|
2204
2230
|
}
|
|
2205
2231
|
function summaryText(state) {
|
|
@@ -2233,7 +2259,7 @@ function usageLine(state) {
|
|
|
2233
2259
|
if (state.usage.outputTokens !== void 0) parts.push(`out ${state.usage.outputTokens}`);
|
|
2234
2260
|
return parts.length ? `\uFF08tokens ${parts.join(" \xB7 ")}\uFF09` : "";
|
|
2235
2261
|
}
|
|
2236
|
-
function renderStandard(state) {
|
|
2262
|
+
function renderStandard(state, now) {
|
|
2237
2263
|
const elements = [];
|
|
2238
2264
|
if (state.reasoning.content) {
|
|
2239
2265
|
elements.push(
|
|
@@ -2262,7 +2288,7 @@ function renderStandard(state) {
|
|
|
2262
2288
|
if (usage) elements.push(noteMd(usage));
|
|
2263
2289
|
}
|
|
2264
2290
|
if (state.terminal === "running") {
|
|
2265
|
-
if (state.footer) elements.push(footerStatus(state.footer));
|
|
2291
|
+
if (state.footer) elements.push(footerStatus(state.footer, state, now));
|
|
2266
2292
|
elements.push(stopButton());
|
|
2267
2293
|
}
|
|
2268
2294
|
return {
|
|
@@ -2274,10 +2300,10 @@ function renderStandard(state) {
|
|
|
2274
2300
|
body: { elements }
|
|
2275
2301
|
};
|
|
2276
2302
|
}
|
|
2277
|
-
function renderCompact(state) {
|
|
2303
|
+
function renderCompact(state, now) {
|
|
2278
2304
|
const elements = [noteMd(summaryText(state))];
|
|
2279
2305
|
if (state.terminal === "running" && state.footer) {
|
|
2280
|
-
elements.push(footerStatus(state.footer));
|
|
2306
|
+
elements.push(footerStatus(state.footer, state, now));
|
|
2281
2307
|
}
|
|
2282
2308
|
if (state.terminal === "running") {
|
|
2283
2309
|
elements.push(stopButton());
|
|
@@ -2291,7 +2317,7 @@ function renderCompact(state) {
|
|
|
2291
2317
|
body: { elements }
|
|
2292
2318
|
};
|
|
2293
2319
|
}
|
|
2294
|
-
function renderDetailed(state) {
|
|
2320
|
+
function renderDetailed(state, now) {
|
|
2295
2321
|
const elements = [];
|
|
2296
2322
|
if (state.reasoning.content) {
|
|
2297
2323
|
elements.push(
|
|
@@ -2330,7 +2356,7 @@ ${safeJsonPreview(tool.input)}
|
|
|
2330
2356
|
if (usage) elements.push(noteMd(usage));
|
|
2331
2357
|
}
|
|
2332
2358
|
if (state.terminal === "running") {
|
|
2333
|
-
if (state.footer) elements.push(footerStatus(state.footer));
|
|
2359
|
+
if (state.footer) elements.push(footerStatus(state.footer, state, now));
|
|
2334
2360
|
elements.push(stopButton());
|
|
2335
2361
|
}
|
|
2336
2362
|
return {
|
|
@@ -2350,10 +2376,10 @@ function safeJsonPreview(value) {
|
|
|
2350
2376
|
return String(value).slice(0, 300);
|
|
2351
2377
|
}
|
|
2352
2378
|
}
|
|
2353
|
-
function renderCard(state, density = "standard") {
|
|
2354
|
-
if (density === "compact") return renderCompact(state);
|
|
2355
|
-
if (density === "detailed") return renderDetailed(state);
|
|
2356
|
-
return renderStandard(state);
|
|
2379
|
+
function renderCard(state, density = "standard", now = Date.now()) {
|
|
2380
|
+
if (density === "compact") return renderCompact(state, now);
|
|
2381
|
+
if (density === "detailed") return renderDetailed(state, now);
|
|
2382
|
+
return renderStandard(state, now);
|
|
2357
2383
|
}
|
|
2358
2384
|
|
|
2359
2385
|
// src/card/approval-card.ts
|
|
@@ -2505,7 +2531,12 @@ async function runAgentBatch(input) {
|
|
|
2505
2531
|
} : {}
|
|
2506
2532
|
});
|
|
2507
2533
|
input.activeRuns.set(input.scope, { runId, stop: run.stop });
|
|
2508
|
-
|
|
2534
|
+
const now = Date.now();
|
|
2535
|
+
let state = {
|
|
2536
|
+
...initialState,
|
|
2537
|
+
startedAtMs: now,
|
|
2538
|
+
lastActivityMs: now
|
|
2539
|
+
};
|
|
2509
2540
|
const stopRequested = { value: false };
|
|
2510
2541
|
const timeoutMs = input.runPolicies?.get(input.scope) ?? input.runTimeoutMs ?? 0;
|
|
2511
2542
|
let timedOut = false;
|
|
@@ -2516,10 +2547,16 @@ async function runAgentBatch(input) {
|
|
|
2516
2547
|
input.chatId,
|
|
2517
2548
|
renderCard(state, density),
|
|
2518
2549
|
async (controller) => {
|
|
2550
|
+
const ticker = setInterval(() => {
|
|
2551
|
+
void controller.update(renderCard(state, density, Date.now())).catch(() => {
|
|
2552
|
+
});
|
|
2553
|
+
}, 5e3);
|
|
2554
|
+
ticker.unref?.();
|
|
2519
2555
|
const consume = async () => {
|
|
2520
2556
|
for await (const event of run.events) {
|
|
2521
2557
|
if (timedOut) return;
|
|
2522
2558
|
state = applyEvent(state, event, stopRequested);
|
|
2559
|
+
state = { ...state, lastActivityMs: Date.now() };
|
|
2523
2560
|
if (event.type === "final_text") {
|
|
2524
2561
|
assistantOutput = event.content;
|
|
2525
2562
|
} else if (event.type === "text") {
|
|
@@ -2560,8 +2597,9 @@ async function runAgentBatch(input) {
|
|
|
2560
2597
|
await consume();
|
|
2561
2598
|
}
|
|
2562
2599
|
state = timedOut ? markIdleTimeout(state, timeoutMs / 6e4) : finalizeIfRunning(state);
|
|
2563
|
-
await controller.update(renderCard(state, density));
|
|
2600
|
+
await controller.update(renderCard(state, density, Date.now()));
|
|
2564
2601
|
} finally {
|
|
2602
|
+
clearInterval(ticker);
|
|
2565
2603
|
if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
|
|
2566
2604
|
unsubscribeSettled?.();
|
|
2567
2605
|
}
|
|
@@ -4188,7 +4226,18 @@ async function startChannel(deps) {
|
|
|
4188
4226
|
log.fail("channel-command", error, { scope });
|
|
4189
4227
|
return false;
|
|
4190
4228
|
});
|
|
4191
|
-
if (!handled)
|
|
4229
|
+
if (!handled) {
|
|
4230
|
+
const queued = deps.pending.size(scope);
|
|
4231
|
+
const busy = queued > 0 || deps.pending.isFlushing(scope) || deps.pending.isBlocked(scope);
|
|
4232
|
+
if (busy) {
|
|
4233
|
+
await commandChannel.sendMarkdown(
|
|
4234
|
+
msg.chatId,
|
|
4235
|
+
`\u23F3 \u5DF2\u6536\u5230\uFF0C\u5F53\u524D\u6392\u961F\u4E2D\uFF08\u961F\u5217 ${queued + 1} \u6761\uFF09\u3002\u4EFB\u52A1\u5F00\u59CB\u540E\u4F1A\u5728\u8FD9\u91CC\u5B9E\u65F6\u663E\u793A\u8FDB\u5EA6\u3002`,
|
|
4236
|
+
{ replyTo: msg.messageId }
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
4239
|
+
deps.pending.push(scope, msg);
|
|
4240
|
+
}
|
|
4192
4241
|
},
|
|
4193
4242
|
cardAction: async (event) => {
|
|
4194
4243
|
const value = event.action.value && typeof event.action.value === "object" ? event.action.value : void 0;
|