@threadbase-sh/streamer 1.23.0 → 1.24.0

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/index.d.cts CHANGED
@@ -76,9 +76,16 @@ interface Logger {
76
76
  pino: Logger$1;
77
77
  }
78
78
 
79
+ declare const CLAUDE_CODE_PROVIDER: "claude-code";
80
+ declare const CODEX_CLI_PROVIDER: "codex-cli";
81
+ type ProviderName = typeof CLAUDE_CODE_PROVIDER | typeof CODEX_CLI_PROVIDER;
82
+ declare function isProviderName(value: unknown): value is ProviderName;
83
+ declare function isProviderResumable(_provider: string | null | undefined, availabilityResumable: boolean): boolean;
84
+
79
85
  type SessionStatus = "running" | "waiting_input" | "idle";
80
86
  interface ManagedSession {
81
87
  id: string;
88
+ provider?: ProviderName;
82
89
  projectId?: string;
83
90
  projectPath: string;
84
91
  projectName: string;
@@ -101,6 +108,15 @@ interface ManagedSession {
101
108
  lastActivityAt?: Date;
102
109
  filePath?: string;
103
110
  resumedFromConversationId?: string;
111
+ /**
112
+ * Set once a live session's underlying persisted conversation file is
113
+ * discovered after the fact (currently: fresh Codex sessions, whose
114
+ * rollout id isn't known until the CLI creates its own JSONL). Distinct
115
+ * from `resumedFromConversationId` (resume flow) and `conversationId` on
116
+ * `SessionResponse` (stable mobile deep-link alias, always === id for the
117
+ * lifetime of a live PTY) — must never be written into either of those.
118
+ */
119
+ boundConversationId?: string;
104
120
  /**
105
121
  * Multi-agent mode only. Per-session in-memory LRU of progress event ids
106
122
  * seen by the webhook receiver. Used to drop Temporal-replay duplicates
@@ -226,6 +242,7 @@ type WSMessage = {
226
242
  interface SessionResponse {
227
243
  id: string;
228
244
  conversationId: string;
245
+ provider?: ProviderName;
229
246
  projectId?: string;
230
247
  status: SessionStatus;
231
248
  projectPath: string;
@@ -251,6 +268,8 @@ interface SessionResponse {
251
268
  lastActivityAt?: string;
252
269
  filePath?: string;
253
270
  resumedFromConversationId?: string;
271
+ /** See `ManagedSession.boundConversationId` — never repurposes `conversationId`. */
272
+ boundConversationId?: string;
254
273
  }
255
274
  interface ConversationListResponse {
256
275
  conversations: unknown[];
@@ -324,6 +343,21 @@ interface StartFreshSessionOptions {
324
343
  projectName?: string;
325
344
  systemPrompt?: string;
326
345
  }
346
+ interface SessionRunner {
347
+ start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
348
+ startFresh(options: StartFreshSessionOptions): Promise<ManagedSession>;
349
+ sendInput(sessionId: string, input: string): number;
350
+ sendKeys(sessionId: string, keys: string): void;
351
+ cancel(sessionId: string): void;
352
+ killPid(pid: number): void;
353
+ putOnHold(sessionId: string): void;
354
+ getOutput(sessionId: string): string;
355
+ getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
356
+ getSession(sessionId: string): ManagedSession | null;
357
+ hasSession(sessionId: string): boolean;
358
+ listSessions(): ManagedSession[];
359
+ dispose(): void;
360
+ }
327
361
 
328
362
  interface ConversationCacheOptions {
329
363
  filterAgentConversations?: boolean;
@@ -454,7 +488,24 @@ declare class ConversationCache {
454
488
  getSessionName(sessionId: string): string | null;
455
489
  listSessionNames(): Record<string, string>;
456
490
  invalidate(id?: string): void;
457
- invalidateByFilePath(filePath: string): string | null;
491
+ /**
492
+ * Drop the cached row for a file. Two callers with opposite intent:
493
+ * - a directory-watch "change" event (the file was appended to) — pass
494
+ * `skipIfTailed: true`. A cached tail means the row is being actively
495
+ * maintained from the file's real content by the live-tail
496
+ * (updateFromLines) or warm-up path, fresher than any scanner-derived
497
+ * view. Both watchers fire on the same append with no ordering guarantee;
498
+ * without this guard the invalidate can land after the tail write and wipe
499
+ * the just-cached row, flickering the conversation out of
500
+ * /api/conversations on nearly every message (CRITICAL #2). The debounced
501
+ * rescan still re-derives metadata, so skipping the eager drop loses
502
+ * nothing.
503
+ * - a genuine unlink (the file is gone) — leave `skipIfTailed` false so the
504
+ * row is always removed, otherwise a deleted session ghosts in the cache.
505
+ */
506
+ invalidateByFilePath(filePath: string, opts?: {
507
+ skipIfTailed?: boolean;
508
+ }): string | null;
458
509
  /**
459
510
  * Drop rows whose `file_path` no longer exists on disk AND which have no
460
511
  * cached tail to fall back to. Rows with a tail are left alone so
@@ -579,30 +630,17 @@ declare class SessionsRepository {
579
630
  listManagedSessions(): ManagedSession[];
580
631
  }
581
632
 
582
- declare class PTYManager {
583
- private sessions;
584
- private onOutput;
585
- private onStatusChange;
586
- private onReady;
587
- private onPermissionChange;
588
- private onLiveQuestion;
589
- private onLiveQuestionGone;
590
- private permissionOpen;
591
- private lastScreenQuestionKey;
592
- private shellPromptOpen;
593
- private pendingReady;
594
- private queuedInputs;
595
- private log;
596
- private firstChunkAt;
597
- private chunkIndex;
598
- private lastChunkAt;
633
+ declare class LiveSessionManager {
634
+ private runners;
599
635
  constructor(options?: PTYManagerOptions);
600
- start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
601
- startFresh(options: StartFreshSessionOptions): Promise<ManagedSession>;
602
- sendKeys(sessionId: string, keys: string): void;
636
+ start(sessionId: string, options: StartSessionOptions & {
637
+ provider?: ProviderName;
638
+ }): Promise<ManagedSession>;
639
+ startFresh(options: StartFreshSessionOptions & {
640
+ provider?: ProviderName;
641
+ }): Promise<ManagedSession>;
603
642
  sendInput(sessionId: string, input: string): number;
604
- private writeSubmit;
605
- private flushQueuedInputs;
643
+ sendKeys(sessionId: string, keys: string): void;
606
644
  cancel(sessionId: string): void;
607
645
  killPid(pid: number): void;
608
646
  putOnHold(sessionId: string): void;
@@ -612,10 +650,8 @@ declare class PTYManager {
612
650
  hasSession(sessionId: string): boolean;
613
651
  listSessions(): ManagedSession[];
614
652
  dispose(): void;
615
- private handleOutput;
616
- private detectLivePrompts;
617
- private markReady;
618
- private handleExit;
653
+ private runnerFor;
654
+ private assertSupportedProvider;
619
655
  }
620
656
 
621
657
  declare class WSHub {
@@ -640,7 +676,7 @@ type ApiDeps = {
640
676
  };
641
677
  publicUrl: string | null;
642
678
  browseRoot: string | null;
643
- ptyManager: PTYManager;
679
+ ptyManager: LiveSessionManager;
644
680
  sessionStore: SessionStore;
645
681
  wsHub: WSHub;
646
682
  cache: () => ConversationCache | null;
@@ -744,6 +780,47 @@ declare function createPool(config: DbConfig): Promise<Pool>;
744
780
 
745
781
  declare function discoverClaudeProcesses(): Promise<DiscoveredProcess[]>;
746
782
 
783
+ declare class PTYManager implements SessionRunner {
784
+ private sessions;
785
+ private onOutput;
786
+ private onStatusChange;
787
+ private onReady;
788
+ private onPermissionChange;
789
+ private onLiveQuestion;
790
+ private onLiveQuestionGone;
791
+ private permissionOpen;
792
+ private lastScreenQuestionKey;
793
+ private shellPromptOpen;
794
+ private pendingReady;
795
+ private queuedInputs;
796
+ private log;
797
+ private firstChunkAt;
798
+ private chunkIndex;
799
+ private lastChunkAt;
800
+ private startPromises;
801
+ constructor(options?: PTYManagerOptions);
802
+ start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
803
+ private doStart;
804
+ startFresh(options: StartFreshSessionOptions): Promise<ManagedSession>;
805
+ sendKeys(sessionId: string, keys: string): void;
806
+ sendInput(sessionId: string, input: string): number;
807
+ private writeSubmit;
808
+ private flushQueuedInputs;
809
+ cancel(sessionId: string): void;
810
+ killPid(pid: number): void;
811
+ putOnHold(sessionId: string): void;
812
+ getOutput(sessionId: string): string;
813
+ getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
814
+ getSession(sessionId: string): ManagedSession | null;
815
+ hasSession(sessionId: string): boolean;
816
+ listSessions(): ManagedSession[];
817
+ dispose(): void;
818
+ private handleOutput;
819
+ private detectLivePrompts;
820
+ private markReady;
821
+ private handleExit;
822
+ }
823
+
747
824
  declare class StreamerServer {
748
825
  private httpServer;
749
826
  private ptyManager;
@@ -860,6 +937,7 @@ declare class StreamerServer {
860
937
  private linkSessionToProject;
861
938
  private watchConversationFile;
862
939
  private watchForJsonl;
940
+ private watchForCodexRollout;
863
941
  private handleBrowse;
864
942
  private handleMkdir;
865
943
  private handleSetSessionName;
@@ -916,4 +994,4 @@ declare class ConversationWatcher {
916
994
  private readNewLines;
917
995
  }
918
996
 
919
- export { type AgentClient, type AgentClientOpts, type AgentConfig, type AppendArgs, type AskOption, type AskQuestion, type ConversationListResponse, ConversationWatcher, type ConversationWriter, type DbConfig, type DiscoveredProcess, type ManagedSession, PTYManager, type PTYManagerOptions, type PermissionOption, type ProgressDedupeLRU, type ServerConfig, type SessionCursor, type SessionListPage, type SessionListQuery, type SessionResponse, type SessionSortKey, type SessionStatus, SessionStore, type SortOrder, type StartFreshSessionOptions, type StartSessionOptions, StreamerServer, WSHub, type WSMessage, createAgentClient, createConversationWriter, createPool, createProgressDedupeLRU, createProgressRoutes, discoverClaudeProcesses, generateApiKey, getDbConfig, isDbEnabled, loadOrCreateApiKey, maskConnectionString, readAgentConfig, validateApiKey };
997
+ export { type AgentClient, type AgentClientOpts, type AgentConfig, type AppendArgs, type AskOption, type AskQuestion, CLAUDE_CODE_PROVIDER, CODEX_CLI_PROVIDER, type ConversationListResponse, ConversationWatcher, type ConversationWriter, type DbConfig, type DiscoveredProcess, LiveSessionManager, type ManagedSession, PTYManager, type PTYManagerOptions, type PermissionOption, type ProgressDedupeLRU, type ProviderName, type ServerConfig, type SessionCursor, type SessionListPage, type SessionListQuery, type SessionResponse, type SessionRunner, type SessionSortKey, type SessionStatus, SessionStore, type SortOrder, type StartFreshSessionOptions, type StartSessionOptions, StreamerServer, WSHub, type WSMessage, createAgentClient, createConversationWriter, createPool, createProgressDedupeLRU, createProgressRoutes, discoverClaudeProcesses, generateApiKey, getDbConfig, isDbEnabled, isProviderName, isProviderResumable, loadOrCreateApiKey, maskConnectionString, readAgentConfig, validateApiKey };
package/dist/index.d.ts CHANGED
@@ -76,9 +76,16 @@ interface Logger {
76
76
  pino: Logger$1;
77
77
  }
78
78
 
79
+ declare const CLAUDE_CODE_PROVIDER: "claude-code";
80
+ declare const CODEX_CLI_PROVIDER: "codex-cli";
81
+ type ProviderName = typeof CLAUDE_CODE_PROVIDER | typeof CODEX_CLI_PROVIDER;
82
+ declare function isProviderName(value: unknown): value is ProviderName;
83
+ declare function isProviderResumable(_provider: string | null | undefined, availabilityResumable: boolean): boolean;
84
+
79
85
  type SessionStatus = "running" | "waiting_input" | "idle";
80
86
  interface ManagedSession {
81
87
  id: string;
88
+ provider?: ProviderName;
82
89
  projectId?: string;
83
90
  projectPath: string;
84
91
  projectName: string;
@@ -101,6 +108,15 @@ interface ManagedSession {
101
108
  lastActivityAt?: Date;
102
109
  filePath?: string;
103
110
  resumedFromConversationId?: string;
111
+ /**
112
+ * Set once a live session's underlying persisted conversation file is
113
+ * discovered after the fact (currently: fresh Codex sessions, whose
114
+ * rollout id isn't known until the CLI creates its own JSONL). Distinct
115
+ * from `resumedFromConversationId` (resume flow) and `conversationId` on
116
+ * `SessionResponse` (stable mobile deep-link alias, always === id for the
117
+ * lifetime of a live PTY) — must never be written into either of those.
118
+ */
119
+ boundConversationId?: string;
104
120
  /**
105
121
  * Multi-agent mode only. Per-session in-memory LRU of progress event ids
106
122
  * seen by the webhook receiver. Used to drop Temporal-replay duplicates
@@ -226,6 +242,7 @@ type WSMessage = {
226
242
  interface SessionResponse {
227
243
  id: string;
228
244
  conversationId: string;
245
+ provider?: ProviderName;
229
246
  projectId?: string;
230
247
  status: SessionStatus;
231
248
  projectPath: string;
@@ -251,6 +268,8 @@ interface SessionResponse {
251
268
  lastActivityAt?: string;
252
269
  filePath?: string;
253
270
  resumedFromConversationId?: string;
271
+ /** See `ManagedSession.boundConversationId` — never repurposes `conversationId`. */
272
+ boundConversationId?: string;
254
273
  }
255
274
  interface ConversationListResponse {
256
275
  conversations: unknown[];
@@ -324,6 +343,21 @@ interface StartFreshSessionOptions {
324
343
  projectName?: string;
325
344
  systemPrompt?: string;
326
345
  }
346
+ interface SessionRunner {
347
+ start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
348
+ startFresh(options: StartFreshSessionOptions): Promise<ManagedSession>;
349
+ sendInput(sessionId: string, input: string): number;
350
+ sendKeys(sessionId: string, keys: string): void;
351
+ cancel(sessionId: string): void;
352
+ killPid(pid: number): void;
353
+ putOnHold(sessionId: string): void;
354
+ getOutput(sessionId: string): string;
355
+ getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
356
+ getSession(sessionId: string): ManagedSession | null;
357
+ hasSession(sessionId: string): boolean;
358
+ listSessions(): ManagedSession[];
359
+ dispose(): void;
360
+ }
327
361
 
328
362
  interface ConversationCacheOptions {
329
363
  filterAgentConversations?: boolean;
@@ -454,7 +488,24 @@ declare class ConversationCache {
454
488
  getSessionName(sessionId: string): string | null;
455
489
  listSessionNames(): Record<string, string>;
456
490
  invalidate(id?: string): void;
457
- invalidateByFilePath(filePath: string): string | null;
491
+ /**
492
+ * Drop the cached row for a file. Two callers with opposite intent:
493
+ * - a directory-watch "change" event (the file was appended to) — pass
494
+ * `skipIfTailed: true`. A cached tail means the row is being actively
495
+ * maintained from the file's real content by the live-tail
496
+ * (updateFromLines) or warm-up path, fresher than any scanner-derived
497
+ * view. Both watchers fire on the same append with no ordering guarantee;
498
+ * without this guard the invalidate can land after the tail write and wipe
499
+ * the just-cached row, flickering the conversation out of
500
+ * /api/conversations on nearly every message (CRITICAL #2). The debounced
501
+ * rescan still re-derives metadata, so skipping the eager drop loses
502
+ * nothing.
503
+ * - a genuine unlink (the file is gone) — leave `skipIfTailed` false so the
504
+ * row is always removed, otherwise a deleted session ghosts in the cache.
505
+ */
506
+ invalidateByFilePath(filePath: string, opts?: {
507
+ skipIfTailed?: boolean;
508
+ }): string | null;
458
509
  /**
459
510
  * Drop rows whose `file_path` no longer exists on disk AND which have no
460
511
  * cached tail to fall back to. Rows with a tail are left alone so
@@ -579,30 +630,17 @@ declare class SessionsRepository {
579
630
  listManagedSessions(): ManagedSession[];
580
631
  }
581
632
 
582
- declare class PTYManager {
583
- private sessions;
584
- private onOutput;
585
- private onStatusChange;
586
- private onReady;
587
- private onPermissionChange;
588
- private onLiveQuestion;
589
- private onLiveQuestionGone;
590
- private permissionOpen;
591
- private lastScreenQuestionKey;
592
- private shellPromptOpen;
593
- private pendingReady;
594
- private queuedInputs;
595
- private log;
596
- private firstChunkAt;
597
- private chunkIndex;
598
- private lastChunkAt;
633
+ declare class LiveSessionManager {
634
+ private runners;
599
635
  constructor(options?: PTYManagerOptions);
600
- start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
601
- startFresh(options: StartFreshSessionOptions): Promise<ManagedSession>;
602
- sendKeys(sessionId: string, keys: string): void;
636
+ start(sessionId: string, options: StartSessionOptions & {
637
+ provider?: ProviderName;
638
+ }): Promise<ManagedSession>;
639
+ startFresh(options: StartFreshSessionOptions & {
640
+ provider?: ProviderName;
641
+ }): Promise<ManagedSession>;
603
642
  sendInput(sessionId: string, input: string): number;
604
- private writeSubmit;
605
- private flushQueuedInputs;
643
+ sendKeys(sessionId: string, keys: string): void;
606
644
  cancel(sessionId: string): void;
607
645
  killPid(pid: number): void;
608
646
  putOnHold(sessionId: string): void;
@@ -612,10 +650,8 @@ declare class PTYManager {
612
650
  hasSession(sessionId: string): boolean;
613
651
  listSessions(): ManagedSession[];
614
652
  dispose(): void;
615
- private handleOutput;
616
- private detectLivePrompts;
617
- private markReady;
618
- private handleExit;
653
+ private runnerFor;
654
+ private assertSupportedProvider;
619
655
  }
620
656
 
621
657
  declare class WSHub {
@@ -640,7 +676,7 @@ type ApiDeps = {
640
676
  };
641
677
  publicUrl: string | null;
642
678
  browseRoot: string | null;
643
- ptyManager: PTYManager;
679
+ ptyManager: LiveSessionManager;
644
680
  sessionStore: SessionStore;
645
681
  wsHub: WSHub;
646
682
  cache: () => ConversationCache | null;
@@ -744,6 +780,47 @@ declare function createPool(config: DbConfig): Promise<Pool>;
744
780
 
745
781
  declare function discoverClaudeProcesses(): Promise<DiscoveredProcess[]>;
746
782
 
783
+ declare class PTYManager implements SessionRunner {
784
+ private sessions;
785
+ private onOutput;
786
+ private onStatusChange;
787
+ private onReady;
788
+ private onPermissionChange;
789
+ private onLiveQuestion;
790
+ private onLiveQuestionGone;
791
+ private permissionOpen;
792
+ private lastScreenQuestionKey;
793
+ private shellPromptOpen;
794
+ private pendingReady;
795
+ private queuedInputs;
796
+ private log;
797
+ private firstChunkAt;
798
+ private chunkIndex;
799
+ private lastChunkAt;
800
+ private startPromises;
801
+ constructor(options?: PTYManagerOptions);
802
+ start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
803
+ private doStart;
804
+ startFresh(options: StartFreshSessionOptions): Promise<ManagedSession>;
805
+ sendKeys(sessionId: string, keys: string): void;
806
+ sendInput(sessionId: string, input: string): number;
807
+ private writeSubmit;
808
+ private flushQueuedInputs;
809
+ cancel(sessionId: string): void;
810
+ killPid(pid: number): void;
811
+ putOnHold(sessionId: string): void;
812
+ getOutput(sessionId: string): string;
813
+ getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
814
+ getSession(sessionId: string): ManagedSession | null;
815
+ hasSession(sessionId: string): boolean;
816
+ listSessions(): ManagedSession[];
817
+ dispose(): void;
818
+ private handleOutput;
819
+ private detectLivePrompts;
820
+ private markReady;
821
+ private handleExit;
822
+ }
823
+
747
824
  declare class StreamerServer {
748
825
  private httpServer;
749
826
  private ptyManager;
@@ -860,6 +937,7 @@ declare class StreamerServer {
860
937
  private linkSessionToProject;
861
938
  private watchConversationFile;
862
939
  private watchForJsonl;
940
+ private watchForCodexRollout;
863
941
  private handleBrowse;
864
942
  private handleMkdir;
865
943
  private handleSetSessionName;
@@ -916,4 +994,4 @@ declare class ConversationWatcher {
916
994
  private readNewLines;
917
995
  }
918
996
 
919
- export { type AgentClient, type AgentClientOpts, type AgentConfig, type AppendArgs, type AskOption, type AskQuestion, type ConversationListResponse, ConversationWatcher, type ConversationWriter, type DbConfig, type DiscoveredProcess, type ManagedSession, PTYManager, type PTYManagerOptions, type PermissionOption, type ProgressDedupeLRU, type ServerConfig, type SessionCursor, type SessionListPage, type SessionListQuery, type SessionResponse, type SessionSortKey, type SessionStatus, SessionStore, type SortOrder, type StartFreshSessionOptions, type StartSessionOptions, StreamerServer, WSHub, type WSMessage, createAgentClient, createConversationWriter, createPool, createProgressDedupeLRU, createProgressRoutes, discoverClaudeProcesses, generateApiKey, getDbConfig, isDbEnabled, loadOrCreateApiKey, maskConnectionString, readAgentConfig, validateApiKey };
997
+ export { type AgentClient, type AgentClientOpts, type AgentConfig, type AppendArgs, type AskOption, type AskQuestion, CLAUDE_CODE_PROVIDER, CODEX_CLI_PROVIDER, type ConversationListResponse, ConversationWatcher, type ConversationWriter, type DbConfig, type DiscoveredProcess, LiveSessionManager, type ManagedSession, PTYManager, type PTYManagerOptions, type PermissionOption, type ProgressDedupeLRU, type ProviderName, type ServerConfig, type SessionCursor, type SessionListPage, type SessionListQuery, type SessionResponse, type SessionRunner, type SessionSortKey, type SessionStatus, SessionStore, type SortOrder, type StartFreshSessionOptions, type StartSessionOptions, StreamerServer, WSHub, type WSMessage, createAgentClient, createConversationWriter, createPool, createProgressDedupeLRU, createProgressRoutes, discoverClaudeProcesses, generateApiKey, getDbConfig, isDbEnabled, isProviderName, isProviderResumable, loadOrCreateApiKey, maskConnectionString, readAgentConfig, validateApiKey };