@code-yeongyu/senpi 2026.9.2 → 2026.9.3

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 (214) hide show
  1. package/CHANGELOG.md +124 -2
  2. package/dist/beta/omo-local-update-fingerprint.d.ts.map +1 -1
  3. package/dist/beta/omo-local-update-fingerprint.js +0 -1
  4. package/dist/beta/omo-local-update-fingerprint.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +1 -0
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +7 -4
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/dynamic-prompt/build.d.ts.map +1 -1
  10. package/dist/core/dynamic-prompt/build.js +2 -5
  11. package/dist/core/dynamic-prompt/build.js.map +1 -1
  12. package/dist/core/dynamic-prompt/index.d.ts +1 -2
  13. package/dist/core/dynamic-prompt/index.d.ts.map +1 -1
  14. package/dist/core/dynamic-prompt/index.js +1 -2
  15. package/dist/core/dynamic-prompt/index.js.map +1 -1
  16. package/dist/core/dynamic-prompt/intent-gate.d.ts.map +1 -1
  17. package/dist/core/dynamic-prompt/intent-gate.js +9 -24
  18. package/dist/core/dynamic-prompt/intent-gate.js.map +1 -1
  19. package/dist/core/dynamic-prompt/policies.d.ts.map +1 -1
  20. package/dist/core/dynamic-prompt/policies.js +3 -7
  21. package/dist/core/dynamic-prompt/policies.js.map +1 -1
  22. package/dist/core/dynamic-prompt/style.d.ts.map +1 -1
  23. package/dist/core/dynamic-prompt/style.js +6 -8
  24. package/dist/core/dynamic-prompt/style.js.map +1 -1
  25. package/dist/core/dynamic-prompt/verification.js +1 -1
  26. package/dist/core/dynamic-prompt/verification.js.map +1 -1
  27. package/dist/core/dynamic-prompt/working-task.d.ts +2 -0
  28. package/dist/core/dynamic-prompt/working-task.d.ts.map +1 -0
  29. package/dist/core/dynamic-prompt/working-task.js +10 -0
  30. package/dist/core/dynamic-prompt/working-task.js.map +1 -0
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/options.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/options.js +6 -2
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/options.js.map +1 -1
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js +2 -1
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js.map +1 -1
  37. package/dist/core/extensions/builtin/compaction/extension-wiring.js +1 -1
  38. package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -1
  39. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts +1 -1
  40. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/config-reload/watch-event-source.js +27 -9
  42. package/dist/core/extensions/builtin/config-reload/watch-event-source.js.map +1 -1
  43. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +52 -50
  45. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js.map +1 -1
  46. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +2 -1
  48. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js.map +1 -1
  49. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.d.ts.map +1 -1
  50. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.js +7 -1
  51. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-5.js.map +1 -1
  52. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.js +7 -1
  54. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-6.js.map +1 -1
  55. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.d.ts.map +1 -1
  56. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.js +7 -1
  57. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-7.js.map +1 -1
  58. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-8.d.ts.map +1 -1
  59. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-8.js +7 -1
  60. package/dist/core/extensions/builtin/prompt-preset/claude-opus-4-8.js.map +1 -1
  61. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.d.ts.map +1 -1
  62. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +2 -1
  63. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js.map +1 -1
  64. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts +50 -0
  65. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts.map +1 -0
  66. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js +66 -0
  67. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js.map +1 -0
  68. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +5 -1
  69. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  70. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +6 -1
  71. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  72. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.d.ts.map +1 -1
  73. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.js +11 -1
  74. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-6.js.map +1 -1
  75. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts.map +1 -1
  76. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js +11 -1
  77. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js.map +1 -1
  78. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.d.ts.map +1 -1
  79. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +2 -1
  80. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js.map +1 -1
  81. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  82. package/dist/core/extensions/builtin/prompt-preset/presets.js +8 -2
  83. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  84. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  85. package/dist/core/extensions/builtin/terminal/extension.js +8 -3
  86. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  87. package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts +4 -3
  88. package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts.map +1 -1
  89. package/dist/core/extensions/builtin/terminal/monitor-notify.js +18 -18
  90. package/dist/core/extensions/builtin/terminal/monitor-notify.js.map +1 -1
  91. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +7 -0
  92. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  93. package/dist/core/extensions/builtin/terminal/monitor-registry.js +37 -8
  94. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  95. package/dist/core/extensions/builtin/terminal/monitor-status.js +1 -1
  96. package/dist/core/extensions/builtin/terminal/monitor-status.js.map +1 -1
  97. package/dist/core/extensions/builtin/terminal/prompt.d.ts +1 -1
  98. package/dist/core/extensions/builtin/terminal/prompt.d.ts.map +1 -1
  99. package/dist/core/extensions/builtin/terminal/prompt.js +17 -13
  100. package/dist/core/extensions/builtin/terminal/prompt.js.map +1 -1
  101. package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/terminal/tools/bash-output.js +14 -2
  103. package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
  104. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +2 -0
  105. package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
  106. package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
  107. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  108. package/dist/core/extensions/builtin/terminal/tools/monitor.js +27 -10
  109. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  110. package/dist/core/provider-composer.d.ts.map +1 -1
  111. package/dist/core/provider-composer.js +17 -8
  112. package/dist/core/provider-composer.js.map +1 -1
  113. package/dist/core/settings-manager.d.ts +2 -2
  114. package/dist/core/settings-manager.d.ts.map +1 -1
  115. package/dist/core/settings-manager.js +5 -3
  116. package/dist/core/settings-manager.js.map +1 -1
  117. package/dist/index.d.ts +1 -1
  118. package/dist/index.d.ts.map +1 -1
  119. package/dist/index.js +1 -1
  120. package/dist/index.js.map +1 -1
  121. package/dist/modes/index.d.ts +1 -1
  122. package/dist/modes/index.d.ts.map +1 -1
  123. package/dist/modes/index.js +1 -1
  124. package/dist/modes/index.js.map +1 -1
  125. package/dist/modes/rpc/host-ensure.d.ts +6 -0
  126. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  127. package/dist/modes/rpc/host-ensure.js +36 -9
  128. package/dist/modes/rpc/host-ensure.js.map +1 -1
  129. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  130. package/dist/modes/rpc/host-lifecycle.js +16 -4
  131. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  132. package/dist/modes/rpc/multi-session-host.d.ts +7 -4
  133. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  134. package/dist/modes/rpc/multi-session-host.js +7 -4
  135. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  136. package/dist/modes/rpc/rpc-client.d.ts +8 -0
  137. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  138. package/dist/modes/rpc/rpc-client.js +62 -7
  139. package/dist/modes/rpc/rpc-client.js.map +1 -1
  140. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  141. package/dist/modes/rpc/rpc-mode.js +1 -1
  142. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  143. package/dist/modes/rpc/session-command-router.d.ts +22 -0
  144. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  145. package/dist/modes/rpc/session-command-router.js +132 -94
  146. package/dist/modes/rpc/session-command-router.js.map +1 -1
  147. package/dist/modes/rpc/session-event-fanout.d.ts +40 -0
  148. package/dist/modes/rpc/session-event-fanout.d.ts.map +1 -0
  149. package/dist/modes/rpc/session-event-fanout.js +146 -0
  150. package/dist/modes/rpc/session-event-fanout.js.map +1 -0
  151. package/dist/modes/rpc/session-event-writer.d.ts +6 -13
  152. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  153. package/dist/modes/rpc/session-event-writer.js +48 -96
  154. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  155. package/dist/modes/rpc/session-registry.d.ts +9 -8
  156. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  157. package/dist/modes/rpc/session-registry.js +14 -42
  158. package/dist/modes/rpc/session-registry.js.map +1 -1
  159. package/dist/modes/rpc/session-teardown.d.ts +12 -0
  160. package/dist/modes/rpc/session-teardown.d.ts.map +1 -0
  161. package/dist/modes/rpc/session-teardown.js +117 -0
  162. package/dist/modes/rpc/session-teardown.js.map +1 -0
  163. package/docs/rpc.md +10 -6
  164. package/docs/settings.md +4 -4
  165. package/docs/terminal-tools.md +26 -6
  166. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +57 -0
  167. package/node_modules/@code-yeongyu/senpi-codemode/README.md +4 -2
  168. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  169. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/skill-contribution.ts +43 -9
  170. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +4 -1
  171. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +10 -1
  172. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/SKILL.md +1 -1
  173. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/runtime-apis.md +1 -1
  174. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +3 -1
  175. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +2 -0
  176. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts +3 -0
  177. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  178. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -2
  179. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  181. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +1 -1
  182. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.d.ts +5 -0
  184. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.d.ts.map +1 -1
  185. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js +26 -4
  186. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js.map +1 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  188. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  189. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google-vertex.json +1 -1
  190. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
  191. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  192. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  193. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  194. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  195. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  196. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  197. package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/profiles.d.ts.map +1 -1
  198. package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/profiles.js +3 -1
  199. package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/profiles.js.map +1 -1
  200. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +1 -1
  201. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  202. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  203. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  204. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  205. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  206. package/package.json +7 -7
  207. package/dist/core/dynamic-prompt/exploration.d.ts +0 -2
  208. package/dist/core/dynamic-prompt/exploration.d.ts.map +0 -1
  209. package/dist/core/dynamic-prompt/exploration.js +0 -6
  210. package/dist/core/dynamic-prompt/exploration.js.map +0 -1
  211. package/dist/core/dynamic-prompt/parallel-tools.d.ts +0 -2
  212. package/dist/core/dynamic-prompt/parallel-tools.d.ts.map +0 -1
  213. package/dist/core/dynamic-prompt/parallel-tools.js +0 -8
  214. package/dist/core/dynamic-prompt/parallel-tools.js.map +0 -1
