u-foo 2.5.0 → 2.5.2

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/bin/uagy.js CHANGED
@@ -42,14 +42,52 @@ const previousConversationId = readPreviousConversationId(cwd, {
42
42
  tmuxPane: process.env.TMUX_PANE || "",
43
43
  });
44
44
 
45
- // In internal / unattended modes, auto-approve tool prompts so the agent
46
- // doesn't stall waiting on a y/n the operator can't see. Terminal/tmux
47
- // modes leave permissions in the user's hands (agy's default).
45
+ function extractUfooParamsFromArgs(args = []) {
46
+ const nextArgs = [];
47
+ let nickname = "";
48
+ let role = "";
49
+ for (let i = 0; i < args.length; i += 1) {
50
+ const arg = String(args[i] || "");
51
+ if (arg === "--nickname") {
52
+ if (i + 1 < args.length) {
53
+ nickname = String(args[i + 1]).trim();
54
+ i += 1;
55
+ }
56
+ continue;
57
+ }
58
+ if (arg.startsWith("--nickname=")) {
59
+ nickname = arg.slice("--nickname=".length).trim();
60
+ continue;
61
+ }
62
+ if (arg === "--role") {
63
+ if (i + 1 < args.length) {
64
+ role = String(args[i + 1]).trim();
65
+ i += 1;
66
+ }
67
+ continue;
68
+ }
69
+ if (arg.startsWith("--role=")) {
70
+ role = arg.slice("--role=".length).trim();
71
+ continue;
72
+ }
73
+ nextArgs.push(args[i]);
74
+ }
75
+ return { args: nextArgs, nickname, role };
76
+ }
77
+
78
+ const { args: cleanArgs, nickname, role } = extractUfooParamsFromArgs(process.argv.slice(2));
79
+ if (nickname) {
80
+ process.env.UFOO_NICKNAME = nickname;
81
+ }
82
+ if (role) {
83
+ process.env.UFOO_PROMPT_PROFILE = role;
84
+ }
85
+
48
86
  const launchMode = String(process.env.UFOO_LAUNCH_MODE || "").trim().toLowerCase();
49
87
  const skipPermissions = launchMode === "internal";
50
88
 
51
89
  const launchArgs = buildAgyLaunchArgs({
52
- userArgs: process.argv.slice(2),
90
+ userArgs: cleanArgs,
53
91
  previousConversationId,
54
92
  skipPermissions,
55
93
  });
package/bin/uclaude.js CHANGED
@@ -8,11 +8,52 @@
8
8
  const AgentLauncher = require("../src/agents/launch/launcher");
9
9
  const { resolveDefaultManualBootstrap } = require("../src/agents/prompts/defaultBootstrap");
10
10
 
11
+ function extractUfooParamsFromArgs(args = []) {
12
+ const nextArgs = [];
13
+ let nickname = "";
14
+ let role = "";
15
+ for (let i = 0; i < args.length; i += 1) {
16
+ const arg = String(args[i] || "");
17
+ if (arg === "--nickname") {
18
+ if (i + 1 < args.length) {
19
+ nickname = String(args[i + 1]).trim();
20
+ i += 1;
21
+ }
22
+ continue;
23
+ }
24
+ if (arg.startsWith("--nickname=")) {
25
+ nickname = arg.slice("--nickname=".length).trim();
26
+ continue;
27
+ }
28
+ if (arg === "--role") {
29
+ if (i + 1 < args.length) {
30
+ role = String(args[i + 1]).trim();
31
+ i += 1;
32
+ }
33
+ continue;
34
+ }
35
+ if (arg.startsWith("--role=")) {
36
+ role = arg.slice("--role=".length).trim();
37
+ continue;
38
+ }
39
+ nextArgs.push(args[i]);
40
+ }
41
+ return { args: nextArgs, nickname, role };
42
+ }
43
+
44
+ const { args: cleanArgs, nickname, role } = extractUfooParamsFromArgs(process.argv.slice(2));
45
+ if (nickname) {
46
+ process.env.UFOO_NICKNAME = nickname;
47
+ }
48
+ if (role) {
49
+ process.env.UFOO_PROMPT_PROFILE = role;
50
+ }
51
+
11
52
  const launcher = new AgentLauncher("claude-code", "claude");
12
53
  const resolved = resolveDefaultManualBootstrap({
13
54
  projectRoot: process.cwd(),
14
55
  agentType: "claude-code",
15
- args: process.argv.slice(2),
56
+ args: cleanArgs,
16
57
  env: process.env,
17
58
  });
