@blade-hq/agent-client 2610.0.3 → 2610.0.4

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 CHANGED
@@ -780,3 +780,5 @@ HTML 一律判无效);`isInternalStatusToolUiCard` 识别内部「阶段进
780
780
  会话列表和详情的 `SessionInfo.initiator` 表示首次发起来源:`ui` 为界面,`sdk` 为 SDK;旧会话缺少可靠记录时为 `unknown`,尚未发起会话时为 `null` 或未提供。它不等同于后台运行标记 `is_headless`,后续继续会话不会改变来源。
781
781
 
782
782
  后台唤醒输入可用 `isBackgroundWakeMessage(message)` 识别,它读取消息的结构化通知 block。自定义 UI 应为这类输入显示说明卡片;`isHiddenInternalMessage` 仍将其视为内部输入,避免作为用户发言或回溯导航点。
783
+
784
+ `@blade-hq/agent-client/internal`(exports 子路径 `./internal`)供 Blade 自有渲染层共享服务器时钟。普通接入方使用会话快照中的 `chat_run` 读取本轮整体区间,`duration_ms` 仍表示模型请求耗时。
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/version.ts
6
6
  var SDK_NAME = "agent-client";
7
- var SDK_VERSION = true ? "2610.0.3" : "1.1.1";
7
+ var SDK_VERSION = true ? "2610.0.4" : "1.1.1";
8
8
 
9
9
  // src/auth-login.ts