@@ -0,0 +1,146 @@
1
+ import { SocketEventSinkActor } from "./socket-event-fanout.js";
2
+ export const RENDERED_COMPONENT_RECORD = "__senpiRenderedComponent";
3
+ const BROADCAST_LIFECYCLE_RECORDS = new Set([
4
+ "agent_start",
5
+ "agent_settled",
6
+ "agent_idle",
7
+ "session_opened",
8
+ "session_closed",
9
+ ]);
10
+ export class SessionEventFanout {
11
+ constructor() {
12
+ this.connections = new Map();
13
+ this.sessionSnapshots = new Map();
14
+ this.connectionCapabilities = new Map();
15
+ this.connectionSessions = new Map();
16
+ this.registeredCapabilityConnections = new Set();
17
+ }
18
+ registerConnection(id, connection) {
19
+ const actor = new SocketEventSinkActor(connection, () => {
20
+ if (this.connections.get(id)?.actor === actor) {
21
+ this.connections.delete(id);
22
+ this.connectionCapabilities.delete(id);
23
+ this.connectionSessions.delete(id);
24
+ this.registeredCapabilityConnections.delete(id);
25
+ }
26
+ });
27
+ this.connections.set(id, { connection, actor });
28
+ this.connectionCapabilities.set(id, new Set());
29
+ this.connectionSessions.set(id, new Set());
30
+ }
31
+ unregisterConnection(id) {
32
+ const registered = this.connections.get(id);
33
+ if (!registered)
34
+ return;
35
+ registered.actor.close();
36
+ this.connections.delete(id);
37
+ this.connectionCapabilities.delete(id);
38
+ this.connectionSessions.delete(id);
39
+ this.registeredCapabilityConnections.delete(id);
40
+ }
41
+ attachConnectionToSession(id, sessionId) {
42
+ if (!this.connections.has(id))
43
+ return;
44
+ const sessions = this.connectionSessions.get(id) ?? new Set();
45
+ if (sessions.has(sessionId))
46
+ return;
47
+ sessions.add(sessionId);
48
+ this.connectionSessions.set(id, sessions);
49
+ this.replaySnapshot(id, sessionId);
50
+ }
51
+ detachConnectionFromSession(id, sessionId) {
52
+ this.connectionSessions.get(id)?.delete(sessionId);
53
+ }
54
+ setConnectionCapabilities(id, capabilities) {
55
+ const registered = this.connections.get(id);
56
+ if (!registered)
57
+ return;
58
+ const wasCapable = this.connectionCapabilities.get(id)?.has("rendered_components") ?? false;
59
+ this.connectionCapabilities.set(id, new Set(capabilities));
60
+ this.registeredCapabilityConnections.add(id);
61
+ if (!wasCapable && capabilities.includes("rendered_components"))
62
+ for (const sessionId of this.connectionSessions.get(id) ?? [])
63
+ this.replayRendered(id, sessionId);
64
+ }
65
+ clearConnectionCapabilities(id) {
66
+ if (this.connections.has(id)) {
67
+ this.connectionCapabilities.set(id, new Set());
68
+ this.registeredCapabilityConnections.delete(id);
69
+ }
70
+ }
71
+ hasRegisteredConnectionCapabilities(id) {
72
+ return this.registeredCapabilityConnections.has(id);
73
+ }
74
+ getConnectionCapabilities(id) {
75
+ if (!this.registeredCapabilityConnections.has(id))
76
+ return undefined;
77
+ return [...(this.connectionCapabilities.get(id) ?? [])];
78
+ }
79
+ hasCapableConnection(sessionId) {
80
+ for (const [id, capabilities] of this.connectionCapabilities)
81
+ if (capabilities.has("rendered_components") && this.connectionSessions.get(id)?.has(sessionId))
82
+ return true;
83
+ return false;
84
+ }
85
+ targets(sessionId, targetId, isTargeted, rendered, recordType) {
86
+ if (isTargeted)
87
+ return [targetId];
88
+ if (typeof recordType === "string" && BROADCAST_LIFECYCLE_RECORDS.has(recordType))
89
+ return this.connections.size > 0 ? [...this.connections.keys()] : [undefined];
90
+ if (rendered)
91
+ return this.connections.size > 0
92
+ ? [...this.connections.keys()].filter((id) => this.connectionCapabilities.get(id)?.has("rendered_components") &&
93
+ this.connectionSessions.get(id)?.has(sessionId))
94
+ : [undefined];
95
+ return this.connections.size > 0
96
+ ? [...this.connections.keys()].filter((id) => this.connectionSessions.get(id)?.has(sessionId))
97
+ : [undefined];
98
+ }
99
+ get(id) {
100
+ return this.connections.get(id);
101
+ }
102
+ values() {
103
+ return this.connections.values();
104
+ }
105
+ /** True when no socket connection is registered, i.e. records must fall back to the stdio lane. */
106
+ isEmpty() {
107
+ return this.connections.size === 0;
108
+ }
109
+ broadcast(line) {
110
+ for (const { actor } of this.connections.values())
111
+ actor.enqueue(line);
112
+ }
113
+ rememberSnapshot(sessionId, value, line) {
114
+ const event = value.assistantMessageEvent;
115
+ const record = { line, rendered: value[RENDERED_COMPONENT_RECORD] === true };
116
+ if (value.type === "message_start" || (value.type === "message_update" && event?.type === "text_start")) {
117
+ this.sessionSnapshots.set(sessionId, [record]);
118
+ }
119
+ else if (this.sessionSnapshots.has(sessionId)) {
120
+ this.sessionSnapshots.get(sessionId)?.push(record);
121
+ }
122
+ if (value.type === "message_end")
123
+ this.sessionSnapshots.delete(sessionId);
124
+ }
125
+ forgetSession(sessionId) {
126
+ this.sessionSnapshots.delete(sessionId);
127
+ }
128
+ replaySnapshot(id, sessionId) {
129
+ const actor = this.connections.get(id)?.actor;
130
+ if (!actor)
131
+ return;
132
+ const capable = this.connectionCapabilities.get(id)?.has("rendered_components") ?? false;
133
+ for (const record of this.sessionSnapshots.get(sessionId) ?? [])
134
+ if (!record.rendered || capable)
135
+ actor.enqueue(record.line);
136
+ }
137
+ replayRendered(id, sessionId) {
138
+ const actor = this.connections.get(id)?.actor;
139
+ if (!actor)
140
+ return;
141
+ for (const record of this.sessionSnapshots.get(sessionId) ?? [])
142
+ if (record.rendered)
143
+ actor.enqueue(record.line);
144
+ }
145
+ }
146
+ //# sourceMappingURL=session-event-fanout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-event-fanout.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-event-fanout.ts"],"names":[],"mappings":"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","sourcesContent":["import { SocketEventSinkActor } from \"./socket-event-fanout.ts\";\n\nexport type RawWriter = (chunk: string) => void;\nexport type BackpressureWaiter = () => Promise<void>;\n\nexport interface SessionEventWriterConnection {\n\treadonly writeRaw: RawWriter;\n\treadonly waitForBackpressure: BackpressureWaiter;\n}\n\nexport const RENDERED_COMPONENT_RECORD = \"__senpiRenderedComponent\";\n\nconst BROADCAST_LIFECYCLE_RECORDS = new Set([\n\t\"agent_start\",\n\t\"agent_settled\",\n\t\"agent_idle\",\n\t\"session_opened\",\n\t\"session_closed\",\n]);\n\ntype RegisteredConnection = {\n\treadonly connection: SessionEventWriterConnection;\n\treadonly actor: SocketEventSinkActor;\n};\n\ntype SnapshotRecord = { readonly line: string; readonly rendered: boolean };\n\nexport class SessionEventFanout {\n\tprivate readonly connections = new Map<string, RegisteredConnection>();\n\tprivate readonly sessionSnapshots = new Map<string, SnapshotRecord[]>();\n\tprivate readonly connectionCapabilities = new Map<string, Set<string>>();\n\tprivate readonly connectionSessions = new Map<string, Set<string>>();\n\tprivate readonly registeredCapabilityConnections = new Set<string>();\n\n\tregisterConnection(id: string, connection: SessionEventWriterConnection): void {\n\t\tconst actor = new SocketEventSinkActor(connection, () => {\n\t\t\tif (this.connections.get(id)?.actor === actor) {\n\t\t\t\tthis.connections.delete(id);\n\t\t\t\tthis.connectionCapabilities.delete(id);\n\t\t\t\tthis.connectionSessions.delete(id);\n\t\t\t\tthis.registeredCapabilityConnections.delete(id);\n\t\t\t}\n\t\t});\n\t\tthis.connections.set(id, { connection, actor });\n\t\tthis.connectionCapabilities.set(id, new Set());\n\t\tthis.connectionSessions.set(id, new Set());\n\t}\n\n\tunregisterConnection(id: string): void {\n\t\tconst registered = this.connections.get(id);\n\t\tif (!registered) return;\n\t\tregistered.actor.close();\n\t\tthis.connections.delete(id);\n\t\tthis.connectionCapabilities.delete(id);\n\t\tthis.connectionSessions.delete(id);\n\t\tthis.registeredCapabilityConnections.delete(id);\n\t}\n\n\tattachConnectionToSession(id: string, sessionId: string): void {\n\t\tif (!this.connections.has(id)) return;\n\t\tconst sessions = this.connectionSessions.get(id) ?? new Set<string>();\n\t\tif (sessions.has(sessionId)) return;\n\t\tsessions.add(sessionId);\n\t\tthis.connectionSessions.set(id, sessions);\n\t\tthis.replaySnapshot(id, sessionId);\n\t}\n\n\tdetachConnectionFromSession(id: string, sessionId: string): void {\n\t\tthis.connectionSessions.get(id)?.delete(sessionId);\n\t}\n\n\tsetConnectionCapabilities(id: string, capabilities: readonly string[]): void {\n\t\tconst registered = this.connections.get(id);\n\t\tif (!registered) return;\n\t\tconst wasCapable = this.connectionCapabilities.get(id)?.has(\"rendered_components\") ?? false;\n\t\tthis.connectionCapabilities.set(id, new Set(capabilities));\n\t\tthis.registeredCapabilityConnections.add(id);\n\t\tif (!wasCapable && capabilities.includes(\"rendered_components\"))\n\t\t\tfor (const sessionId of this.connectionSessions.get(id) ?? []) this.replayRendered(id, sessionId);\n\t}\n\n\tclearConnectionCapabilities(id: string): void {\n\t\tif (this.connections.has(id)) {\n\t\t\tthis.connectionCapabilities.set(id, new Set());\n\t\t\tthis.registeredCapabilityConnections.delete(id);\n\t\t}\n\t}\n\n\thasRegisteredConnectionCapabilities(id: string): boolean {\n\t\treturn this.registeredCapabilityConnections.has(id);\n\t}\n\n\tgetConnectionCapabilities(id: string): readonly string[] | undefined {\n\t\tif (!this.registeredCapabilityConnections.has(id)) return undefined;\n\t\treturn [...(this.connectionCapabilities.get(id) ?? [])];\n\t}\n\n\thasCapableConnection(sessionId: string): boolean {\n\t\tfor (const [id, capabilities] of this.connectionCapabilities)\n\t\t\tif (capabilities.has(\"rendered_components\") && this.connectionSessions.get(id)?.has(sessionId)) return true;\n\t\treturn false;\n\t}\n\n\ttargets(\n\t\tsessionId: string,\n\t\ttargetId: string | undefined,\n\t\tisTargeted: boolean,\n\t\trendered: boolean,\n\t\trecordType: unknown,\n\t): readonly (string | undefined)[] {\n\t\tif (isTargeted) return [targetId];\n\t\tif (typeof recordType === \"string\" && BROADCAST_LIFECYCLE_RECORDS.has(recordType))\n\t\t\treturn this.connections.size > 0 ? [...this.connections.keys()] : [undefined];\n\t\tif (rendered)\n\t\t\treturn this.connections.size > 0\n\t\t\t\t? [...this.connections.keys()].filter(\n\t\t\t\t\t\t(id) =>\n\t\t\t\t\t\t\tthis.connectionCapabilities.get(id)?.has(\"rendered_components\") &&\n\t\t\t\t\t\t\tthis.connectionSessions.get(id)?.has(sessionId),\n\t\t\t\t\t)\n\t\t\t\t: [undefined];\n\t\treturn this.connections.size > 0\n\t\t\t? [...this.connections.keys()].filter((id) => this.connectionSessions.get(id)?.has(sessionId))\n\t\t\t: [undefined];\n\t}\n\n\tget(id: string): RegisteredConnection | undefined {\n\t\treturn this.connections.get(id);\n\t}\n\n\tvalues(): IterableIterator<RegisteredConnection> {\n\t\treturn this.connections.values();\n\t}\n\n\t/** True when no socket connection is registered, i.e. records must fall back to the stdio lane. */\n\tisEmpty(): boolean {\n\t\treturn this.connections.size === 0;\n\t}\n\n\tbroadcast(line: string): void {\n\t\tfor (const { actor } of this.connections.values()) actor.enqueue(line);\n\t}\n\n\trememberSnapshot(sessionId: string, value: Record<string, unknown>, line: string): void {\n\t\tconst event = value.assistantMessageEvent as Record<string, unknown> | undefined;\n\t\tconst record = { line, rendered: value[RENDERED_COMPONENT_RECORD] === true };\n\t\tif (value.type === \"message_start\" || (value.type === \"message_update\" && event?.type === \"text_start\")) {\n\t\t\tthis.sessionSnapshots.set(sessionId, [record]);\n\t\t} else if (this.sessionSnapshots.has(sessionId)) {\n\t\t\tthis.sessionSnapshots.get(sessionId)?.push(record);\n\t\t}\n\t\tif (value.type === \"message_end\") this.sessionSnapshots.delete(sessionId);\n\t}\n\n\tforgetSession(sessionId: string): void {\n\t\tthis.sessionSnapshots.delete(sessionId);\n\t}\n\n\tprivate replaySnapshot(id: string, sessionId: string): void {\n\t\tconst actor = this.connections.get(id)?.actor;\n\t\tif (!actor) return;\n\t\tconst capable = this.connectionCapabilities.get(id)?.has(\"rendered_components\") ?? false;\n\t\tfor (const record of this.sessionSnapshots.get(sessionId) ?? [])\n\t\t\tif (!record.rendered || capable) actor.enqueue(record.line);\n\t}\n\n\tprivate replayRendered(id: string, sessionId: string): void {\n\t\tconst actor = this.connections.get(id)?.actor;\n\t\tif (!actor) return;\n\t\tfor (const record of this.sessionSnapshots.get(sessionId) ?? []) if (record.rendered) actor.enqueue(record.line);\n\t}\n}\n"]}
@@ -1,3 +1,5 @@
1
+ import { type SessionEventWriterConnection } from "./session-event-fanout.ts";
2
+ export { RENDERED_COMPONENT_RECORD, type SessionEventWriterConnection } from "./session-event-fanout.ts";
1
3
  type RawWriter = (chunk: string) => void;
