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/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
- return [
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
- ].join("\n");
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(join3(root, "cordis.patch.yml"), patchYamlFor(), "utf8");
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
- const 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";
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
- let state = initialState;
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) deps.pending.push(scope, msg);
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;