@co0ontty/wand 4.76.0 → 4.77.0-beta.g6c4df97

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.
Files changed (71) hide show
  1. package/dist/agent-dispatch.d.ts +46 -0
  2. package/dist/agent-dispatch.js +84 -0
  3. package/dist/ai-team-availability.d.ts +69 -0
  4. package/dist/ai-team-availability.js +100 -0
  5. package/dist/ai-team-prompts.d.ts +113 -0
  6. package/dist/ai-team-prompts.js +435 -0
  7. package/dist/ai-team-runner.d.ts +241 -0
  8. package/dist/ai-team-runner.js +1370 -0
  9. package/dist/ai-team-types.d.ts +147 -0
  10. package/dist/ai-team-types.js +32 -0
  11. package/dist/attention.d.ts +40 -0
  12. package/dist/attention.js +86 -0
  13. package/dist/build-info.json +4 -4
  14. package/dist/claude-sdk-runner.d.ts +5 -0
  15. package/dist/claude-sdk-runner.js +2 -1
  16. package/dist/commit-task-archive.d.ts +3 -0
  17. package/dist/commit-task-archive.js +43 -0
  18. package/dist/config.js +0 -2
  19. package/dist/git-quick-commit.d.ts +2 -1
  20. package/dist/git-quick-commit.js +72 -60
  21. package/dist/message-truncator.d.ts +41 -5
  22. package/dist/message-truncator.js +255 -38
  23. package/dist/missions.d.ts +4 -2
  24. package/dist/missions.js +2 -2
  25. package/dist/process-manager.d.ts +1 -0
  26. package/dist/process-manager.js +17 -8
  27. package/dist/prompt-optimizer.js +13 -13
  28. package/dist/server-ai-team-routes.d.ts +11 -0
  29. package/dist/server-ai-team-routes.js +283 -0
  30. package/dist/server-attention-routes.d.ts +9 -0
  31. package/dist/server-attention-routes.js +26 -0
  32. package/dist/server-file-routes.js +1 -1
  33. package/dist/server-session-routes.js +41 -5
  34. package/dist/server-settings-routes.js +1 -1
  35. package/dist/server-task-routes.d.ts +2 -0
  36. package/dist/server-task-routes.js +6 -40
  37. package/dist/server-workspace-routes.js +13 -5
  38. package/dist/server.js +43 -0
  39. package/dist/session-topic.js +13 -13
  40. package/dist/session-transport.d.ts +3 -0
  41. package/dist/session-transport.js +1 -0
  42. package/dist/storage.d.ts +54 -1
  43. package/dist/storage.js +413 -2
  44. package/dist/structured-claude-adapter.d.ts +5 -0
  45. package/dist/structured-claude-adapter.js +13 -2
  46. package/dist/structured-claude-protocol.d.ts +2 -0
  47. package/dist/structured-claude-protocol.js +4 -0
  48. package/dist/structured-codex-adapter.js +2 -2
  49. package/dist/structured-grok-adapter.js +2 -1
  50. package/dist/structured-opencode-adapter.js +2 -2
  51. package/dist/structured-pi-adapter.js +2 -1
  52. package/dist/structured-provider-common.d.ts +17 -0
  53. package/dist/structured-provider-common.js +52 -0
  54. package/dist/structured-qoder-adapter.js +2 -1
  55. package/dist/structured-session-manager.d.ts +18 -1
  56. package/dist/structured-session-manager.js +67 -10
  57. package/dist/system-ai.d.ts +13 -2
  58. package/dist/system-ai.js +9 -5
  59. package/dist/task-title.js +10 -10
  60. package/dist/types.d.ts +25 -2
  61. package/dist/wand-task-sync.d.ts +1 -0
  62. package/dist/wand-task-sync.js +1 -0
  63. package/dist/web-ui/content/ai-teams.js +12 -0
  64. package/dist/web-ui/content/scripts.js +86 -5301
  65. package/dist/web-ui/content/styles.css +1 -1
  66. package/dist/web-ui/embedded-assets.d.ts +1 -0
  67. package/dist/web-ui/embedded-assets.js +3 -2
  68. package/dist/web-ui/scripts.d.ts +8 -0
  69. package/dist/web-ui/scripts.js +28 -2
  70. package/dist/ws-broadcast.js +52 -19
  71. package/package.json +1 -1
@@ -1 +1,9 @@
1
1
  export declare function getScriptContent(configPath: string): string;
2
+ /**
3
+ * 按需加载的 AI 团队脚本(content/ai-teams.js)。与 scripts.js 一样优先读磁盘上的新产物,
4
+ * 自更新期间目录消失时退回内嵌副本;hash 进页面 meta 里的地址,长缓存按它失效。
5
+ */
6
+ export declare function getAiTeamsChunk(): {
7
+ content: string;
8
+ hash: string;
9
+ };
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import * as fs from "node:fs";
2
3
  import * as path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