2
4
  type BackpressureWaiter = () => Promise<void>;
3
5
  type FlushScheduler = (flush: () => Promise<void>) => void;
@@ -10,19 +12,10 @@ type FlushScheduler = (flush: () => Promise<void>) => void;
10
12
  * by itself: coalescing records from different sessions would obscure the
11
13
  * scheduling boundary and violate D9.
12
14
  */
13
- export interface SessionEventWriterConnection {
14
- readonly writeRaw: RawWriter;
15
- readonly waitForBackpressure: BackpressureWaiter;
16
- }
17
- export declare const RENDERED_COMPONENT_RECORD = "__senpiRenderedComponent";
18
15
  export declare class SessionEventWriter {
19
16
  private readonly queues;
20
- private readonly connections;
21
- private readonly sessionSnapshots;
17
+ private readonly fanout;
22
18
  private readonly connectionContext;
23
- private readonly connectionCapabilities;
24
- private readonly connectionSessions;
25
- private readonly registeredCapabilityConnections;
26
19
  private readonly controlQueue;
27
20
  private readonly readyQueues;
28
21
  private readonly sealedSessions;
@@ -50,7 +43,7 @@ export declare class SessionEventWriter {
50
43
  withConnection<T>(id: string, task: () => T): T;
51
44
  /** Connection id of the command currently being dispatched, when one owns it. */
52
45
  currentConnection(): string | undefined;
53
- /** Queue a session record. Lifecycle/events are broadcast; responses/UI are targeted. */
46
+ /** Queue a session record. Content events target connections attached to the session. */
54
47
  enqueue(sessionId: string, value: object): boolean;
55
48
  /** Queue one untagged host-control response for the current connection. */
56
49
  enqueueControl(value: object): Promise<void>;
@@ -60,6 +53,8 @@ export declare class SessionEventWriter {
60
53
  * session's final stdout record.
61
54
  */
62
55
  closeSession(sessionId: string, response: object): void;
56
+ /** Queue a successful response for a joined close after the terminal lifecycle record. */
57
+ enqueueClosedResponse(sessionId: string, response: object): void;
63
58
  /**
64
59
  * Drops per-session bookkeeping for a handle whose runtime is fully disposed.
65
60
  * Routing handles are unique per process epoch, so nothing can legitimately
@@ -71,7 +66,6 @@ export declare class SessionEventWriter {
71
66
  flush(): Promise<void>;
72
67
  private drainUntilEmpty;
73
68
  private drainReadyQueues;
74
- private rememberSnapshot;
75
69
  private connectionQueue;
76
70
  private appendSessionRecord;
77
71
  private demoteAndMerge;
@@ -82,5 +76,4 @@ export declare class SessionEventWriter {
82
76
  private fail;
83
77
  private allQueues;
84
78
  }
85
- export {};
86
79
  //# sourceMappingURL=session-event-writer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-event-writer.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-event-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-event-writer.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-event-writer.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { serializeJsonLine } from "./jsonl.js";
3
- import { SocketEventSinkActor } from "./socket-event-fanout.js";
3
+ import { RENDERED_COMPONENT_RECORD, SessionEventFanout, } from "./session-event-fanout.js";
4
+ export { RENDERED_COMPONENT_RECORD } from "./session-event-fanout.js";
4
5
  const MESSAGE_KEY = "message";
5
6
  const COMPACT_DELTA_TYPES = new Set(["text_delta", "thinking_delta", "toolcall_delta"]);
6
7
  function compactDelta(value) {
@@ -27,16 +28,20 @@ function toolUpdateKey(value) {
27
28
  ? `tool:${value.toolCallId}`
28
29
  : undefined;
29
30
  }
30
- export const RENDERED_COMPONENT_RECORD = "__senpiRenderedComponent";
31
+ /**
32
+ * Process-wide stdout scheduler for multi-session RPC mode.
33
+ *
34
+ * Each queue contains complete structured JSONL records for one routing handle.
35
+ * Draining takes one record per queue in round-robin order and waits for stdout
36
+ * backpressure before selecting the next one. A record is deliberately written
37
+ * by itself: coalescing records from different sessions would obscure the
38
+ * scheduling boundary and violate D9.
39
+ */
31
40
  export class SessionEventWriter {
32
41
  constructor(writeRaw, waitOrSchedule, scheduleFlush) {
33
42
  this.queues = new Map();
34
- this.connections = new Map();
35
- this.sessionSnapshots = new Map();
43
+ this.fanout = new SessionEventFanout();
36
44
  this.connectionContext = new AsyncLocalStorage();
37
- this.connectionCapabilities = new Map();
38
- this.connectionSessions = new Map();
39
- this.registeredCapabilityConnections = new Set();
40
45
  this.controlQueue = { latestByKey: new Map(), ready: false };
41
46
  this.readyQueues = [];
42
47
  this.sealedSessions = new Set();
@@ -68,78 +73,31 @@ export class SessionEventWriter {
68
73
  return bytes;
69
74
  }
70
75
  registerConnection(id, connection) {
71
- const actor = new SocketEventSinkActor(connection, () => {
72
- if (this.connections.get(id)?.actor === actor) {
73
- this.connections.delete(id);
74
- this.connectionCapabilities.delete(id);
75
- this.connectionSessions.delete(id);
76
- this.registeredCapabilityConnections.delete(id);
77
- }
78
- });
79
- this.connections.set(id, { connection, actor });
80
- this.connectionCapabilities.set(id, new Set());
81
- this.connectionSessions.set(id, new Set());
82
- for (const snapshot of this.sessionSnapshots.values())
83
- for (const record of snapshot)
84
- if (!record.rendered)
85
- actor.enqueue(record.line);
76
+ this.fanout.registerConnection(id, connection);
86
77
  }
87
78
  unregisterConnection(id) {
88
- const registered = this.connections.get(id);
89
- if (!registered)
90
- return;
91
- registered.actor.close();
92
- this.connections.delete(id);
93
- this.connectionCapabilities.delete(id);
94
- this.connectionSessions.delete(id);
95
- this.registeredCapabilityConnections.delete(id);
79
+ this.fanout.unregisterConnection(id);
96
80
  }
97
81
  attachConnectionToSession(id, sessionId) {
98
- if (!this.connections.has(id))
99
- return;
100
- const sessions = this.connectionSessions.get(id) ?? new Set();
101
- sessions.add(sessionId);
102
- this.connectionSessions.set(id, sessions);
103
- if (this.connectionCapabilities.get(id)?.has("rendered_components"))
104
- for (const record of this.sessionSnapshots.get(sessionId) ?? [])
105
- if (record.rendered)
106
- this.connections.get(id).actor.enqueue(record.line);
82
+ this.fanout.attachConnectionToSession(id, sessionId);
107
83
  }
108
84
  detachConnectionFromSession(id, sessionId) {
109
- this.connectionSessions.get(id)?.delete(sessionId);
85
+ this.fanout.detachConnectionFromSession(id, sessionId);
110
86
  }
111
87
  setConnectionCapabilities(id, capabilities) {
112
- const registered = this.connections.get(id);
113
- if (!registered)
114
- return;
115
- const wasCapable = this.connectionCapabilities.get(id)?.has("rendered_components") ?? false;
116
- this.connectionCapabilities.set(id, new Set(capabilities));
117
- this.registeredCapabilityConnections.add(id);
118
- if (!wasCapable && capabilities.includes("rendered_components"))
119
- for (const sessionId of this.connectionSessions.get(id) ?? [])
120
- for (const record of this.sessionSnapshots.get(sessionId) ?? [])
121
- if (record.rendered)
122
- registered.actor.enqueue(record.line);
88
+ this.fanout.setConnectionCapabilities(id, capabilities);
123
89
  }
124
90
  clearConnectionCapabilities(id) {
125
- if (this.connections.has(id)) {
126
- this.connectionCapabilities.set(id, new Set());
127
- this.registeredCapabilityConnections.delete(id);
128
- }
91
+ this.fanout.clearConnectionCapabilities(id);
129
92
  }
130
93
  hasRegisteredConnectionCapabilities(id) {
131
- return this.registeredCapabilityConnections.has(id);
94
+ return this.fanout.hasRegisteredConnectionCapabilities(id);
132
95
  }
133
96
  getConnectionCapabilities(id) {
134
- if (!this.registeredCapabilityConnections.has(id))
135
- return undefined;
136
- return [...(this.connectionCapabilities.get(id) ?? [])];
97
+ return this.fanout.getConnectionCapabilities(id);
137
98
  }
138
99
  hasCapableConnection(sessionId) {
139
- for (const [id, capabilities] of this.connectionCapabilities)
140
- if (capabilities.has("rendered_components") && this.connectionSessions.get(id)?.has(sessionId))
141
- return true;
142
- return false;
100
+ return this.fanout.hasCapableConnection(sessionId);
143
101
  }
144
102
  /** Execute a connection's command with its response destination in context. */
145
103
  withConnection(id, task) {
@@ -149,7 +107,7 @@ export class SessionEventWriter {
149
107
  currentConnection() {
150
108
  return this.connectionContext.getStore();
151
109
  }
152
- /** Queue a session record. Lifecycle/events are broadcast; responses/UI are targeted. */
110
+ /** Queue a session record. Content events target connections attached to the session. */
153
111
  enqueue(sessionId, value) {
154
112
  if (this.sealedSessions.has(sessionId))
155
113
  return false;
@@ -162,19 +120,13 @@ export class SessionEventWriter {
162
120
  const tagged = { ...value, sessionId };
163
121
  const { [RENDERED_COMPONENT_RECORD]: _rendered, ...wireTagged } = tagged;
164
122
  const line = serializeJsonLine(wireTagged);
165
- this.rememberSnapshot(sessionId, tagged, line);
166
- const targets = isTargeted
167
- ? [targetId]
168
- : record[RENDERED_COMPONENT_RECORD] && this.connections.size > 0
169
- ? [...this.connections.keys()].filter((id) => this.connectionCapabilities.get(id)?.has("rendered_components") &&
170
- this.connectionSessions.get(id)?.has(sessionId))
171
- : this.connections.size > 0
172
- ? [...this.connections.keys()]
173
- : [undefined];
123
+ if (!isTargeted)
124
+ this.fanout.rememberSnapshot(sessionId, tagged, line);
125
+ const targets = this.fanout.targets(sessionId, targetId, isTargeted, record[RENDERED_COMPONENT_RECORD] === true, record.type);
174
126
  for (const target of targets) {
175
- if (target !== undefined && !this.connections.has(target))
127
+ if (target !== undefined && !this.fanout.get(target))
176
128
  continue;
177
- const registered = target === undefined ? undefined : this.connections.get(target);
129
+ const registered = target === undefined ? undefined : this.fanout.get(target);
178
130
  if (registered)
179
131
  registered.actor.enqueue(line, compactDelta(tagged) && tagged.message !== null ? MESSAGE_KEY : undefined);
180
132
  else
@@ -188,7 +140,7 @@ export class SessionEventWriter {
188
140
  if (this.failure !== undefined)
189
141
  return Promise.reject(this.failure);
190
142
  const targetId = this.connectionContext.getStore();
191
- const registered = targetId === undefined ? undefined : this.connections.get(targetId);
143
+ const registered = targetId === undefined ? undefined : this.fanout.get(targetId);
192
144
  if (registered) {
193
145
  registered.actor.enqueue(serializeJsonLine(value));
194
146
  return Promise.resolve();
@@ -212,11 +164,23 @@ export class SessionEventWriter {
212
164
  this.sealedSessions.add(sessionId);
213
165
  const targetId = this.connectionContext.getStore();
214
166
  const lifecycle = { type: "session_closed", sessionId };
215
- for (const connectionId of this.connections.keys()) {
216
- this.connections.get(connectionId).actor.enqueue(serializeJsonLine(lifecycle));
217
- }
167
+ if (this.fanout.isEmpty())
168
+ this.appendSessionRecord(sessionId, lifecycle);
169
+ else
170
+ this.fanout.broadcast(serializeJsonLine(lifecycle));
218
171
  const taggedResponse = { ...response, sessionId };
219
- const registered = targetId === undefined ? undefined : this.connections.get(targetId);
172
+ const registered = targetId === undefined ? undefined : this.fanout.get(targetId);
173
+ if (registered)
174
+ registered.actor.enqueue(serializeJsonLine(taggedResponse));
175
+ else
176
+ this.appendSessionRecord(sessionId, taggedResponse, targetId);
177
+ this.requestFlush();
178
+ }
179
+ /** Queue a successful response for a joined close after the terminal lifecycle record. */
180
+ enqueueClosedResponse(sessionId, response) {
181
+ const targetId = this.connectionContext.getStore();
182
+ const taggedResponse = { ...response, sessionId };
183
+ const registered = targetId === undefined ? undefined : this.fanout.get(targetId);
220
184
  if (registered)
221
185
  registered.actor.enqueue(serializeJsonLine(taggedResponse));
222
186
  else
@@ -231,7 +195,7 @@ export class SessionEventWriter {
231
195
  */
232
196
  forgetSession(sessionId) {
233
197
  this.sealedSessions.delete(sessionId);
234
- this.sessionSnapshots.delete(sessionId);
198
+ this.fanout.forgetSession(sessionId);
235
199
  }
236
200
  /** Drain every retained lane and the current in-flight record. */
237
201
  flush() {
@@ -241,7 +205,7 @@ export class SessionEventWriter {
241
205
  if (this.drainPromise)
242
206
  return this.drainPromise;
243
207
  if (this.readyQueues.length === 0)
244
- return Promise.all([...this.connections.values()].map(({ actor }) => actor.flush())).then(() => undefined);
208
+ return Promise.all([...this.fanout.values()].map(({ actor }) => actor.flush())).then(() => undefined);
245
209
  let resolveDrain;
246
210
  let rejectDrain;
247
211
  const drain = new Promise((resolve, reject) => {
@@ -266,7 +230,7 @@ export class SessionEventWriter {
266
230
  do {
267
231
  await this.drainReadyQueues();
268
232
  } while (this.readyQueues.length > 0);
269
- await Promise.all([...this.connections.values()].map(({ actor }) => actor.flush()));
233
+ await Promise.all([...this.fanout.values()].map(({ actor }) => actor.flush()));
270
234
  }
271
235
  async drainReadyQueues() {
272
236
  while (this.readyQueues.length > 0) {
@@ -280,7 +244,7 @@ export class SessionEventWriter {
280
244
  try {
281
245
  // D9: exactly one complete record per raw write. The next lane is not
282
246
  // selected until this record has cleared stdout backpressure.
283
- const connection = queue.targetId ? this.connections.get(queue.targetId)?.connection : undefined;
247
+ const connection = queue.targetId ? this.fanout.get(queue.targetId)?.connection : undefined;
284
248
  const writeRaw = connection?.writeRaw ?? this.writeRaw;
285
249
  const waitForBackpressure = connection?.waitForBackpressure ?? this.waitForBackpressure;
286
250
  if (!connection && queue.targetId) {
@@ -311,18 +275,6 @@ export class SessionEventWriter {
311
275
  }
312
276
  }
313
277
  }
314
- rememberSnapshot(sessionId, value, line) {
315
- const event = value.assistantMessageEvent;
316
- const record = { line, rendered: value[RENDERED_COMPONENT_RECORD] === true };
317
- if (value.type === "message_start" || (value.type === "message_update" && event?.type === "text_start")) {
318
- this.sessionSnapshots.set(sessionId, [record]);
319
- }
320
- else if (this.sessionSnapshots.has(sessionId)) {
321
- this.sessionSnapshots.get(sessionId).push(record);
322
- }
323
- if (value.type === "message_end")
324
- this.sessionSnapshots.delete(sessionId);
325
- }
326
278
  connectionQueue(targetId) {
327
279
  const key = `connection:${targetId}`;
328
280
  let queue = this.queues.get(key);