10
10
  function isAuthCallbackMessage(value) {
@@ -584,6 +584,54 @@ var SessionSetupError = class extends Error {
584
584
  cause;
585
585
  };
586
586
 
587
+ // src/shared/projection/chat-run.ts
588
+ function readChatRun(payload, ended = false) {
589
+ const runId = payload.chat_run_id;
590
+ const start = payload.started_at;
591
+ if (typeof runId !== "string" || !runId || typeof start !== "string" || !Number.isFinite(Date.parse(start))) return null;
592
+ const status = payload.status === "interrupted" ? "cancelled" : payload.status;
593
+ return {
594
+ run_id: runId,
595
+ scope: "chat",
596
+ loop_id: "root",
597
+ started_at: start,
598
+ duration_ms: ended && typeof payload.duration_ms === "number" ? Math.max(0, payload.duration_ms) : null,
599
+ status: ended && ["completed", "failed", "paused", "cancelled"].includes(String(status)) ? status : "incomplete"
600
+ };
601
+ }
602
+ function isUserSupplement(entry) {
603
+ const message = entry.message;
604
+ if (!message || message.role !== "user" || !("_display_content" in message)) return false;
605
+ const content = message.content;
606
+ if (typeof content === "string") return content.startsWith("<user-supplement>") && content.endsWith("</user-supplement>");
607
+ if (!Array.isArray(content) || content.length < 2) return false;
608
+ const first = content[0], last = content[content.length - 1];
609
+ return first?.type === "text" && first.text === "<user-supplement>" && last?.type === "text" && last.text === "</user-supplement>";
610
+ }
611
+ function applyHistoryChatRuns(turns, entries, aliases) {
612
+ const byRun = /* @__PURE__ */ new Map();
613
+ const supplements = /* @__PURE__ */ new Set();
614
+ for (const entry of entries) {
615
+ const supplement = aliases.get(String(entry.id ?? ""));
616
+ if (supplement && isUserSupplement(entry)) supplements.add(supplement.turn_id);
617
+ if (entry.kind !== "run_timing" || !entry.data || entry.data.scope !== "chat") continue;
618
+ const data = entry.data;
619
+ const anchor = aliases.get(String(data.anchor_entry_id ?? ""));
620
+ const timing = readChatRun({ ...data, chat_run_id: data.run_id }, data.status !== "incomplete");
621
+ if (anchor && timing) byRun.set(timing.run_id, { anchor, timing });
622
+ }
623
+ const runs = [...byRun.values()].sort((a, b) => a.anchor.sequence - b.anchor.sequence);
624
+ let index = -1;
625
+ let active;
626
+ for (const turn of turns) {
627
+ if (turn.loop_id === "root" && (turn.role === "user" && !supplements.has(turn.turn_id) || turn.blocks.some((block) => block.type === "ask_user_answer"))) active = void 0;
628
+ while (index + 1 < runs.length && runs[index + 1].anchor.sequence <= turn.sequence) active = runs[++index].timing;
629
+ if (active && turn.loop_id === "root" && turn.role === "assistant" && (turn.kind ?? "message") === "message") {
630
+ turn.chat_run = active;
631
+ }
632
+ }
633
+ }
634
+
587
635
  // src/shared/projection/helpers.ts
588
636
  function upsertToolCall(state, toolCallId, toolName, displayName, args) {
589
637
  if (!toolCallId) return;
@@ -1270,6 +1318,7 @@ function acceptedPostChatFollowupCompletesLatestRun(events, updates) {
1270
1318
  updates.flatMap((update) => {
1271
1319
  if (update.kind !== "upsert") return [];
1272
1320
  const followup = update.turn.post_chat_followup;
1321
+ if (update.turn.chat_run?.status === "incomplete") return [];
1273
1322
  return followup?.assistant_entry_id ? [followup.assistant_entry_id] : [];
1274
1323
  })
1275
1324
  );
@@ -1352,6 +1401,9 @@ var ClientProjectionBuilder = class {
1352
1401
  pendingMemoryRefs = /* @__PURE__ */ new Map();
1353
1402
  activeCompactions = /* @__PURE__ */ new Map();
1354
1403
  failedCompactions = /* @__PURE__ */ new Map();
1404
+ // undefined: history may seed the builder; null: a live start reported no interval.
1405
+ chatRun = void 0;
1406
+ chatTurnIds = /* @__PURE__ */ new Set();
1355
1407
  seq = 0;
1356
1408
  syntheticCounter = 0;
1357
1409
  displayNameResolver;
@@ -1375,6 +1427,35 @@ var ClientProjectionBuilder = class {
1375
1427
  return [...nonUpserts, ...lastUpsertByTurn.values()];
1376
1428
  }
1377
1429
  processEvent(event) {
1430
+ if (event.type === "chat:start") {
1431
+ const timing = readChatRun(event.payload);
1432
+ this.chatTurnIds.clear();
1433
+ this.chatRun = timing;
1434
+ }
1435
+ const ended = event.type === "chat:end" ? readChatRun(event.payload, true) : null;
1436
+ if (ended && (!this.chatRun || ended.run_id === this.chatRun.run_id)) this.chatRun = ended;
1437
+ const updates = this.processTurnEvent(event) ?? [];
1438
+ const changed = /* @__PURE__ */ new Set();
1439
+ for (const update of updates) {
1440
+ if (update.kind !== "upsert" || update.turn.loop_id !== "root" || update.turn.role !== "assistant" || !this.chatRun) continue;
1441
+ update.turn.chat_run = this.chatRun;
1442
+ this.chatTurnIds.add(update.turn.turn_id);
1443
+ changed.add(update.turn.turn_id);
1444
+ }
1445
+ if (ended && ended.run_id === this.chatRun?.run_id) {
1446
+ for (const id of this.chatTurnIds) {
1447
+ if (changed.has(id)) continue;
1448
+ const previous = this.turnsById.get(id);
1449
+ if (!previous) continue;
1450
+ const turn = { ...previous, chat_run: ended };
1451
+ this.turnsById.set(id, turn);
1452
+ if (this.latestTurns.get("root")?.turn_id === id) this.latestTurns.set("root", turn);
1453
+ updates.push({ kind: "upsert", turn });
1454
+ }
1455
+ }
1456
+ return updates.length ? updates : null;
1457
+ }
1458
+ processTurnEvent(event) {
1378
1459
  const payload = event.payload;
1379
1460
  const loopId = String(payload.loop_name ?? "root");
1380
1461
  switch (event.type) {
@@ -1486,6 +1567,8 @@ var ClientProjectionBuilder = class {
1486
1567
  }
1487
1568
  }
1488
1569
  reset() {
1570
+ this.chatRun = void 0;
1571
+ this.chatTurnIds.clear();
1489
1572
  this.activeTurns.clear();
1490
1573
  this.lastTurnIds.clear();
1491
1574
  this.latestTurns.clear();
@@ -1518,6 +1601,15 @@ var ClientProjectionBuilder = class {
1518
1601
  }
1519
1602
  this.latestTurns = latestTurns;
1520
1603
  this.turnsById = turnsById;
1604
+ const root = latestTurns.get("root");
1605
+ const timing = root?.role === "assistant" ? root.chat_run ?? null : null;
1606
+ if (this.chatRun === void 0 || timing && timing.run_id === this.chatRun?.run_id) {
1607
+ this.chatRun = timing;
1608
+ this.chatTurnIds.clear();
1609
+ }
1610
+ for (const turn of turns) {
1611
+ if (turn.chat_run && turn.chat_run.run_id === this.chatRun?.run_id) this.chatTurnIds.add(turn.turn_id);
1612
+ }
1521
1613
  }
1522
1614
  // --- Event handlers ---
1523
1615
  onMemoryInject(loopId, payload) {
@@ -2614,6 +2706,7 @@ function projectHistory(entries) {
2614
2706
  );
2615
2707
  }
2616
2708
  }
2709
+ applyHistoryChatRuns(turns, entries, turnEntryAliases);
2617
2710
  return turns;
2618
2711
  }
2619
2712
  function parseToolArguments(value) {
@@ -4189,7 +4282,8 @@ function projectionToMessage(turn) {
4189
4282
  entry_id: turn.turn_id,
4190
4283
  status: turn.status,
4191
4284
  ...typeof turn.duration_ms === "number" ? { duration_ms: turn.duration_ms } : {},
4192
- ...turn.started_at ? { timestamp: turn.started_at } : {}
4285
+ ...turn.started_at ? { timestamp: turn.started_at } : {},
4286
+ ...turn.chat_run ? { chat_run: turn.chat_run } : {}
4193
4287
  };
4194
4288
  }
4195
4289
  const content = buildMessageContent2(turn) || workspaceNotificationContent(turn);
@@ -4218,6 +4312,7 @@ function projectionToMessage(turn) {
4218
4312
  status: turn.status,
4219
4313
  ...typeof turn.duration_ms === "number" ? { duration_ms: turn.duration_ms } : {},
4220
4314
  ...turn.started_at ? { timestamp: turn.started_at } : {},
4315
+ ...turn.chat_run ? { chat_run: turn.chat_run } : {},
4221
4316
  ...turn.memory_refs?.length ? { memory_refs: turn.memory_refs } : {}
4222
4317
  };
4223
4318
  }