adhdev 0.8.25 → 0.8.28
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/dist/cli/index.js +2794 -720
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1240 -615
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/vendor/session-host-daemon/index.d.mts +16 -0
- package/vendor/session-host-daemon/index.d.ts +16 -0
- package/vendor/session-host-daemon/index.js +200 -4
- package/vendor/session-host-daemon/index.js.map +1 -1
- package/vendor/session-host-daemon/index.mjs +200 -4
- package/vendor/session-host-daemon/index.mjs.map +1 -1
- package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.d.mts +72 -1
- package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.d.ts +72 -1
- package/vendor/session-host-daemon/node_modules/@adhdev/session-host-core/index.js.map +1 -1
|
@@ -238,6 +238,20 @@ var PtySessionRuntime = class {
|
|
|
238
238
|
if (!this.ptyProcess) return;
|
|
239
239
|
this.ptyProcess.kill();
|
|
240
240
|
}
|
|
241
|
+
sendSignal(signal) {
|
|
242
|
+
if (!this.ptyProcess) throw new Error(`Session not running: ${this.sessionId}`);
|
|
243
|
+
const normalized = String(signal || "").trim().toUpperCase();
|
|
244
|
+
if (!normalized) throw new Error("signal is required");
|
|
245
|
+
try {
|
|
246
|
+
process.kill(this.ptyProcess.pid, normalized);
|
|
247
|
+
} catch {
|
|
248
|
+
if (normalized === "SIGTERM" || normalized === "SIGKILL") {
|
|
249
|
+
this.ptyProcess.kill();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
throw new Error(`Unsupported signal for runtime ${this.sessionId}: ${normalized}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
241
255
|
getSnapshotText() {
|
|
242
256
|
return this.screenMirror?.formatVT() || "";
|
|
243
257
|
}
|
|
@@ -306,7 +320,8 @@ var SessionHostStorage = class {
|
|
|
306
320
|
};
|
|
307
321
|
|
|
308
322
|
// src/server.ts
|
|
309
|
-
var SessionHostServer = class extends EventEmitter {
|
|
323
|
+
var SessionHostServer = class _SessionHostServer extends EventEmitter {
|
|
324
|
+
static MAX_RECENT_DIAGNOSTICS = 200;
|
|
310
325
|
endpoint;
|
|
311
326
|
registry = new SessionHostRegistry();
|
|
312
327
|
runtimes = /* @__PURE__ */ new Map();
|
|
@@ -314,6 +329,11 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
314
329
|
ipcServer = null;
|
|
315
330
|
sockets = /* @__PURE__ */ new Set();
|
|
316
331
|
persistTimers = /* @__PURE__ */ new Map();
|
|
332
|
+
startedAt = Date.now();
|
|
333
|
+
recentLogs = [];
|
|
334
|
+
recentRequests = [];
|
|
335
|
+
recentTransitions = [];
|
|
336
|
+
exitWaiters = /* @__PURE__ */ new Map();
|
|
317
337
|
constructor(options = {}) {
|
|
318
338
|
super();
|
|
319
339
|
this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || "adhdev");
|
|
@@ -341,12 +361,12 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
341
361
|
this.ipcServer?.once("error", reject);
|
|
342
362
|
this.ipcServer?.listen(this.endpoint.path);
|
|
343
363
|
});
|
|
344
|
-
this.
|
|
364
|
+
this.recordHostLog("info", `session host endpoint ready: ${this.endpoint.path}`);
|
|
345
365
|
setTimeout(() => {
|
|
346
366
|
try {
|
|
347
367
|
this.restorePersistedRuntimes();
|
|
348
368
|
} catch (error) {
|
|
349
|
-
this.
|
|
369
|
+
this.recordHostLog("error", `session host restore failed: ${error?.message || String(error)}`);
|
|
350
370
|
}
|
|
351
371
|
}, 0);
|
|
352
372
|
}
|
|
@@ -387,12 +407,14 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
387
407
|
const record = this.registry.createSession(request.payload);
|
|
388
408
|
this.schedulePersist(record.sessionId);
|
|
389
409
|
this.emitEvent({ type: "session_created", sessionId: record.sessionId, record });
|
|
410
|
+
this.recordRuntimeTransition(record.sessionId, "create_session", "starting", `provider=${record.providerType}`, true);
|
|
390
411
|
try {
|
|
391
412
|
const startedRecord = this.startRuntime(record, request.payload, "session_started");
|
|
392
413
|
return { success: true, result: startedRecord };
|
|
393
414
|
} catch (error) {
|
|
394
415
|
this.registry.markStopped(record.sessionId, "failed");
|
|
395
416
|
this.persistNow(record.sessionId);
|
|
417
|
+
this.recordRuntimeTransition(record.sessionId, "create_session_failed", "failed", void 0, false, error?.message || String(error));
|
|
396
418
|
return { success: false, error: error?.message || String(error) };
|
|
397
419
|
}
|
|
398
420
|
}
|
|
@@ -405,32 +427,39 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
405
427
|
if (client) {
|
|
406
428
|
this.emitEvent({ type: "client_attached", sessionId: record.sessionId, client });
|
|
407
429
|
}
|
|
430
|
+
this.recordRuntimeTransition(record.sessionId, "attach_client", record.lifecycle, request.payload.clientId, true);
|
|
408
431
|
return { success: true, result: record };
|
|
409
432
|
}
|
|
410
433
|
case "detach_session": {
|
|
411
434
|
const record = this.registry.detachClient(request.payload);
|
|
412
435
|
this.schedulePersist(record.sessionId);
|
|
413
436
|
this.emitEvent({ type: "client_detached", sessionId: record.sessionId, clientId: request.payload.clientId });
|
|
437
|
+
this.recordRuntimeTransition(record.sessionId, "detach_client", record.lifecycle, request.payload.clientId, true);
|
|
414
438
|
return { success: true, result: record };
|
|
415
439
|
}
|
|
416
440
|
case "acquire_write": {
|
|
417
441
|
const record = this.registry.acquireWrite(request.payload);
|
|
418
442
|
this.persistNow(record.sessionId);
|
|
419
443
|
this.emitEvent({ type: "write_owner_changed", sessionId: record.sessionId, owner: record.writeOwner });
|
|
444
|
+
this.recordRuntimeTransition(record.sessionId, "acquire_write", record.lifecycle, request.payload.clientId, true);
|
|
420
445
|
return { success: true, result: record };
|
|
421
446
|
}
|
|
422
447
|
case "release_write": {
|
|
423
448
|
const record = this.registry.releaseWrite(request.payload);
|
|
424
449
|
this.persistNow(record.sessionId);
|
|
425
450
|
this.emitEvent({ type: "write_owner_changed", sessionId: record.sessionId, owner: record.writeOwner });
|
|
451
|
+
this.recordRuntimeTransition(record.sessionId, "release_write", record.lifecycle, request.payload.clientId, true);
|
|
426
452
|
return { success: true, result: record };
|
|
427
453
|
}
|
|
428
454
|
case "get_snapshot":
|
|
429
455
|
return { success: true, result: this.getSnapshot(request.payload.sessionId, request.payload.sinceSeq) };
|
|
456
|
+
case "get_host_diagnostics":
|
|
457
|
+
return { success: true, result: this.getHostDiagnostics(request.payload) };
|
|
430
458
|
case "clear_session_buffer": {
|
|
431
459
|
const record = this.registry.clearBuffer(request.payload.sessionId);
|
|
432
460
|
this.persistNow(record.sessionId);
|
|
433
461
|
this.emitEvent({ type: "session_cleared", sessionId: record.sessionId });
|
|
462
|
+
this.recordRuntimeTransition(record.sessionId, "clear_buffer", record.lifecycle, void 0, true);
|
|
434
463
|
return { success: true, result: record };
|
|
435
464
|
}
|
|
436
465
|
case "update_session_meta": {
|
|
@@ -440,6 +469,7 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
440
469
|
request.payload.replace === true
|
|
441
470
|
);
|
|
442
471
|
this.persistNow(record.sessionId);
|
|
472
|
+
this.recordRuntimeTransition(record.sessionId, "update_meta", record.lifecycle, void 0, true);
|
|
443
473
|
return { success: true, result: record };
|
|
444
474
|
}
|
|
445
475
|
case "send_input": {
|
|
@@ -484,6 +514,7 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
484
514
|
this.persistNow(request.payload.sessionId);
|
|
485
515
|
this.requireRuntime(request.payload.sessionId).stop();
|
|
486
516
|
this.emitEvent({ type: "session_stopped", sessionId: request.payload.sessionId });
|
|
517
|
+
this.recordRuntimeTransition(request.payload.sessionId, "stop_session", "stopping", void 0, true);
|
|
487
518
|
return { success: true, result: this.registry.getSession(request.payload.sessionId) };
|
|
488
519
|
}
|
|
489
520
|
case "resume_session": {
|
|
@@ -495,8 +526,38 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
495
526
|
return { success: true, result: existing };
|
|
496
527
|
}
|
|
497
528
|
const resumed = this.startRuntime(existing, this.buildPayloadFromRecord(existing), "session_resumed");
|
|
529
|
+
this.recordRuntimeTransition(request.payload.sessionId, "resume_session", resumed.lifecycle, void 0, true);
|
|
498
530
|
return { success: true, result: resumed };
|
|
499
531
|
}
|
|
532
|
+
case "restart_session": {
|
|
533
|
+
const restarted = await this.restartRuntime(request.payload.sessionId);
|
|
534
|
+
return { success: true, result: restarted };
|
|
535
|
+
}
|
|
536
|
+
case "send_signal": {
|
|
537
|
+
const runtime = this.requireRuntime(request.payload.sessionId);
|
|
538
|
+
runtime.sendSignal(request.payload.signal);
|
|
539
|
+
const record = this.registry.getSession(request.payload.sessionId);
|
|
540
|
+
this.recordRuntimeTransition(request.payload.sessionId, "send_signal", record?.lifecycle, request.payload.signal, true);
|
|
541
|
+
return { success: true, result: record };
|
|
542
|
+
}
|
|
543
|
+
case "force_detach_client": {
|
|
544
|
+
const session = this.registry.getSession(request.payload.sessionId);
|
|
545
|
+
if (session?.writeOwner?.clientId === request.payload.clientId) {
|
|
546
|
+
const released = this.registry.releaseWrite({
|
|
547
|
+
sessionId: request.payload.sessionId,
|
|
548
|
+
clientId: request.payload.clientId
|
|
549
|
+
});
|
|
550
|
+
this.emitEvent({ type: "write_owner_changed", sessionId: released.sessionId, owner: released.writeOwner });
|
|
551
|
+
}
|
|
552
|
+
const record = this.registry.detachClient({
|
|
553
|
+
sessionId: request.payload.sessionId,
|
|
554
|
+
clientId: request.payload.clientId
|
|
555
|
+
});
|
|
556
|
+
this.schedulePersist(record.sessionId);
|
|
557
|
+
this.emitEvent({ type: "client_detached", sessionId: record.sessionId, clientId: request.payload.clientId });
|
|
558
|
+
this.recordRuntimeTransition(record.sessionId, "force_detach_client", record.lifecycle, request.payload.clientId, true);
|
|
559
|
+
return { success: true, result: record };
|
|
560
|
+
}
|
|
500
561
|
default:
|
|
501
562
|
return { success: false, error: `Unsupported session host request: ${request?.type || "unknown"}` };
|
|
502
563
|
}
|
|
@@ -523,7 +584,18 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
523
584
|
this.emit("event", event);
|
|
524
585
|
}
|
|
525
586
|
async handleIncomingRequest(socket, envelope) {
|
|
587
|
+
const startedAt = Date.now();
|
|
526
588
|
const response = await this.handleRequest(envelope.request);
|
|
589
|
+
this.recordRequestTrace({
|
|
590
|
+
timestamp: startedAt,
|
|
591
|
+
requestId: envelope.requestId,
|
|
592
|
+
type: envelope.request.type,
|
|
593
|
+
sessionId: this.getRequestSessionId(envelope.request),
|
|
594
|
+
clientId: this.getRequestClientId(envelope.request),
|
|
595
|
+
success: response.success,
|
|
596
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
597
|
+
error: response.success ? void 0 : response.error
|
|
598
|
+
});
|
|
527
599
|
writeEnvelope(socket, createResponseEnvelope(envelope.requestId, response));
|
|
528
600
|
}
|
|
529
601
|
schedulePersist(sessionId) {
|
|
@@ -540,6 +612,99 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
540
612
|
const snapshot = this.getSnapshot(sessionId);
|
|
541
613
|
this.storage.save(record, snapshot);
|
|
542
614
|
}
|
|
615
|
+
getHostDiagnostics(payload) {
|
|
616
|
+
const limit = Math.max(1, Math.min(200, Number(payload?.limit) || 50));
|
|
617
|
+
return {
|
|
618
|
+
hostStartedAt: this.startedAt,
|
|
619
|
+
endpoint: this.endpoint.path,
|
|
620
|
+
runtimeCount: this.runtimes.size,
|
|
621
|
+
sessions: payload?.includeSessions === false ? void 0 : this.registry.listSessions(),
|
|
622
|
+
recentLogs: this.recentLogs.slice(-limit),
|
|
623
|
+
recentRequests: this.recentRequests.slice(-limit),
|
|
624
|
+
recentTransitions: this.recentTransitions.slice(-limit)
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
getRequestSessionId(request) {
|
|
628
|
+
const payload = request.payload;
|
|
629
|
+
return typeof payload?.sessionId === "string" ? payload.sessionId : void 0;
|
|
630
|
+
}
|
|
631
|
+
getRequestClientId(request) {
|
|
632
|
+
const payload = request.payload;
|
|
633
|
+
return typeof payload?.clientId === "string" ? payload.clientId : void 0;
|
|
634
|
+
}
|
|
635
|
+
pushRecent(bucket, entry) {
|
|
636
|
+
bucket.push(entry);
|
|
637
|
+
if (bucket.length > _SessionHostServer.MAX_RECENT_DIAGNOSTICS) {
|
|
638
|
+
bucket.splice(0, bucket.length - _SessionHostServer.MAX_RECENT_DIAGNOSTICS);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
recordHostLog(level, message, sessionId, data) {
|
|
642
|
+
const entry = {
|
|
643
|
+
timestamp: Date.now(),
|
|
644
|
+
level,
|
|
645
|
+
message,
|
|
646
|
+
sessionId,
|
|
647
|
+
data
|
|
648
|
+
};
|
|
649
|
+
this.pushRecent(this.recentLogs, entry);
|
|
650
|
+
this.emitEvent({ type: "host_log", entry });
|
|
651
|
+
this.emit("log", `[${level}] ${message}`);
|
|
652
|
+
}
|
|
653
|
+
recordRequestTrace(trace) {
|
|
654
|
+
this.pushRecent(this.recentRequests, trace);
|
|
655
|
+
this.emitEvent({ type: "request_trace", trace });
|
|
656
|
+
if (!trace.success) {
|
|
657
|
+
this.recordHostLog(
|
|
658
|
+
"warn",
|
|
659
|
+
`request ${trace.type} failed after ${trace.durationMs}ms${trace.error ? `: ${trace.error}` : ""}`,
|
|
660
|
+
trace.sessionId,
|
|
661
|
+
{ requestId: trace.requestId, clientId: trace.clientId }
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
recordRuntimeTransition(sessionId, action, lifecycle, detail, success = true, error) {
|
|
666
|
+
const transition = {
|
|
667
|
+
timestamp: Date.now(),
|
|
668
|
+
sessionId,
|
|
669
|
+
action,
|
|
670
|
+
lifecycle,
|
|
671
|
+
detail,
|
|
672
|
+
success,
|
|
673
|
+
error
|
|
674
|
+
};
|
|
675
|
+
this.pushRecent(this.recentTransitions, transition);
|
|
676
|
+
this.emitEvent({ type: "runtime_transition", transition });
|
|
677
|
+
}
|
|
678
|
+
waitForRuntimeExit(sessionId, timeoutMs = 5e3) {
|
|
679
|
+
if (!this.runtimes.has(sessionId)) {
|
|
680
|
+
return Promise.resolve(this.registry.getSession(sessionId)?.lifecycle === "failed" ? 1 : 0);
|
|
681
|
+
}
|
|
682
|
+
return new Promise((resolve, reject) => {
|
|
683
|
+
const timeout = setTimeout(() => {
|
|
684
|
+
const waiters2 = this.exitWaiters.get(sessionId) || [];
|
|
685
|
+
this.exitWaiters.set(sessionId, waiters2.filter((waiter) => waiter !== onExit));
|
|
686
|
+
reject(new Error(`Timed out waiting for runtime ${sessionId} to exit`));
|
|
687
|
+
}, timeoutMs);
|
|
688
|
+
const onExit = (exitCode) => {
|
|
689
|
+
clearTimeout(timeout);
|
|
690
|
+
resolve(exitCode);
|
|
691
|
+
};
|
|
692
|
+
const waiters = this.exitWaiters.get(sessionId) || [];
|
|
693
|
+
waiters.push(onExit);
|
|
694
|
+
this.exitWaiters.set(sessionId, waiters);
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
resolveExitWaiters(sessionId, exitCode) {
|
|
698
|
+
const waiters = this.exitWaiters.get(sessionId);
|
|
699
|
+
if (!waiters?.length) return;
|
|
700
|
+
this.exitWaiters.delete(sessionId);
|
|
701
|
+
for (const waiter of waiters) {
|
|
702
|
+
try {
|
|
703
|
+
waiter(exitCode);
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
543
708
|
getSnapshot(sessionId, sinceSeq) {
|
|
544
709
|
const snapshot = this.registry.getSnapshot(sessionId, sinceSeq);
|
|
545
710
|
const record = this.registry.getSession(sessionId);
|
|
@@ -575,6 +740,23 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
575
740
|
this.persistNow(record.sessionId);
|
|
576
741
|
}
|
|
577
742
|
}
|
|
743
|
+
async restartRuntime(sessionId) {
|
|
744
|
+
const existing = this.registry.getSession(sessionId);
|
|
745
|
+
if (!existing) {
|
|
746
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
747
|
+
}
|
|
748
|
+
if (this.runtimes.has(sessionId)) {
|
|
749
|
+
this.registry.setLifecycle(sessionId, "stopping");
|
|
750
|
+
this.persistNow(sessionId);
|
|
751
|
+
this.recordRuntimeTransition(sessionId, "restart_requested", "stopping", void 0, true);
|
|
752
|
+
this.requireRuntime(sessionId).stop();
|
|
753
|
+
await this.waitForRuntimeExit(sessionId);
|
|
754
|
+
}
|
|
755
|
+
const latest = this.registry.getSession(sessionId) || existing;
|
|
756
|
+
const restarted = this.startRuntime(latest, this.buildPayloadFromRecord(latest), "session_resumed");
|
|
757
|
+
this.recordRuntimeTransition(sessionId, "restart_completed", restarted.lifecycle, void 0, true);
|
|
758
|
+
return restarted;
|
|
759
|
+
}
|
|
578
760
|
restorePersistedRuntimes() {
|
|
579
761
|
const states = this.storage.loadAll();
|
|
580
762
|
const runtimesToResume = [];
|
|
@@ -607,7 +789,7 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
607
789
|
}
|
|
608
790
|
}
|
|
609
791
|
if (skippedOrphanLiveSessions > 0) {
|
|
610
|
-
this.
|
|
792
|
+
this.recordHostLog("warn", `session host skipped ${skippedOrphanLiveSessions} orphan live runtime(s) during restore`);
|
|
611
793
|
}
|
|
612
794
|
for (const { persisted, recoveredRecord } of runtimesToResume) {
|
|
613
795
|
try {
|
|
@@ -626,6 +808,7 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
626
808
|
this.registry.getSnapshot(resumed.sessionId)
|
|
627
809
|
);
|
|
628
810
|
this.persistNow(resumed.sessionId);
|
|
811
|
+
this.recordRuntimeTransition(resumed.sessionId, "restore_auto_resumed", resumed.lifecycle, void 0, true);
|
|
629
812
|
} catch (error) {
|
|
630
813
|
const interrupted = this.registry.setLifecycle(recoveredRecord.sessionId, "interrupted");
|
|
631
814
|
this.registry.restoreSession({
|
|
@@ -638,6 +821,8 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
638
821
|
}
|
|
639
822
|
}, persisted.snapshot);
|
|
640
823
|
this.persistNow(recoveredRecord.sessionId);
|
|
824
|
+
this.recordRuntimeTransition(recoveredRecord.sessionId, "restore_resume_failed", "interrupted", void 0, false, error?.message || String(error));
|
|
825
|
+
this.recordHostLog("error", `restore resume failed for ${recoveredRecord.sessionId}: ${error?.message || String(error)}`, recoveredRecord.sessionId);
|
|
641
826
|
}
|
|
642
827
|
}
|
|
643
828
|
}
|
|
@@ -667,8 +852,17 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
667
852
|
onExit: (exitCode) => {
|
|
668
853
|
this.registry.markStopped(record.sessionId, exitCode === 0 ? "stopped" : "failed");
|
|
669
854
|
this.runtimes.delete(record.sessionId);
|
|
855
|
+
this.resolveExitWaiters(record.sessionId, exitCode);
|
|
670
856
|
this.persistNow(record.sessionId);
|
|
671
857
|
this.emitEvent({ type: "session_exit", sessionId: record.sessionId, exitCode });
|
|
858
|
+
this.recordRuntimeTransition(
|
|
859
|
+
record.sessionId,
|
|
860
|
+
"session_exit",
|
|
861
|
+
exitCode === 0 ? "stopped" : "failed",
|
|
862
|
+
void 0,
|
|
863
|
+
exitCode === 0,
|
|
864
|
+
exitCode === 0 ? void 0 : `exitCode=${exitCode}`
|
|
865
|
+
);
|
|
672
866
|
setTimeout(() => this.storage.remove(record.sessionId), 5e3);
|
|
673
867
|
}
|
|
674
868
|
});
|
|
@@ -678,6 +872,7 @@ var SessionHostServer = class extends EventEmitter {
|
|
|
678
872
|
const startedRecord = this.registry.markStarted(record.sessionId, pid);
|
|
679
873
|
this.persistNow(record.sessionId);
|
|
680
874
|
this.emitEvent({ type: startEventType, sessionId: record.sessionId, pid });
|
|
875
|
+
this.recordRuntimeTransition(record.sessionId, startEventType, startedRecord.lifecycle, `pid=${pid}`, true);
|
|
681
876
|
return startedRecord;
|
|
682
877
|
}
|
|
683
878
|
};
|
|
@@ -939,6 +1134,7 @@ async function attachRuntime(target, readOnly = false, takeover = false) {
|
|
|
939
1134
|
return { signal, handler };
|
|
940
1135
|
});
|
|
941
1136
|
const unsubscribe = client.onEvent((event) => {
|
|
1137
|
+
if (!("sessionId" in event)) return;
|
|
942
1138
|
if (event.sessionId !== runtimeId) return;
|
|
943
1139
|
if (event.type === "session_output") {
|
|
944
1140
|
if (event.seq <= lastSeq) return;
|