@co0ontty/wand 4.80.1 → 4.81.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.
@@ -56,6 +56,7 @@ export declare class WsBroadcastManager {
56
56
  * leadingBlockOffset/leadingBlockTotal;否则 turn 级窗口(字段与改动前一致)。
57
57
  */
58
58
  private windowForClient;
59
+ private rememberToolNames;
59
60
  private broadcast;
60
61
  /**
61
62
  * Resume accepting business messages after the queue crosses the low-water
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { WebSocket } from "ws";
6
6
  import { readSessionCookie } from "./auth.js";
7
- import { blockWindowMessagesForTransport, windowMessagesForTransport } from "./message-truncator.js";
7
+ import { blockWindowMessagesForTransport, compactToolMessagesForTransport, windowMessagesForTransport } from "./message-truncator.js";
8
8
  import { boundSessionEventData, toSessionDetailDTO } from "./session-transport.js";
9
9
  import { enrichStructuredMessages } from "./structured-client-protocol.js";
10
10
  // ── Constants ──
@@ -124,6 +124,8 @@ export class WsBroadcastManager {
124
124
  backpressurePaused: false,
125
125
  outputSeqBySession: new Map(),
126
126
  pendingResyncSessions: new Set(),
127
+ compactTools: false,
128
+ toolNamesBySession: new Map(),
127
129
  lastSeenAt: Date.now(),
128
130
  ptySubscriptions: new Map(),
129
131
  };
@@ -145,6 +147,8 @@ export class WsBroadcastManager {
145
147
  try {
146
148
  const msg = JSON.parse(data.toString());
147
149
  if (msg.type === "subscribe" && msg.sessionId) {
150
+ client.compactTools = client.compactTools
151
+ || msg.compactTools === true || msg.capabilities?.compactTools === true;
148
152
  // 客户端可在 subscribe 时声明块级窗口预算(iOS);后续 init/resync/广播
149
153
  // 全量快照都按它块级窗口化。不带则保持 turn 级(Web/Android)。
150
154
  if (Number.isSafeInteger(msg.blockBudget) && msg.blockBudget > 0) {
@@ -153,6 +157,7 @@ export class WsBroadcastManager {
153
157
  // 默认仍是历史的“切换当前会话”;分屏池显式用 mode:add 叠加订阅。
154
158
  if (msg.mode !== "add") {
155
159
  client.ptySubscriptions.clear();
160
+ client.toolNamesBySession.clear();
156
161
  }
157
162
  client.ptySubscriptions.set(msg.sessionId, {
158
163
  supportsAck: msg.capabilities?.ptyAck === true,
@@ -176,6 +181,7 @@ export class WsBroadcastManager {
176
181
  }
177
182
  else if (msg.type === "unsubscribe" && msg.sessionId) {
178
183
  client.ptySubscriptions.delete(msg.sessionId);
184
+ client.toolNamesBySession.delete(msg.sessionId);
179
185
  }
180
186
  else if (msg.type === "resync" && msg.sessionId) {
181
187
  this.flushOutput(msg.sessionId);
@@ -409,6 +415,10 @@ export class WsBroadcastManager {
409
415
  return { messages: undefined, messageOffset: 0, messageTotal: 0 };
410
416
  }
411
417
  messages = enrichStructuredMessages(messages, sessionId);
418
+ if (client.compactTools) {
419
+ this.rememberToolNames(client, sessionId, messages);
420
+ messages = compactToolMessagesForTransport(messages);
421
+ }
412
422
  if (client.blockBudget && client.blockBudget > 0) {
413
423
  const w = blockWindowMessagesForTransport(messages, this.getCardDefaults(), client.blockBudget);
414
424
  return {
@@ -423,6 +433,22 @@ export class WsBroadcastManager {
423
433
  const w = windowMessagesForTransport(messages, this.getCardDefaults());
424
434
  return { messages: w.messages, messageOffset: w.messageOffset, messageTotal: w.messageTotal };
425
435
  }
436
+ rememberToolNames(client, sessionId, messages) {
437
+ let names = client.toolNamesBySession.get(sessionId);
438
+ if (!names) {
439
+ names = new Map();
440
+ client.toolNamesBySession.set(sessionId, names);
441
+ }
442
+ for (const turn of messages) {
443
+ for (const block of turn.content) {
444
+ if (block.type !== "tool_use")
445
+ continue;
446
+ names.set(block.id, block.name);
447
+ if (names.size > 2_000)
448
+ names.delete(names.keys().next().value);
449
+ }
450
+ }
451
+ }
426
452
  broadcast(event) {
427
453
  if (this.disposed)
428
454
  return;
@@ -444,9 +470,18 @@ export class WsBroadcastManager {
444
470
  : undefined;
445
471
  let turnWindowedEvent;
446
472
  const eventForClient = (client) => {
447
- if (!hasFullMessages)
448
- return boundedEvent;
449
- if (client.blockBudget && client.blockBudget > 0) {
473
+ if (!hasFullMessages) {
474
+ if (!client.compactTools || !data?.lastMessage)
475
+ return boundedEvent;
476
+ const last = enrichStructuredMessages([data.lastMessage], event.sessionId);
477
+ this.rememberToolNames(client, event.sessionId, last);
478
+ const names = client.toolNamesBySession.get(event.sessionId);
479
+ return {
480
+ ...boundedEvent,
481
+ data: { ...data, lastMessage: compactToolMessagesForTransport(last, names)[0] },
482
+ };
483
+ }
484
+ if (client.compactTools || (client.blockBudget && client.blockBudget > 0)) {
450
485
  return {
451
486
  ...boundedEvent,
452
487
  data: { ...data, ...this.windowForClient(client, event.sessionId, rawMessages) },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "4.80.1",
3
+ "version": "4.81.0",
4
4
  "description": "A web console for Claude Code, Codex, OpenCode, and other local CLI tools.",
5
5
  "type": "module",
6
6
  "bin": {