pipane 0.1.5 → 0.1.7

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 (138) hide show
  1. package/README.md +45 -1
  2. package/THIRD_PARTY_NOTICES.md +29 -0
  3. package/bin/pipane-rendezvous.js +21 -0
  4. package/bin/pipane.js +21 -1
  5. package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  6. package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  7. package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  8. package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  9. package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  10. package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  11. package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  12. package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  13. package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  14. package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  15. package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  16. package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  17. package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  18. package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  19. package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  20. package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  21. package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  22. package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  23. package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  24. package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  25. package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  26. package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  27. package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  28. package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  29. package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  30. package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  31. package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  32. package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  33. package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  34. package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  35. package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  36. package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  37. package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  38. package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  39. package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  40. package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  41. package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  42. package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  43. package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  44. package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  45. package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  46. package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  47. package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  48. package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  49. package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  50. package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  51. package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  52. package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  53. package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  54. package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  55. package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  56. package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  57. package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  58. package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  59. package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  60. package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  61. package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  62. package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  63. package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  64. package/dist/client/assets/__vite-browser-external-DtLbhLGx.js +1 -0
  65. package/dist/client/assets/app-runtime-Rw-O-AwW.js +1 -0
  66. package/dist/client/assets/backend-landing-page-B7SbyIXQ.js +1 -0
  67. package/dist/client/assets/index-DwbBcYUf.js +2 -0
  68. package/dist/client/assets/index-iblQYBAl.css +1 -0
  69. package/dist/client/assets/main-LGItV9Aj.js +2807 -0
  70. package/dist/client/assets/pairing-page-D-WxlWZR.js +1 -0
  71. package/dist/client/assets/remote-backend-manager-BWuGh30I.js +1 -0
  72. package/dist/client/assets/rendezvous-trust-api-C2Zob5i4.js +4 -0
  73. package/dist/client/assets/webrtc-frame-transport-BijNnA7N.js +1 -0
  74. package/dist/client/assets/ws-agent-adapter-BRysf-Y7.js +6 -0
  75. package/dist/client/index.html +2 -2
  76. package/dist/server/rendezvous/rendezvous-hub.js +426 -0
  77. package/dist/server/rendezvous/server.js +247 -0
  78. package/dist/server/rendezvous/trust-store.js +432 -0
  79. package/dist/server/server/auth-guard.js +61 -0
  80. package/dist/server/server/backend-connection-authorizer.js +29 -0
  81. package/dist/server/server/backend-identity.js +167 -0
  82. package/dist/server/server/backend-protocol-handler.js +132 -0
  83. package/dist/server/server/backend-trust-store.js +157 -0
  84. package/dist/server/server/backend-webrtc.js +239 -0
  85. package/dist/server/server/bundled-extensions.js +6 -0
  86. package/dist/server/server/conversation-file-access.js +97 -0
  87. package/dist/server/server/extension-status.js +31 -0
  88. package/dist/server/server/frame-connection.js +48 -0
  89. package/dist/server/server/frame-router.js +45 -0
  90. package/dist/server/server/ice-servers.js +24 -0
  91. package/dist/server/server/local-backend-api.js +389 -0
  92. package/dist/server/server/local-settings.js +28 -0
  93. package/dist/server/server/pi-rpc-protocol.js +407 -0
  94. package/dist/server/server/pi-runtime.js +1 -1
  95. package/dist/server/server/process-pool.js +179 -136
  96. package/dist/server/server/rendezvous-client.js +282 -0
  97. package/dist/server/server/rest-api.js +121 -231
  98. package/dist/server/server/server.js +188 -134
  99. package/dist/server/server/session-actor.js +202 -0
  100. package/dist/server/server/session-index.js +111 -18
  101. package/dist/server/server/session-jsonl.js +128 -8
  102. package/dist/server/server/session-path.js +145 -0
  103. package/dist/server/server/session-registry.js +67 -0
  104. package/dist/server/server/update-api.js +28 -0
  105. package/dist/server/server/update-check.js +33 -13
  106. package/dist/server/server/update-manager.js +231 -0
  107. package/dist/server/server/worktree-name.js +124 -0
  108. package/dist/server/server/ws-handler.js +784 -478
  109. package/dist/server/shared/backend-api.js +1 -0
  110. package/dist/server/shared/backend-protocol.js +164 -0
  111. package/dist/server/shared/jsonl-sync.js +41 -0
  112. package/dist/server/shared/node-trust-crypto.js +61 -0
  113. package/dist/server/shared/rendezvous-protocol.js +243 -0
  114. package/dist/server/shared/rpc-timeouts.js +4 -0
  115. package/dist/server/shared/thinking-levels.js +94 -0
  116. package/dist/server/shared/tool-runtime.js +1 -0
  117. package/dist/server/shared/trust-protocol.js +97 -0
  118. package/dist/server/shared/updates.js +4 -0
  119. package/dist/server/shared/ws-protocol.js +473 -0
  120. package/docs/protocol.md +127 -0
  121. package/extensions/canvas.ts +1 -1
  122. package/package.json +42 -17
  123. package/dist/client/assets/_node-stub_node_crypto-C_7epg3G.js +0 -1
  124. package/dist/client/assets/_node-stub_node_fs-B-VOzeXW.js +0 -1
  125. package/dist/client/assets/_node-stub_node_http-CROXaVGJ.js +0 -1
  126. package/dist/client/assets/_node-stub_node_os-TTKJha15.js +0 -1
  127. package/dist/client/assets/_node-stub_node_path-DJCJiKv7.js +0 -1
  128. package/dist/client/assets/_node-stub_stream-DDexIdn_.js +0 -1
  129. package/dist/client/assets/index-BtDAXAo1.css +0 -1
  130. package/dist/client/assets/index-CAQqlK61.js +0 -131
  131. package/dist/client/assets/index-CJEeBmBj.js +0 -3
  132. package/dist/client/assets/index-DUEhKmPQ.js +0 -1
  133. package/dist/client/assets/index-XEVKscP-.js +0 -4443
  134. package/dist/client/assets/index-woI4agCy.js +0 -45
  135. package/dist/server/server/attached-session.js +0 -209
  136. package/dist/server/server/load-trace-store.js +0 -48
  137. package/dist/server/server/session-lifecycle.js +0 -155
  138. package/patches/@mariozechner+pi-web-ui+0.55.3.patch +0 -3437