18
59
 
package/bin/ucode.js CHANGED
@@ -59,8 +59,49 @@ function shouldPreserveAppendTarget({
59
59
  return fs.existsSync(append);
60
60
  }
61
61
 
62
+ function extractUfooParamsFromArgs(args = []) {
63
+ const nextArgs = [];
64
+ let nickname = "";
65
+ let role = "";
66
+ for (let i = 0; i < args.length; i += 1) {
67
+ const arg = String(args[i] || "");
68
+ if (arg === "--nickname") {
69
+ if (i + 1 < args.length) {
70
+ nickname = String(args[i + 1]).trim();
71
+ i += 1;
72
+ }
73
+ continue;
74
+ }
75
+ if (arg.startsWith("--nickname=")) {
76
+ nickname = arg.slice("--nickname=".length).trim();
77
+ continue;
78
+ }
79
+ if (arg === "--role") {
80
+ if (i + 1 < args.length) {
81
+ role = String(args[i + 1]).trim();
82
+ i += 1;
83
+ }
84
+ continue;
85
+ }
86
+ if (arg.startsWith("--role=")) {
87
+ role = arg.slice("--role=".length).trim();
88
+ continue;
89
+ }
90
+ nextArgs.push(args[i]);
91
+ }
92
+ return { args: nextArgs, nickname, role };
93
+ }
94
+
95
+ const { args: cleanArgs, nickname, role } = extractUfooParamsFromArgs(process.argv.slice(2));
96
+ if (nickname) {
97
+ process.env.UFOO_NICKNAME = nickname;
98
+ }
99
+ if (role) {
100
+ process.env.UFOO_PROMPT_PROFILE = role;
101
+ }
102
+
62
103
  const resolved = resolveUcodeLaunch({
63
- argv: process.argv.slice(2),
104
+ argv: cleanArgs,
64
105
  env: process.env,
65
106
  cwd: process.cwd(),
66
107
  });
package/bin/ucodex.js CHANGED
@@ -8,11 +8,52 @@
8
8
  const AgentLauncher = require("../src/agents/launch/launcher");
9
9
  const { resolveDefaultManualBootstrap } = require("../src/agents/prompts/defaultBootstrap");
10
10
 
11
+ function extractUfooParamsFromArgs(args = []) {
12
+ const nextArgs = [];
13
+ let nickname = "";
14
+ let role = "";
15
+ for (let i = 0; i < args.length; i += 1) {
16
+ const arg = String(args[i] || "");
17
+ if (arg === "--nickname") {
18
+ if (i + 1 < args.length) {
19
+ nickname = String(args[i + 1]).trim();
20
+ i += 1;
21
+ }
22
+ continue;
23
+ }
24
+ if (arg.startsWith("--nickname=")) {
25
+ nickname = arg.slice("--nickname=".length).trim();
26
+ continue;
27
+ }
28
+ if (arg === "--role") {
29
+ if (i + 1 < args.length) {
30
+ role = String(args[i + 1]).trim();
31
+ i += 1;
32
+ }
33
+ continue;
34
+ }
35
+ if (arg.startsWith("--role=")) {
36
+ role = arg.slice("--role=".length).trim();
37
+ continue;
38
+ }
39
+ nextArgs.push(args[i]);
40
+ }
41
+ return { args: nextArgs, nickname, role };
42
+ }
43
+
44
+ const { args: cleanArgs, nickname, role } = extractUfooParamsFromArgs(process.argv.slice(2));
45
+ if (nickname) {
46
+ process.env.UFOO_NICKNAME = nickname;
47
+ }
48
+ if (role) {
49
+ process.env.UFOO_PROMPT_PROFILE = role;
50
+ }
51
+
11
52
  const launcher = new AgentLauncher("codex", "codex");
12
53
  const resolved = resolveDefaultManualBootstrap({
13
54
  projectRoot: process.cwd(),
14
55
  agentType: "codex",
15
- args: process.argv.slice(2),
56
+ args: cleanArgs,
16
57
  env: process.env,
17
58
  });
18
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -16,6 +16,7 @@ const { redactToolCallPayload, redactSecrets } = require("../../runtime/privacy/
16
16
  const { buildCachedMemoryPrefix } = require("../../coordination/memory");
17
17
  const { normalizePublisher, shouldForwardStreamToPublisher } = require("../launch/publisherRouting");
18
18
  const { appendAgentRegistryDiagnostic } = require("../../coordination/state/agentRegistryDiagnostics");
19
+ const { DeliveryQueue } = require("../../coordination/bus/deliveryQueue");
19
20
  const {
20
21
  buildDefaultStartupBootstrapPrompt,
21
22
  isValueForCodexOption,
@@ -216,37 +217,15 @@ function shouldStreamReplyToPublisher(projectRoot, publisher, evt = {}) {
216
217
  return shouldForwardStreamToPublisher(projectRoot, publisher);
217
218
  }
218
219
 
219
- function drainQueue(queueFile) {
220
- if (!fs.existsSync(queueFile)) return [];
221
- const processingFile = `${queueFile}.processing.${process.pid}.${Date.now()}`;
222
- let content = "";
223
- let readOk = false;
224
- try {
225
- fs.renameSync(queueFile, processingFile);
226
- content = fs.readFileSync(processingFile, "utf8");
227
- readOk = true;
228
- } catch {
229
- try {
230
- if (fs.existsSync(processingFile)) {
231
- fs.renameSync(processingFile, queueFile);
232
- }
233
- } catch {
234
- // ignore rollback errors
235
- }
236
- return [];
237
- } finally {
238
- if (readOk) {
239
- try {
240
- if (fs.existsSync(processingFile)) {
241
- fs.rmSync(processingFile, { force: true });
242
- }
243
- } catch {
244
- // ignore cleanup errors
245
- }
246
- }
220
+ function claimQueuedEvents(queueFile) {
221
+ const queue = new DeliveryQueue(queueFile);
222
+ const claims = [];
223
+ while (true) {
224
+ const claim = queue.claimNext();
225
+ if (!claim) break;
226
+ claims.push({ ...claim, queue });
247
227
  }
248
- if (!content.trim()) return [];
249
- return content.split(/\r?\n/).filter(Boolean);
228
+ return claims;
250
229
  }
251
230
 
252
231
  function parseAgentViewRawInput(message) {
@@ -835,67 +814,67 @@ async function runInternalRunner({ projectRoot, agentType = "codex", extraArgs =
835
814
  if (!processing) {
836
815
  processing = true;
837
816
  try {
838
- const lines = drainQueue(queueFile);
839
- if (lines.length > 0) {
840
- const events = [];
841
- for (const line of lines) {
842
- try {
843
- events.push(JSON.parse(line));
844
- } catch {
845
- // ignore malformed line
846
- }
847
- }
817
+ const claims = claimQueuedEvents(queueFile);
818
+ if (claims.length > 0) {
819
+ let handledAny = false;
848
820
 
849
- const runnableEvents = [];
850
- for (const evt of events) {
821
+ for (const claim of claims) {
822
+ const evt = claim.event;
823
+ const runnableEvents = [];
851
824
  const rawInput = parseAgentViewRawInput(evt && evt.data ? evt.data.message : "");
852
825
  if (rawInput === null) {
853
826
  runnableEvents.push(evt);
854
- continue;
827
+ } else {
828
+ const session = getInteractiveSession(evt.publisher || "unknown");
829
+ const submissions = session.handleRaw(rawInput);
830
+ for (const message of submissions) {
831
+ runnableEvents.push({
832
+ ...evt,
833
+ __agentViewRaw: true,
834
+ data: {
835
+ ...(evt.data || {}),
836
+ message,
837
+ },
838
+ });
839
+ }
855
840
  }
856
841
 
857
- const session = getInteractiveSession(evt.publisher || "unknown");
858
- const submissions = session.handleRaw(rawInput);
859
- for (const message of submissions) {
860
- runnableEvents.push({
861
- ...evt,
862
- __agentViewRaw: true,
863
- data: {
864
- ...(evt.data || {}),
865
- message,
866
- },
867
- });
842
+ if (runnableEvents.length > 0) {
843
+ activityTracker.notifyTurnStart("thinking");
868
844
  }
869
- }
870
-
871
- if (runnableEvents.length > 0) {
872
- activityTracker.notifyTurnStart("thinking");
873
- }
874
845
 
875
- for (const evt of runnableEvents) {
876
- // eslint-disable-next-line no-await-in-loop
877
- await handleEvent(
878
- projectRoot,
879
- parsedAgentType,
880
- provider,
881
- model,
882
- subscriber,
883
- nickname,
884
- evt,
885
- busSender,
886
- bootstrap.extraArgs,
887
- threadRuntime,
888
- bootstrap.promptText,
889
- activityTracker
890
- );
891
- if (evt.__agentViewRaw) {
892
- getInteractiveSession(evt.publisher || "unknown").writeResponsePrompt();
846
+ try {
847
+ for (const runnableEvent of runnableEvents) {
848
+ // eslint-disable-next-line no-await-in-loop
849
+ await handleEvent(
850
+ projectRoot,
851
+ parsedAgentType,
852
+ provider,
853
+ model,
854
+ subscriber,
855
+ nickname,
856
+ runnableEvent,
857
+ busSender,
858
+ bootstrap.extraArgs,
859
+ threadRuntime,
860
+ bootstrap.promptText,
861
+ activityTracker
862
+ );
863
+ if (runnableEvent.__agentViewRaw) {
864
+ getInteractiveSession(runnableEvent.publisher || "unknown").writeResponsePrompt();
865
+ }
866
+ }
867
+ claim.queue.completeClaim(claim);
868
+ if (runnableEvents.length > 0) handledAny = true;
869
+ } catch (err) {
870
+ claim.queue.restoreClaim(claim);
871
+ throw err;
893
872
  }
894
873
  }
895
874
  // 处理消息后更新心跳
896
875
  updateHeartbeat();
897
876
  lastHeartbeat = now;
898
- if (runnableEvents.length > 0) {
877
+ if (handledAny) {
899
878
  activityTracker.markIdle();
900
879
  }
901
880
  await busSender.flush();
@@ -9,8 +9,8 @@ const { shakeTerminalByTty } = require("../../coordination/bus/shake");
9
9
  const { isITerm2 } = require("../../runtime/terminal/detect");
10
10
  const iterm2 = require("../../runtime/terminal/iterm2");
11
11
  const { createActivityStatePublisher } = require("../activity/activityStatePublisher");
12
- const { INJECTION_MODES, getInjectionModeFromEvent } = require("../../coordination/bus/messageMeta");
13
12
  const { buildPromptInjectionText } = require("../../coordination/bus/promptEnvelope");
13
+ const { DeliveryQueue } = require("../../coordination/bus/deliveryQueue");
14
14
 
15
15
  /**
16
16
  * Agent 消息通知监听器
@@ -41,6 +41,7 @@ class AgentNotifier {
41
41
  safeSub,
42
42
  "pending.jsonl"
43
43
  );
44
+ this.deliveryQueue = new DeliveryQueue(this.queueFile);
44
45
  this.agentsFile = paths.agentsFile;
45
46
 
46
47
  // 初始化 injector
@@ -187,45 +188,6 @@ class AgentNotifier {
187
188
  }
188
189
  }
189
190
 
190
- drainPending() {
191
- if (!fs.existsSync(this.queueFile)) return [];
192
- const processingFile = `${this.queueFile}.processing.${process.pid}.${Date.now()}`;
193
- let content = "";
194
- let readOk = false;
195
- try {
196
- fs.renameSync(this.queueFile, processingFile);
197
- content = fs.readFileSync(processingFile, "utf8");
198
- readOk = true;
199
- } catch {
200
- try {
201
- if (fs.existsSync(processingFile)) {
202
- fs.renameSync(processingFile, this.queueFile);
203
- }
204
- } catch {
205
- // ignore rollback errors
206
- }
207
- return [];
208
- } finally {
209
- if (readOk) {
210
- try {
211
- if (fs.existsSync(processingFile)) {
212
- fs.rmSync(processingFile, { force: true });
213
- }
214
- } catch {
215
- // ignore cleanup errors
216
- }
217
- }
218
- }
219
- if (!content.trim()) return [];
220
- return content.split(/\r?\n/).filter(Boolean).map((line) => {
221
- try {
222
- return JSON.parse(line);
223
- } catch {
224
- return null;
225
- }
226
- }).filter(Boolean);
227
- }
228
-
229
191
  normalizePublisher(publisher) {
230
192
  if (!publisher) return "";
231
193
  if (typeof publisher === "string") return publisher;
@@ -272,57 +234,41 @@ class AgentNotifier {
272
234
  return 0;
273
235
  }
274
236
 
275
- const events = this.drainPending();
276
- if (events.length === 0) return 0;
277
- const requeue = [];
278
- let delivered = 0;
279
- let consumedOne = false;
280
- for (const evt of events) {
281
- if (!evt || evt.event !== "message" || !evt.data || typeof evt.data.message !== "string") {
282
- continue;
283
- }
284
- const injectionMode = getInjectionModeFromEvent(evt, INJECTION_MODES.IMMEDIATE);
285
- const activityState = this.getCurrentActivityState();
286
- if (injectionMode === INJECTION_MODES.QUEUED && this.isBusyState(activityState)) {
287
- requeue.push(evt);
288
- continue;
289
- }
290
- if (consumedOne) {
291
- requeue.push(evt);
292
- continue;
293
- }
294
- const message = buildPromptInjectionText(evt, this.subscriber, this.getAgentsMap());
295
- try {
296
- // Inject the prompt-facing text into the terminal/tmux agent
297
- // (Bus is the source of truth; inject is the delivery adapter.)
298
- // eslint-disable-next-line no-await-in-loop
299
- await this.injector.inject(this.subscriber, message);
300
- delivered += 1;
301
- consumedOne = true;
302
- this.injectFailCount = 0;
303
- this.updateActivityState("working");
304
- // eslint-disable-next-line no-await-in-loop
305
- await this.emitDelivery(evt, "ok");
306
- } catch (err) {
307
- consumedOne = true;
308
- this.injectFailCount += 1;
309
- requeue.push(evt);
310
- // eslint-disable-next-line no-await-in-loop
311
- await this.emitDelivery(evt, "error", err.message || "inject failed");
312
- }
237
+ if (this.isBusyState(this.getCurrentActivityState())) {
238
+ return 0;
313
239
  }
314
- if (requeue.length > 0) {
315
- try {
316
- const content = requeue.map((e) => JSON.stringify(e)).join("\n") + "\n";
317
- fs.appendFileSync(this.queueFile, content, "utf8");
318
- } catch {
319
- // ignore requeue failures
320
- }
240
+
241
+ const claim = this.deliveryQueue.claimNext();
242
+ if (!claim) return 0;
243
+
244
+ const evt = claim.event;
245
+ if (!evt || evt.event !== "message" || !evt.data || typeof evt.data.message !== "string") {
246
+ this.deliveryQueue.completeClaim(claim);
247
+ return 0;
248
+ }
249
+
250
+ const activityState = this.getCurrentActivityState();
251
+ if (this.isBusyState(activityState)) {
252
+ this.deliveryQueue.restoreClaim(claim);
253
+ return 0;
321
254
  }
322
- if (delivered > 0) {
255
+
256
+ const message = buildPromptInjectionText(evt, this.subscriber, this.getAgentsMap());
257
+ try {
258
+ // Inject the prompt-facing text into the terminal/tmux agent.
259
+ await this.injector.inject(this.subscriber, message);
260
+ this.deliveryQueue.completeClaim(claim);
261
+ this.injectFailCount = 0;
262
+ this.updateActivityState("working");
263
+ await this.emitDelivery(evt, "ok");
323
264
  this.lastWorkingAt = Date.now();
265
+ return 1;
266
+ } catch (err) {
267
+ this.injectFailCount += 1;
268
+ this.deliveryQueue.restoreClaim(claim);
269
+ await this.emitDelivery(evt, "error", err.message || "inject failed");
270
+ return 0;
324
271
  }
325
- return delivered;
326
272
  }
327
273
 
328
274
  /**
@@ -367,32 +313,10 @@ class AgentNotifier {
367
313
  if (currentCount > this.lastCount) {
368
314
  const newCount = currentCount - this.lastCount;
369
315
  this.notify(newCount);
370
-
371
- // 自动触发终端输入(非阻塞)
372
- this.autoTriggerInput().catch(() => {
373
- // 忽略触发失败
374
- });
375
316
  }
376
317
 
377
- // Ensure pending delivery happens even if count doesn't change
378
- if (this.autoTrigger && currentCount > 0) {
379
- if (this.isUfooCodeSubscriber()) {
380
- if (this.lastUbusWakeCount !== currentCount) {
381
- try {
382
- await this.autoTriggerInput();
383
- this.lastUbusWakeCount = currentCount;
384
- } catch {
385
- // ignore delivery errors
386
- }
387
- }
388
- } else {
389
- try {
390
- await this.deliverPending();
391
- } catch {
392
- // ignore delivery errors
393
- }
394
- }
395
- }
318
+ // Delivery is owned by the project daemon scheduler. The notifier keeps
319
+ // terminal notifications, title badges, heartbeat, and activity fallback.
396
320
  if (currentCount <= 0) {
397
321
  this.lastUbusWakeCount = -1;
398
322
  }
@@ -17,6 +17,7 @@ const {
17
17
  resolveDefaultManualBootstrap,
18
18
  } = require("../prompts/defaultBootstrap");
19
19
  const { buildPromptInjectionText } = require("../../coordination/bus/promptEnvelope");
20
+ const { DeliveryQueue } = require("../../coordination/bus/deliveryQueue");
20
21
 
21
22
  function sleep(ms) {
22
23
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -40,37 +41,15 @@ function safeSubscriber(subscriber) {
40
41
  return subscriber.replace(/:/g, "_");
41
42
  }
42
43
 
43
- function drainQueue(queueFile) {
44
- if (!fs.existsSync(queueFile)) return [];
45
- const processingFile = `${queueFile}.processing.${process.pid}.${Date.now()}`;
46
- let content = "";
47
- let readOk = false;
48
- try {
49
- fs.renameSync(queueFile, processingFile);
50
- content = fs.readFileSync(processingFile, "utf8");
51
- readOk = true;
52
- } catch {
53
- try {
54
- if (fs.existsSync(processingFile)) {
55
- fs.renameSync(processingFile, queueFile);
56
- }
57
- } catch {
58
- // ignore rollback errors
59
- }
60
- return [];
61
- } finally {
62
- if (readOk) {
63
- try {
64
- if (fs.existsSync(processingFile)) {
65
- fs.rmSync(processingFile, { force: true });
66
- }
67
- } catch {
68
- // ignore cleanup errors
69
- }
70
- }
44
+ function claimQueuedEvents(queueFile) {
45
+ const queue = new DeliveryQueue(queueFile);
46
+ const claims = [];
47
+ while (true) {
48
+ const claim = queue.claimNext();
49
+ if (!claim) break;
50
+ claims.push({ ...claim, queue });
71
51
  }
72
- if (!content.trim()) return [];
73
- return content.split(/\r?\n/).filter(Boolean);
52
+ return claims;
74
53
  }
75
54
 
76
55
  function stripAnsi(text) {
@@ -1044,28 +1023,29 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
1044
1023
  lastHeartbeat = now;
1045
1024
  }
1046
1025
 
1047
- const lines = drainQueue(queueFile);
1048
- if (lines.length > 0) {
1026
+ const claims = claimQueuedEvents(queueFile);
1027
+ if (claims.length > 0) {
1049
1028
  const agents = readAgentsMap(agentsFilePath);
1050
- const events = [];
1051
- for (const line of lines) {
1029
+ for (const claim of claims) {
1030
+ const evt = claim.event;
1052
1031
  try {
1053
- events.push(JSON.parse(line));
1032
+ const input = buildPtyInputFromEvent(evt, subscriber, agents);
1033
+ if (!input) {
1034
+ claim.queue.completeClaim(claim);
1035
+ continue;
1036
+ }
1037
+ const { raw, text } = input;
1038
+ if (messageQueue.length >= maxQueue) {
1039
+ messageQueue.shift();
1040
+ }
1041
+ const publisher = typeof evt.publisher === "object" && evt.publisher
1042
+ ? (evt.publisher.subscriber || evt.publisher.nickname || "unknown")
1043
+ : (evt.publisher || "unknown");
1044
+ messageQueue.push({ publisher, raw, text });
1045
+ claim.queue.completeClaim(claim);
1054
1046
  } catch {
1055
- // ignore malformed line
1056
- }
1057
- }
1058
- for (const evt of events) {
1059
- const input = buildPtyInputFromEvent(evt, subscriber, agents);
1060
- if (!input) continue;
1061
- const { raw, text } = input;
1062
- if (messageQueue.length >= maxQueue) {
1063
- messageQueue.shift();
1047
+ claim.queue.restoreClaim(claim);
1064
1048
  }
1065
- const publisher = typeof evt.publisher === "object" && evt.publisher
1066
- ? (evt.publisher.subscriber || evt.publisher.nickname || "unknown")
1067
- : (evt.publisher || "unknown");
1068
- messageQueue.push({ publisher, raw, text });
1069
1049
  }
1070
1050
  }
1071
1051
  processQueue();