@@ -27,6 +28,31 @@ export function getScriptContent(configPath) {
27
28
  // old process is still serving requests. The embedded build asset keeps the
28
29
  // app shell renderable even when dist/web-ui/content has disappeared.
29
30
  }
30
- // Inject the config path
31
- return _scriptCache.replace("${escapeHtml(configPath)}", escapeHtml(configPath));
31
+ // Inject the config path and the hashed on-demand AI teams script URL
32
+ return _scriptCache
33
+ .replace("${escapeHtml(configPath)}", escapeHtml(configPath))
34
+ .replace("${aiTeamsChunkSrc}", `/assets/ai-teams.js?v=${getAiTeamsChunk().hash}`);
35
+ }
36
+ let _aiTeamsChunk = withHash(EMBEDDED_WEB_ASSETS.aiTeamsJs);
37
+ let _aiTeamsChunkMtimeMs = 0;
38
+ function withHash(content) {
39
+ return { content, hash: createHash("md5").update(content).digest("hex").slice(0, 8) };
40
+ }
41
+ /**
42
+ * 按需加载的 AI 团队脚本(content/ai-teams.js)。与 scripts.js 一样优先读磁盘上的新产物,
43
+ * 自更新期间目录消失时退回内嵌副本;hash 进页面 meta 里的地址,长缓存按它失效。
44
+ */
45
+ export function getAiTeamsChunk() {
46
+ const chunkPath = path.join(__dirname, "content", "ai-teams.js");
47
+ try {
48
+ const stat = fs.statSync(chunkPath);
49
+ if (stat.mtimeMs !== _aiTeamsChunkMtimeMs) {
50
+ _aiTeamsChunk = withHash(fs.readFileSync(chunkPath, "utf-8"));
51
+ _aiTeamsChunkMtimeMs = stat.mtimeMs;
52
+ }
53
+ }
54
+ catch {
55
+ // 同 getScriptContent:用内嵌副本兜底。
56
+ }
57
+ return _aiTeamsChunk;
32
58
  }
@@ -157,6 +157,7 @@ export class WsBroadcastManager {
157
157
  supportsAck: msg.capabilities?.ptyAck === true,
158
158
  unackedBytes: 0,
159
159
  degraded: false,
160
+ resyncRequested: false,
160
161
  lastResyncNoticeAt: 0,
161
162
  });
162
163
  this.flushOutput(msg.sessionId);
@@ -350,6 +351,25 @@ export class WsBroadcastManager {
350
351
  const seq = (client.outputSeqBySession.get(sessionId) ?? 0) + 1;
351
352
  client.outputSeqBySession.set(sessionId, seq);
352
353
  client.pendingResyncSessions.delete(sessionId);
354
+ const subscription = client.ptySubscriptions.get(sessionId);
355
+ if (resync && subscription) {
356
+ // sendInit bypasses the per-client send queue. Queued pre-snapshot output must not
357
+ // arrive after the new screen (or trigger a false sequence gap on the client).
358
+ client.sendQueue = client.sendQueue.filter((frame) => {
359
+ try {
360
+ return JSON.parse(frame).sessionId !== sessionId;
361
+ }
362
+ catch {
363
+ return true;
364
+ }
365
+ });
366
+ if (subscription.resyncTimer)
367
+ clearTimeout(subscription.resyncTimer);
368
+ subscription.resyncTimer = undefined;
369
+ subscription.unackedBytes = 0;
370
+ subscription.degraded = false;
371
+ subscription.resyncRequested = false;
372
+ }
353
373
  const terminalState = (snapshot.sessionKind ?? "pty") === "pty"
354
374
  ? this.port?.getTerminalState?.(sessionId) ?? undefined
355
375
  : undefined;
@@ -385,6 +405,7 @@ export class WsBroadcastManager {
385
405
  messageTotal: w.messageTotal,
386
406
  leadingBlockOffset: w.leadingBlockOffset,
387
407
  leadingBlockTotal: w.leadingBlockTotal,
408
+ leadingVisibleCount: w.leadingVisibleCount,
388
409
  };
389
410
  }
390
411
  const w = windowMessagesForTransport(messages, this.getCardDefaults());