@@ -1,209 +0,0 @@
1
- /**
2
- * Attached session state — only exists while a pi process is running a turn.
3
- *
4
- * This is the in-memory state for sessions that have a pi process attached.
5
- * It accumulates streaming events (message_start, message_update, message_end,
6
- * tool_execution_start/end) into a canonical state.
7
- *
8
- * When the turn ends (agent_end), this object is discarded and the final
9
- * authoritative state is read from the JSONL file on disk.
10
- *
11
- * Detached sessions have NO in-memory state — they are read from disk on demand.
12
- */
13
- import { existsSync, readFileSync, statSync } from "node:fs";
14
- import { parseSessionEntries, buildSessionContext, } from "@mariozechner/pi-coding-agent";
15
- // ── Attached session ───────────────────────────────────────────────────────
16
- export class AttachedSession {
17
- messages;
18
- streamMessage = null;
19
- pendingToolCalls = [];
20
- /** Partial tool results from tool_execution_update, keyed by toolCallId */
21
- partialToolResults = {};
22
- model;
23
- thinkingLevel;
24
- steeringQueue = [];
25
- error;
26
- /** Monotonically increasing version for change detection */
27
- _version = 1;
28
- /** Whether the stream message changed since last push */
29
- _streamDirty = false;
30
- constructor(init) {
31
- this.messages = init.messages;
32
- this.model = init.model;
33
- this.thinkingLevel = init.thinkingLevel;
34
- }
35
- get version() { return this._version; }
36
- /**
37
- * Apply a streaming event from the pi process.
38
- * Returns true if state changed.
39
- */
40
- applyEvent(event) {
41
- switch (event.type) {
42
- case "agent_start":
43
- this.error = undefined;
44
- this.streamMessage = null;
45
- this._version++;
46
- return true;
47
- case "message_start":
48
- this.streamMessage = event.message;
49
- this._streamDirty = true;
50
- this._version++;
51
- return true;
52
- case "message_update":
53
- this.streamMessage = event.message;
54
- this._streamDirty = true;
55
- this._version++;
56
- return true;
57
- case "message_end": {
58
- this.streamMessage = null;
59
- const msg = event.message;
60
- if (msg) {
61
- this.messages = [...this.messages, msg];
62
- }
63
- this._version++;
64
- return true;
65
- }
66
- case "turn_end": {
67
- if (event.message?.role === "assistant" && event.message?.errorMessage) {
68
- this.error = event.message.errorMessage;
69
- this._version++;
70
- return true;
71
- }
72
- return false;
73
- }
74
- case "tool_execution_start": {
75
- const toolCallId = event.toolCallId;
76
- if (toolCallId && !this.pendingToolCalls.includes(toolCallId)) {
77
- this.pendingToolCalls = [...this.pendingToolCalls, toolCallId];
78
- this._streamDirty = true;
79
- this._version++;
80
- return true;
81
- }
82
- return false;
83
- }
84
- case "tool_execution_update": {
85
- const toolCallId = event.toolCallId;
86
- const partialResult = event.partialResult;
87
- if (toolCallId && partialResult) {
88
- this.partialToolResults = { ...this.partialToolResults, [toolCallId]: partialResult };
89
- this._streamDirty = true;
90
- this._version++;
91
- return true;
92
- }
93
- return false;
94
- }
95
- case "tool_execution_end": {
96
- const toolCallId = event.toolCallId;
97
- if (toolCallId) {
98
- this.pendingToolCalls = this.pendingToolCalls.filter(id => id !== toolCallId);
99
- // Clear partial result for this tool
100
- if (toolCallId in this.partialToolResults) {
101
- const { [toolCallId]: _, ...rest } = this.partialToolResults;
102
- this.partialToolResults = rest;
103
- }
104
- this._streamDirty = true;
105
- this._version++;
106
- return true;
107
- }
108
- return false;
109
- }
110
- default:
111
- return false;
112
- }
113
- }
114
- /**
115
- * Build a full snapshot of the current state.
116
- */
117
- toSnapshot() {
118
- return {
119
- messages: this.messages,
120
- streamMessage: this.streamMessage,
121
- status: "streaming",
122
- pendingToolCalls: this.pendingToolCalls,
123
- partialToolResults: this.partialToolResults,
124
- model: this.model,
125
- thinkingLevel: this.thinkingLevel,
126
- steeringQueue: this.steeringQueue,
127
- error: this.error,
128
- };
129
- }
130
- /**
131
- * Compute the cheapest update op to send to a client.
132
- *
133
- * If only streamMessage/pendingToolCalls changed (the high-frequency case
134
- * during streaming), returns a lightweight stream_delta.
135
- * Otherwise returns a full snapshot.
136
- *
137
- * Returns null if nothing changed since the client's last known version.
138
- */
139
- computeUpdateOp(clientVersion, clientMessageCount) {
140
- if (this._version === clientVersion)
141
- return null;
142
- // If client has never seen this session, or messages changed, send snapshot
143
- if (clientVersion === 0
144
- || this.messages.length !== clientMessageCount
145
- || this.messages.length < clientMessageCount) {
146
- this._streamDirty = false;
147
- return { op: "snapshot", state: this.toSnapshot() };
148
- }
149
- // Messages didn't change — only stream state changed
150
- if (this._streamDirty) {
151
- this._streamDirty = false;
152
- return {
153
- op: "stream_delta",
154
- streamMessage: this.streamMessage,
155
- pendingToolCalls: this.pendingToolCalls,
156
- partialToolResults: this.partialToolResults,
157
- };
158
- }
159
- // Something else changed (error, steering queue, etc.) — send snapshot
160
- return { op: "snapshot", state: this.toSnapshot() };
161
- }
162
- }
163
- // ── Disk reader ────────────────────────────────────────────────────────────
164
- /**
165
- * Read a session from disk and return a snapshot.
166
- * Used for detached sessions (no pi process running).
167
- */
168
- export function readSessionFromDisk(sessionPath) {
169
- let messages = [];
170
- let model = null;
171
- let thinkingLevel = "off";
172
- try {
173
- if (existsSync(sessionPath)) {
174
- const content = readFileSync(sessionPath, "utf8");
175
- const entries = parseSessionEntries(content);
176
- const context = buildSessionContext(entries);
177
- messages = context.messages ?? [];
178
- model = context.model ?? null;
179
- thinkingLevel = context.thinkingLevel ?? "off";
180
- }
181
- }
182
- catch (err) {
183
- console.error(`[session] Failed to read session ${sessionPath}:`, err);
184
- }
185
- return {
186
- messages,
187
- streamMessage: null,
188
- status: "idle",
189
- pendingToolCalls: [],
190
- partialToolResults: {},
191
- model,
192
- thinkingLevel,
193
- steeringQueue: [],
194
- };
195
- }
196
- /**
197
- * Get the file size of a session file. Returns 0 if the file doesn't exist.
198
- */
199
- export function getSessionFileSize(sessionPath) {
200
- try {
201
- if (existsSync(sessionPath)) {
202
- return statSync(sessionPath).size;
203
- }
204
- }
205
- catch {
206
- // ignore
207
- }
208
- return 0;
209
- }
@@ -1,48 +0,0 @@
1
- const MAX_TRACES = 50;
2
- const MAX_EVENTS_PER_TRACE = 1000;
3
- export class LoadTraceStore {
4
- traces = new Map();
5
- record(traceId, event) {
6
- if (!traceId)
7
- return;
8
- const trace = this.ensure(traceId);
9
- trace.events.push(event);
10
- if (trace.events.length > MAX_EVENTS_PER_TRACE) {
11
- trace.events.splice(0, trace.events.length - MAX_EVENTS_PER_TRACE);
12
- }
13
- trace.updatedAt = event.ts;
14
- }
15
- get(traceId) {
16
- return this.traces.get(traceId);
17
- }
18
- getLatest() {
19
- return Array.from(this.traces.values())
20
- .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
21
- .slice(0, 10);
22
- }
23
- ensure(traceId) {
24
- let trace = this.traces.get(traceId);
25
- if (!trace) {
26
- const now = new Date().toISOString();
27
- trace = {
28
- traceId,
29
- startedAt: now,
30
- updatedAt: now,
31
- events: [],
32
- };
33
- this.traces.set(traceId, trace);
34
- this.compact();
35
- }
36
- return trace;
37
- }
38
- compact() {
39
- if (this.traces.size <= MAX_TRACES)
40
- return;
41
- const oldest = Array.from(this.traces.values())
42
- .sort((a, b) => a.updatedAt.localeCompare(b.updatedAt))
43
- .slice(0, this.traces.size - MAX_TRACES);
44
- for (const trace of oldest) {
45
- this.traces.delete(trace.traceId);
46
- }
47
- }
48
- }
@@ -1,155 +0,0 @@
1
- /**
2
- * Session lifecycle state machine.
3
- *
4
- * Single source of truth for session→process mappings, session status,
5
- * and steering queues. Enforces legal state transitions and emits typed events.
6
- *
7
- * States per session:
8
- * (absent) → session has never been tracked
9
- * "running" → a pi process is attached and executing a turn
10
- * "done" → pi process was attached at some point and has since detached
11
- *
12
- * Transitions:
13
- * attach(sessionPath, proc) → sets "running"
14
- * detach(sessionPath) → sets "done", clears proc mapping
15
- * crash(sessionPath) → sets "done", clears proc mapping (for unexpected exits)
16
- */
17
- export class SessionLifecycle {
18
- /** session path → attached process */
19
- attached = new Map();
20
- /** session path → "running" | "done" (persists for server lifetime) */
21
- statuses = new Map();
22
- /** session path → queued steering messages */
23
- steeringQueues = new Map();
24
- listeners = new Set();
25
- // ── Event subscription ───────────────────────────────────────────────
26
- subscribe(fn) {
27
- this.listeners.add(fn);
28
- return () => this.listeners.delete(fn);
29
- }
30
- emit(event) {
31
- for (const fn of this.listeners)
32
- fn(event);
33
- }
34
- // ── State queries ────────────────────────────────────────────────────
35
- getAttachedProcess(sessionPath) {
36
- return this.attached.get(sessionPath);
37
- }
38
- getAllStatuses() {
39
- const result = {};
40
- for (const [k, v] of this.statuses)
41
- result[k] = v;
42
- return result;
43
- }
44
- getAttachedSessionForProcess(proc) {
45
- for (const [sessionPath, p] of this.attached) {
46
- if (p === proc)
47
- return sessionPath;
48
- }
49
- return undefined;
50
- }
51
- get attachedCount() {
52
- return this.attached.size;
53
- }
54
- // ── Transitions ──────────────────────────────────────────────────────
55
- /**
56
- * Attach a process to a session. Sets status to "running".
57
- * If the session is already attached, returns the existing process (idempotent).
58
- */
59
- attach(sessionPath, proc) {
60
- const existing = this.attached.get(sessionPath);
61
- if (existing) {
62
- return existing;
63
- }
64
- this.attached.set(sessionPath, proc);
65
- this.statuses.set(sessionPath, "running");
66
- this.emit({ type: "status_change", sessionPath, status: "running" });
67
- this.emit({ type: "session_attached", sessionPath, procId: proc.id });
68
- return proc;
69
- }
70
- /**
71
- * Detach a process from a session (normal completion). Sets status to "done".
72
- * Returns the process that was detached, or undefined if not attached.
73
- */
74
- detach(sessionPath) {
75
- const proc = this.attached.get(sessionPath);
76
- if (!proc) {
77
- console.warn(`[lifecycle] detach called for non-attached session: ${sessionPath}`);
78
- return undefined;
79
- }
80
- this.attached.delete(sessionPath);
81
- this.statuses.set(sessionPath, "done");
82
- // Clear steering queue on detach
83
- this.steeringQueues.delete(sessionPath);
84
- this.emit({ type: "steering_queue_update", sessionPath, queue: [] });
85
- this.emit({ type: "status_change", sessionPath, status: "done" });
86
- this.emit({ type: "session_detached", sessionPath, procId: proc.id });
87
- return proc;
88
- }
89
- /**
90
- * Handle a process crash while attached to a session.
91
- * Same as detach but named differently for clarity in call sites.
92
- */
93
- crash(sessionPath) {
94
- return this.detach(sessionPath);
95
- }
96
- // ── Steering queue management ────────────────────────────────────────
97
- getAllSteeringQueues() {
98
- const result = {};
99
- for (const [k, v] of this.steeringQueues) {
100
- if (v.length > 0)
101
- result[k] = [...v];
102
- }
103
- return result;
104
- }
105
- enqueueSteering(sessionPath, message) {
106
- let queue = this.steeringQueues.get(sessionPath);
107
- if (!queue) {
108
- queue = [];
109
- this.steeringQueues.set(sessionPath, queue);
110
- }
111
- queue.push(message);
112
- this.emit({ type: "steering_queue_update", sessionPath, queue: [...queue] });
113
- }
114
- /**
115
- * Dequeue a steering message by matching its text.
116
- * Used when the agent confirms it received a user message.
117
- */
118
- dequeueSteering(sessionPath, text) {
119
- const queue = this.steeringQueues.get(sessionPath);
120
- if (!queue)
121
- return false;
122
- const idx = queue.indexOf(text);
123
- if (idx === -1)
124
- return false;
125
- queue.splice(idx, 1);
126
- if (queue.length === 0) {
127
- this.steeringQueues.delete(sessionPath);
128
- }
129
- this.emit({ type: "steering_queue_update", sessionPath, queue: [...(this.steeringQueues.get(sessionPath) ?? [])] });
130
- return true;
131
- }
132
- /**
133
- * Remove a steering message by index.
134
- */
135
- removeSteeringByIndex(sessionPath, index) {
136
- const queue = this.steeringQueues.get(sessionPath);
137
- if (!queue || index < 0 || index >= queue.length)
138
- return false;
139
- queue.splice(index, 1);
140
- if (queue.length === 0) {
141
- this.steeringQueues.delete(sessionPath);
142
- }
143
- this.emit({ type: "steering_queue_update", sessionPath, queue: [...(this.steeringQueues.get(sessionPath) ?? [])] });
144
- return true;
145
- }
146
- /**
147
- * Clear all steering messages for a session.
148
- */
149
- clearSteering(sessionPath) {
150
- if (!this.steeringQueues.has(sessionPath))
151
- return;
152
- this.steeringQueues.delete(sessionPath);
153
- this.emit({ type: "steering_queue_update", sessionPath, queue: [] });
154
- }
155
- }