@@ -438,18 +459,11 @@ export class WsBroadcastManager {
438
459
  continue;
439
460
  if (isRawPtyOutput && !client.ptySubscriptions.has(event.sessionId))
440
461
  continue;
441
- const clientEvent = eventForClient(client);
442
- // Stamp output events with a per-(client, session) sequence number so
443
- // the client can detect a gap caused by backpressure drops.
444
- let outgoing = clientEvent;
445
- if (event.type === "output") {
446
- const seq = (client.outputSeqBySession.get(event.sessionId) ?? 0) + 1;
447
- client.outputSeqBySession.set(event.sessionId, seq);
448
- outgoing = { ...clientEvent, seq };
449
- }
450
462
  const subscription = client.ptySubscriptions.get(event.sessionId);
451
463
  const usesPtyAckFlowControl = isRawPtyOutput && subscription?.supportsAck === true;
452
- if (usesPtyAckFlowControl && subscription.degraded) {
464
+ if (usesPtyAckFlowControl
465
+ && (subscription.degraded || subscription.unackedBytes >= PTY_UNACKED_HIGH_WATER)) {
466
+ subscription.degraded = true;
453
467
  // 已经积压到高水位的客户端:后面这些原始字节对它是**过期的 TUI 帧**(终端
454
468
  // 每帧整屏重绘,中间帧没有任何保留价值),停发这些分片;等它 ack 追上来
455
469
  // 再由 requestPtyResync 让它按终端快照重建屏幕。
@@ -460,12 +474,6 @@ export class WsBroadcastManager {
460
474
  // 才恢复;手机不回来就永久冻住。降级只影响积压的这一个客户端。
461
475
  continue;
462
476
  }
463
- if (usesPtyAckFlowControl) {
464
- outgoing = { ...outgoing, ptyBytes };
465
- subscription.unackedBytes += ptyBytes;
466
- if (subscription.unackedBytes >= PTY_UNACKED_HIGH_WATER)
467
- subscription.degraded = true;
468
- }
469
477
  // Backpressure only gates new business messages. The send pump must
470
478
  // keep draining messages that were already accepted; otherwise a queue
471
479
  // at the high-water mark can never reach the low-water mark again.
@@ -481,6 +489,21 @@ export class WsBroadcastManager {
481
489
  this.processWsQueue(client);
482
490
  continue;
483
491
  }
492
+ // Only delivered output consumes a sequence number. In particular, do not advance it
493
+ // while waiting for a resync: the next init is the new ordering boundary.
494
+ const clientEvent = eventForClient(client);
495
+ const seq = event.type === "output"
496
+ ? (client.outputSeqBySession.get(event.sessionId) ?? 0) + 1
497
+ : undefined;
498
+ if (seq !== undefined)
499
+ client.outputSeqBySession.set(event.sessionId, seq);
500
+ const outgoing = {
501
+ ...clientEvent,
502
+ ...(seq !== undefined ? { seq } : {}),
503
+ ...(usesPtyAckFlowControl ? { ptyBytes } : {}),
504
+ };
505
+ if (usesPtyAckFlowControl)
506
+ subscription.unackedBytes += ptyBytes;
484
507
  client.sendQueue.push(JSON.stringify(outgoing));
485
508
  if (client.sendQueue.length >= MAX_QUEUE_SIZE) {
486
509
  client.backpressurePaused = true;
@@ -535,7 +558,6 @@ export class WsBroadcastManager {
535
558
  return;
536
559
  subscription.unackedBytes = Math.max(0, subscription.unackedBytes - Math.max(0, Math.floor(bytes)));
537
560
  if (subscription.degraded && subscription.unackedBytes <= PTY_UNACKED_LOW_WATER) {
538
- subscription.degraded = false;
539
561
  this.requestPtyResync(client, sessionId);
540
562
  }
541
563
  }
@@ -547,11 +569,22 @@ export class WsBroadcastManager {
547
569
  */
548
570
  requestPtyResync(client, sessionId) {
549
571
  const subscription = client.ptySubscriptions.get(sessionId);
550
- if (!subscription)
572
+ if (!subscription || subscription.resyncRequested || subscription.resyncTimer)
551
573
  return;
552
574
  const now = Date.now();
553
- if (now - subscription.lastResyncNoticeAt < PTY_RESYNC_MIN_INTERVAL_MS)
575
+ const delay = PTY_RESYNC_MIN_INTERVAL_MS - (now - subscription.lastResyncNoticeAt);
576
+ if (delay > 0) {
577
+ subscription.resyncTimer = setTimeout(() => {
578
+ subscription.resyncTimer = undefined;
579
+ if (client.ptySubscriptions.get(sessionId) === subscription && subscription.degraded
580
+ && subscription.unackedBytes <= PTY_UNACKED_LOW_WATER) {
581
+ this.requestPtyResync(client, sessionId);
582
+ }
583
+ }, delay);
584
+ subscription.resyncTimer.unref();
554
585
  return;
586
+ }
587
+ subscription.resyncRequested = true;
555
588
  subscription.lastResyncNoticeAt = now;
556
589
  this.queueResyncNotice(client, sessionId, "pty_backlog_drop");
557
590
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@co0ontty/wand",
3
- "version": "4.76.0",
3
+ "version": "4.77.0-beta.g6c4df97",
4
4
  "description": "A web console for Claude Code, Codex, OpenCode, and other local CLI tools.",
5
5
  "type": "module",
6
6
  "bin": {