@threadbase-sh/streamer 1.36.3 → 1.37.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.ts CHANGED
@@ -78,6 +78,30 @@ interface Logger {
78
78
  pino: Logger$1;
79
79
  }
80
80
 
81
+ /** Claude Code `--permission-mode` values, as accepted by CLI v2.1.x. */
82
+ declare const PERMISSION_MODES: readonly ["acceptEdits", "auto", "bypassPermissions", "manual", "dontAsk", "plan"];
83
+ type PermissionMode = (typeof PERMISSION_MODES)[number];
84
+ type FlagValueType = "boolean" | "string" | "enum" | "list";
85
+ /** How risky enabling a flag is. Drives the client's confirmation UX. */
86
+ type FlagRisk = "low" | "elevated" | "dangerous";
87
+ interface FlagDefinition {
88
+ /** Stable config/wire key. Never the CLI spelling. */
89
+ id: string;
90
+ /** The literal CLI token, e.g. "--add-dir". */
91
+ flag: string;
92
+ valueType: FlagValueType;
93
+ /** Allowed values when valueType === "enum". */
94
+ enumValues?: readonly string[];
95
+ /**
96
+ * Baseline risk. `permissionMode` is the exception: it is only dangerous for
97
+ * the values in DANGEROUS_PERMISSION_MODES, so clients must call
98
+ * `flagValueRisk()` rather than reading this field directly.
99
+ */
100
+ risk: FlagRisk;
101
+ }
102
+ type ClaudeFlagValue = string | string[] | boolean;
103
+ type ClaudeFlagValues = Record<string, ClaudeFlagValue>;
104
+
81
105
  declare const CLAUDE_CODE_PROVIDER: "claude-code";
82
106
  declare const CODEX_CLI_PROVIDER: "codex-cli";
83
107
  type ProviderName = typeof CLAUDE_CODE_PROVIDER | typeof CODEX_CLI_PROVIDER;
@@ -85,6 +109,34 @@ declare function isProviderName(value: unknown): value is ProviderName;
85
109
  declare function isProviderResumable(_provider: string | null | undefined, availabilityResumable: boolean): boolean;
86
110
 
87
111
  type SessionStatus = "running" | "waiting_input" | "idle";
112
+ /**
113
+ * Process-lifetime axis for a managed session (C1 durable session runtime).
114
+ * Orthogonal to SessionStatus — see SessionResponse.lifecycle for why the two
115
+ * are separate, and docs/architecture/2026-07-24-durable-session-runtime.md.
116
+ */
117
+ type SessionLifecycle = "attached" | "detached" | "orphaned" | "resumable" | "completed" | "failed";
118
+ /**
119
+ * How a SessionStatus was derived (C3).
120
+ * See docs/architecture/2026-07-24-session-state-confidence.md.
121
+ *
122
+ * The runners already compute this at every transition — it was written to a log
123
+ * line and discarded, so a status reached by a timer expiring was indistinguishable
124
+ * on the wire from one reached by observing a prompt marker.
125
+ */
126
+ type StatusSource = "spawn" | "prompt-marker" | "screen-marker" | "user-input" | "process-exit" | "timeout-fallback" | "quiet-fallback" | "shutdown";
127
+ /**
128
+ * How much to trust the status.
129
+ *
130
+ * `observed` — something in the stream or the process told us.
131
+ * `inferred` — a timer expired and we picked the most likely state.
132
+ *
133
+ * Deliberately two buckets rather than a numeric score: a percentage would imply
134
+ * a calibration we have no data to support. The point is that a guess must never
135
+ * be presented as an observation.
136
+ */
137
+ type StatusConfidence = "observed" | "inferred";
138
+ /** Confidence implied by each source. Inference is exactly the timer-driven paths. */
139
+ declare function confidenceForSource(source: StatusSource): StatusConfidence;
88
140
  interface ManagedSession {
89
141
  id: string;
90
142
  provider?: ProviderName;
@@ -108,6 +160,12 @@ interface ManagedSession {
108
160
  lastMessageText?: string;
109
161
  lastMessageAt?: Date;
110
162
  lastActivityAt?: Date;
163
+ /**
164
+ * How `status` was derived, and when (C3). Confidence is derived from the
165
+ * source via confidenceForSource — storing both would let them disagree.
166
+ */
167
+ statusSource?: StatusSource;
168
+ statusUpdatedAt?: Date;
111
169
  filePath?: string;
112
170
  resumedFromConversationId?: string;
113
171
  /**
@@ -297,6 +355,31 @@ interface SessionResponse {
297
355
  startedAt: string;
298
356
  completedAt: string | null;
299
357
  ptyAttached: boolean;
358
+ /**
359
+ * Process-lifetime axis, orthogonal to `status` (C1).
360
+ *
361
+ * `status` answers "what is the agent doing" (running / waiting_input /
362
+ * idle); `lifecycle` answers "does this process still exist and do we own
363
+ * it". They were conflated before: `idle` meant finished, killed-to-save-
364
+ * resources, and externally-discovered all at once, so a client could not
365
+ * tell a completed session from one we terminated.
366
+ *
367
+ * Additive and optional — `ptyAttached` keeps its meaning (=== "attached"),
368
+ * so a client that ignores this behaves exactly as it did before.
369
+ */
370
+ /**
371
+ * How `status` was derived and how far to trust it (C3). Additive: `status`
372
+ * keeps its exact meaning, so a client ignoring these behaves as before.
373
+ * An `inferred` confidence means a timer expired and we assumed — not that
374
+ * anything in the stream confirmed the state.
375
+ */
376
+ statusSource?: StatusSource;
377
+ statusConfidence?: StatusConfidence;
378
+ statusUpdatedAt?: string;
379
+ lifecycle?: SessionLifecycle;
380
+ /** How `lifecycle` was determined, so stale values are visible not implied. */
381
+ lifecycleSource?: "spawn" | "exit" | "probe" | "reconcile";
382
+ lifecycleUpdatedAt?: string;
300
383
  failureReason?: string;
301
384
  pid?: number;
302
385
  sessionName?: string;
@@ -404,9 +487,11 @@ interface ServerConfig {
404
487
  tailSize?: number;
405
488
  directoryScanDebounceMs?: number;
406
489
  defaultSystemPrompt?: string;
407
- defaultPermissionMode?: "acceptEdits" | "manual";
490
+ defaultPermissionMode?: PermissionMode;
408
491
  defaultModel?: string;
409
492
  defaultEffort?: "low" | "medium" | "high" | "xhigh" | "max";
493
+ claudeFlags?: ClaudeFlagValues;
494
+ claudeExtraArgs?: string;
410
495
  }
411
496
  interface PTYManagerOptions {
412
497
  onOutput?: (sessionId: string, data: string) => void;
@@ -427,17 +512,21 @@ interface StartSessionOptions {
427
512
  projectPath: string;
428
513
  projectName?: string;
429
514
  branch?: string;
430
- permissionMode?: "acceptEdits" | "manual";
515
+ permissionMode?: PermissionMode;
431
516
  model?: string;
432
517
  effort?: "low" | "medium" | "high" | "xhigh" | "max";
518
+ claudeFlags?: ClaudeFlagValues;
519
+ claudeExtraArgs?: string;
433
520
  }
434
521
  interface StartFreshSessionOptions {
435
522
  projectPath: string;
436
523
  projectName?: string;
437
524
  systemPrompt?: string;
438
- permissionMode?: "acceptEdits" | "manual";
525
+ permissionMode?: PermissionMode;
439
526
  model?: string;
440
527
  effort?: "low" | "medium" | "high" | "xhigh" | "max";
528
+ claudeFlags?: ClaudeFlagValues;
529
+ claudeExtraArgs?: string;
441
530
  }
442
531
  interface SessionRunner {
443
532
  start(sessionId: string, options: StartSessionOptions): Promise<ManagedSession>;
@@ -450,12 +539,38 @@ interface SessionRunner {
450
539
  getOutput(sessionId: string): string;
451
540
  getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
452
541
  getInputHistory(sessionId: string): UserMessage[];
542
+ getPid(sessionId: string): number | null;
453
543
  getSession(sessionId: string): ManagedSession | null;
454
544
  hasSession(sessionId: string): boolean;
455
545
  listSessions(): ManagedSession[];
456
546
  dispose(): void;
457
547
  }
458
548
 
549
+ /**
550
+ * Scoped device capabilities (C5).
551
+ * See docs/architecture/2026-07-24-device-identity-and-capabilities.md.
552
+ *
553
+ * Authorization was all-or-nothing: authMiddleware asked one question — is this
554
+ * token the API key — and answered 401 or full access. A device paired merely to
555
+ * glance at session status held exactly the authority of the one driving the
556
+ * agent, because there was only ever one credential and no principal to scope.
557
+ */
558
+ declare const CAPABILITIES: readonly ["history:read", "session:control", "fs:browse", "fs:upload", "notifications", "admin"];
559
+ type Capability = (typeof CAPABILITIES)[number];
560
+ type CapabilityPreset = "full" | "read-only";
561
+ /**
562
+ * The principal behind a request.
563
+ *
564
+ * `legacy` is the shared API key: it predates device identity, so it carries the
565
+ * full preset and no device id. Keeping it working is what lets this ship
566
+ * without breaking every already-paired device.
567
+ */
568
+ interface Principal {
569
+ kind: "device" | "legacy";
570
+ deviceId?: string;
571
+ capabilities: Capability[];
572
+ }
573
+
459
574
  interface LineSpan {
460
575
  /** Absolute byte offset of the line's first byte in the file. */
461
576
  byteOffset: number;
@@ -818,6 +933,74 @@ declare class ConversationsRepository {
818
933
  hasOrphanRows(): boolean;
819
934
  }
820
935
 
936
+ /**
937
+ * Paired-device registry (C5).
938
+ * See docs/architecture/2026-07-24-device-identity-and-capabilities.md.
939
+ *
940
+ * Stores identity and authority for each paired device. Never stores a usable
941
+ * credential: only the SHA-256 of a device token, so reading this table cannot
942
+ * impersonate a device.
943
+ */
944
+ interface DeviceRow {
945
+ device_id: string;
946
+ public_key: string;
947
+ token_hash: string;
948
+ name: string | null;
949
+ capabilities: string;
950
+ created_at: number;
951
+ last_seen_at: number | null;
952
+ revoked_at: number | null;
953
+ }
954
+ /** A device as reported over the API. Deliberately carries no credential. */
955
+ interface DeviceView {
956
+ deviceId: string;
957
+ name: string | null;
958
+ capabilities: Capability[];
959
+ createdAt: number;
960
+ lastSeenAt: number | null;
961
+ revokedAt: number | null;
962
+ }
963
+ interface RegisteredDevice {
964
+ deviceId: string;
965
+ /** Returned to the client exactly once, at pairing. Never persisted raw. */
966
+ deviceToken: string;
967
+ capabilities: Capability[];
968
+ }
969
+ declare class DevicesRepository {
970
+ private insertStmt;
971
+ private byTokenHashStmt;
972
+ private byIdStmt;
973
+ private listStmt;
974
+ private revokeStmt;
975
+ private touchStmt;
976
+ constructor(db: Database.Database);
977
+ /**
978
+ * Record a newly paired device and mint its token.
979
+ *
980
+ * The raw token is returned to the caller and never stored — this is the only
981
+ * moment it exists outside the client.
982
+ */
983
+ register(args: {
984
+ publicKey: string;
985
+ name?: string | null;
986
+ preset?: CapabilityPreset;
987
+ now?: number;
988
+ }): RegisteredDevice;
989
+ /**
990
+ * Resolve a presented token to a device, or null.
991
+ *
992
+ * Returns null for a revoked device, so revocation takes effect on the very
993
+ * next request with no cache to go stale.
994
+ */
995
+ authenticate(token: string): DeviceRow | null;
996
+ get(deviceId: string): DeviceRow | null;
997
+ /** All devices, including revoked ones — an audit surface needs the history. */
998
+ list(): DeviceView[];
999
+ /** Revoke one device. Others are untouched — no key rotation, no collateral. */
1000
+ revoke(deviceId: string, now?: number): boolean;
1001
+ touch(deviceId: string, now?: number): void;
1002
+ }
1003
+
821
1004
  /**
822
1005
  * Persisted Project row. Mirrors the projects SQLite table.
823
1006
  */
@@ -862,6 +1045,74 @@ declare class ProjectsRepository {
862
1045
  upsertProjectByPath(rawPath: string, input?: UpsertProjectInput): Project;
863
1046
  }
864
1047
 
1048
+ interface PushTokenRow {
1049
+ token: string;
1050
+ platform: string;
1051
+ device_id: string | null;
1052
+ registered_at: number;
1053
+ last_success_at: number | null;
1054
+ last_failure_at: number | null;
1055
+ last_failure_code: string | null;
1056
+ failure_streak: number;
1057
+ revoked_at: number | null;
1058
+ }
1059
+ /**
1060
+ * Health as reported to a client. Deliberately omits the token itself — a push
1061
+ * token is a delivery credential, and a health endpoint has no reason to echo
1062
+ * one back.
1063
+ */
1064
+ interface PushTokenHealth {
1065
+ platform: string;
1066
+ deviceId: string | null;
1067
+ registeredAt: number;
1068
+ lastSuccessAt: number | null;
1069
+ lastFailureAt: number | null;
1070
+ lastFailureCode: string | null;
1071
+ failureStreak: number;
1072
+ revokedAt: number | null;
1073
+ /**
1074
+ * Never delivered vs delivering vs failing vs revoked. The distinction the
1075
+ * user actually needs: "not yet" and "broken" look identical without it.
1076
+ */
1077
+ state: "never-delivered" | "healthy" | "failing" | "dead" | "revoked";
1078
+ }
1079
+ declare class PushRepository {
1080
+ private upsertStmt;
1081
+ private getStmt;
1082
+ private listActiveStmt;
1083
+ private listAllStmt;
1084
+ private successStmt;
1085
+ private failureStmt;
1086
+ private revokeStmt;
1087
+ private claimEventStmt;
1088
+ private markDeliveredStmt;
1089
+ constructor(db: Database.Database);
1090
+ register(args: {
1091
+ token: string;
1092
+ platform: string;
1093
+ deviceId?: string | null;
1094
+ now?: number;
1095
+ }): void;
1096
+ get(token: string): PushTokenRow | null;
1097
+ /** Tokens eligible for delivery — not revoked, not past the failure limit. */
1098
+ listDeliverable(): PushTokenRow[];
1099
+ /** Every token, including dead and revoked ones, for the health report. */
1100
+ listHealth(): PushTokenHealth[];
1101
+ recordSuccess(token: string, now?: number): void;
1102
+ recordFailure(token: string, code: string, now?: number): void;
1103
+ revoke(token: string, now?: number): boolean;
1104
+ /**
1105
+ * Claim an event id for delivery.
1106
+ *
1107
+ * Returns true exactly once per event id. A retry, a reconnect
1108
+ * reconciliation, or two triggers firing for the same underlying event all
1109
+ * get false and must not notify — the user should never be told twice about
1110
+ * one thing.
1111
+ */
1112
+ claimEvent(eventId: string, sessionId: string | null, now?: number): boolean;
1113
+ markDelivered(eventId: string, now?: number): void;
1114
+ }
1115
+
865
1116
  declare class SessionStore {
866
1117
  private managed;
867
1118
  private discovered;
@@ -914,6 +1165,7 @@ declare class LiveSessionManager {
914
1165
  getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
915
1166
  getInputHistory(sessionId: string): UserMessage[];
916
1167
  getSession(sessionId: string): ManagedSession | null;
1168
+ getPid(sessionId: string): number | null;
917
1169
  hasSession(sessionId: string): boolean;
918
1170
  listSessions(): ManagedSession[];
919
1171
  dispose(): void;
@@ -1025,6 +1277,17 @@ type ApiDeps = {
1025
1277
  newKey: string;
1026
1278
  persisted: boolean;
1027
1279
  };
1280
+ claudeFlagsConfig: () => {
1281
+ registry: readonly FlagDefinition[];
1282
+ values: ClaudeFlagValues;
1283
+ extraArgs: string | null;
1284
+ persisted: boolean;
1285
+ };
1286
+ setClaudeFlagsConfig: (values: ClaudeFlagValues, extraArgs: string | undefined) => {
1287
+ values: ClaudeFlagValues;
1288
+ extraArgs: string | null;
1289
+ persisted: boolean;
1290
+ };
1028
1291
  publicUrl: string | null;
1029
1292
  browseRoot: string | null;
1030
1293
  browserCors: string | undefined;
@@ -1033,6 +1296,10 @@ type ApiDeps = {
1033
1296
  wsHub: WSHub;
1034
1297
  cache: () => ConversationCache | null;
1035
1298
  cacheMonitor: () => CacheIntegrityMonitor | null;
1299
+ /** Push registration + delivery state (C7). Null when the cache DB is unavailable. */
1300
+ pushRepo: () => PushRepository | null;
1301
+ /** Paired-device registry (C5). Null when the cache DB is unavailable. */
1302
+ devicesRepo: () => DevicesRepository | null;
1036
1303
  projectsRepo: () => ProjectsRepository | null;
1037
1304
  conversationsRepo: () => ConversationsRepository | null;
1038
1305
  sessionsRepo: () => SessionsRepository | null;
@@ -1078,6 +1345,8 @@ type AppEnv = {
1078
1345
  requestId?: string;
1079
1346
  validatedBody?: unknown;
1080
1347
  validatedQuery?: unknown;
1348
+ /** Who is making this request (C5). Set by authMiddleware. */
1349
+ principal?: Principal;
1081
1350
  };
1082
1351
  };
1083
1352
 
@@ -1168,6 +1437,7 @@ declare class PTYManager implements SessionRunner {
1168
1437
  getOutput(sessionId: string): string;
1169
1438
  getOutputLines(sessionId: string, maxLines: number): Promise<string[]>;
1170
1439
  getInputHistory(sessionId: string): UserMessage[];
1440
+ getPid(sessionId: string): number | null;
1171
1441
  private recordUserMessage;
1172
1442
  getSession(sessionId: string): ManagedSession | null;
1173
1443
  hasSession(sessionId: string): boolean;
@@ -1195,6 +1465,7 @@ declare class StreamerServer {
1195
1465
  private selfPtyEndedAt;
1196
1466
  private pendingQuestionKey;
1197
1467
  private pendingPermission;
1468
+ private pendingPermissionKey;
1198
1469
  private scanner;
1199
1470
  private scannerPersistenceDisabled;
1200
1471
  private allScanners;
@@ -1227,9 +1498,16 @@ declare class StreamerServer {
1227
1498
  private defaultPermissionMode;
1228
1499
  private defaultModel;
1229
1500
  private defaultEffort;
1501
+ private claudeFlags;
1502
+ private claudeExtraArgs;
1503
+ private claudeFlagsPersistable;
1230
1504
  private ptyGraceTimers;
1231
1505
  private ptyGraceDeferCounts;
1232
1506
  private sessionSubscribers;
1507
+ private lastAgentChunkAt;
1508
+ private idempotency;
1509
+ private sessionLifecycles;
1510
+ private idleReaperTimer;
1233
1511
  private clientIdToWs;
1234
1512
  private wsToClientId;
1235
1513
  private cache;
@@ -1237,7 +1515,11 @@ declare class StreamerServer {
1237
1515
  private projectsRepo;
1238
1516
  private conversationsRepo;
1239
1517
  private sessionsRepo;
1518
+ private managedSessionsRepo;
1519
+ private readonly streamerInstanceId;
1240
1520
  private cacheMetadataRepo;
1521
+ private pushRepo;
1522
+ private devicesRepo;
1241
1523
  private discoveryCache;
1242
1524
  private cacheDir;
1243
1525
  private tailSize;
@@ -1262,7 +1544,86 @@ declare class StreamerServer {
1262
1544
  * a full broadcast if no match exists (old clients, or no WS registered yet).
1263
1545
  */
1264
1546
  private broadcastOrUnicastSessionList;
1547
+ /**
1548
+ * Overlay boot-reconciliation verdicts onto session responses.
1549
+ *
1550
+ * A session left by a previous run is not in the in-memory store, so
1551
+ * SessionStore cannot classify it — it only ever sees what this run spawned.
1552
+ * Discovery may still surface the process, in which case the reconciler knows
1553
+ * strictly more about it than discovery does: it can tell `detached` (alive
1554
+ * and confirmed ours) from `orphaned` (alive but identity unconfirmed), which
1555
+ * a pid enumeration alone cannot.
1556
+ *
1557
+ * Only applied when the session is NOT live here: a session this run owns has
1558
+ * an authoritative lifecycle already, and a stale verdict must never override
1559
+ * it.
1560
+ */
1561
+ private withReconciledLifecycle;
1265
1562
  private addSessionSubscriber;
1563
+ /**
1564
+ * Classify sessions left behind by previous streamer runs (C1 Phase 3a).
1565
+ *
1566
+ * Agents already outlive the streamer today on the crash and dev-takeover
1567
+ * paths, which exit without reaching ptyManager.dispose() — they are just
1568
+ * invisible when they do, because nothing recorded that they existed. This
1569
+ * turns those rows into an explicit verdict per session.
1570
+ *
1571
+ * Read-only with respect to processes: it probes and classifies, and never
1572
+ * signals anything. `orphaned` is a report, not a cleanup trigger.
1573
+ */
1574
+ private reconcilePreviousSessions;
1575
+ /**
1576
+ * Pick a token guaranteed to appear in the spawned process's argv, for the
1577
+ * reconciler's pid-reuse guard.
1578
+ *
1579
+ * Claude always passes the session id (`--resume <id>` or `--session-id
1580
+ * <id>`), so it is both present and unique. Codex only does on *resume*
1581
+ * (`codex resume <id>`); a fresh Codex spawn is `codex --cd <path>
1582
+ * --no-alt-screen` with no id at all, because the rollout id does not exist
1583
+ * until the CLI writes it. boundConversationId is what distinguishes the two:
1584
+ * it is set once that rollout has been discovered.
1585
+ */
1586
+ private spawnArgvToken;
1587
+ /**
1588
+ * Mirror a freshly-spawned session into the durable registry (C1 Phase 2).
1589
+ *
1590
+ * Called at each addManaged() site rather than inside SessionStore, because
1591
+ * the store is a pure in-memory structure with no DB dependency and adding
1592
+ * one would drag persistence into every unit test that touches it.
1593
+ *
1594
+ * Best-effort by design: a failed registry write must never break session
1595
+ * start. Losing a row costs post-restart *visibility* for that session, which
1596
+ * is strictly better than refusing to run the agent at all.
1597
+ */
1598
+ private recordSessionSpawn;
1599
+ /**
1600
+ * Stamp every live session as ended-by-shutdown before dispose() kills it.
1601
+ *
1602
+ * PTYManager.dispose() signals each child directly and fires no
1603
+ * onStatusChange, so the registry would otherwise keep rows sitting at
1604
+ * `running` forever and the next boot could not tell a deliberate restart
1605
+ * from a crash. Recording `shutdown` as the status source makes that
1606
+ * distinction explicit rather than inferred.
1607
+ *
1608
+ * Not a `completed_at` write for the agent's own work — the agent did not
1609
+ * finish, we stopped it — but the session is genuinely terminal, so it must
1610
+ * leave the reconciler's probe set.
1611
+ */
1612
+ private recordShutdownState;
1613
+ /**
1614
+ * Release PTYs whose agent has been silent past IDLE_REAP_AFTER_MS.
1615
+ *
1616
+ * This is the bound that lets handleWsClose stop arming kill timers. The
1617
+ * distinction that matters: the old timer measured how long nobody was
1618
+ * *watching*, which is uncorrelated with whether work is in flight. This
1619
+ * measures how long the *agent* has produced nothing, and only ever considers
1620
+ * sessions that are already settled — a `running` PTY is skipped regardless of
1621
+ * age, so a long silent turn is never interrupted.
1622
+ *
1623
+ * Exposed (not private) so tests can drive one sweep deterministically instead
1624
+ * of waiting on the interval.
1625
+ */
1626
+ reapIdleSessions(now?: number): string[];
1266
1627
  private startGraceTimer;
1267
1628
  get port(): number;
1268
1629
  private currentWarmupState;
@@ -1282,6 +1643,19 @@ declare class StreamerServer {
1282
1643
  private handlePairStart;
1283
1644
  private handlePairExchange;
1284
1645
  private rotateApiKey;
1646
+ private getClaudeFlagsConfig;
1647
+ /**
1648
+ * Replace the per-server flag set. Applies to the NEXT spawn — a live PTY
1649
+ * keeps the argv it was started with.
1650
+ *
1651
+ * Mirrors rotateApiKey(): when the values were pinned by a CLI flag we still
1652
+ * apply them in memory but skip the server.yaml write, because the flag would
1653
+ * win again on restart and silently revert them.
1654
+ *
1655
+ * Logged with old→new at info level on purpose: this can disable the
1656
+ * permission prompts entirely, so it needs a forensic trail.
1657
+ */
1658
+ private setClaudeFlagsConfig;
1285
1659
  private checkRateLimit;
1286
1660
  private checkExchangeRateLimit;
1287
1661
  private checkSessionStartRateLimit;
@@ -1482,4 +1856,4 @@ declare class ConversationWatcher {
1482
1856
  private readNewLines;
1483
1857
  }
1484
1858
 
1485
- export { type AgentClient, type AgentClientOpts, type AgentConfig, type AppendArgs, type AskOption, type AskQuestion, CLAUDE_CODE_PROVIDER, CODEX_CLI_PROVIDER, type CacheAlertResolveAction, type ConversationListResponse, ConversationWatcher, type ConversationWriter, type DbConfig, type DiscoveredProcess, LiveSessionManager, type ManagedSession, PTYManager, type PTYManagerOptions, type PermissionOption, type ProcessLiveness, type ProgressDedupeLRU, type ProviderName, type ServerConfig, type ServerWarmingUpResponse, type ServerWarmupState, type SessionActivity, type SessionCursor, type SessionListPage, type SessionListQuery, type SessionOwnership, type SessionResponse, type SessionRunner, type SessionSortKey, type SessionStatus, SessionStore, type SortOrder, type StartFreshSessionOptions, type StartSessionOptions, StreamerServer, type UserMessage, WSHub, type WSMessage, createAgentClient, createConversationWriter, createPool, createProgressDedupeLRU, createProgressRoutes, discoverClaudeProcesses, generateApiKey, getDbConfig, isDbEnabled, isProviderName, isProviderResumable, loadOrCreateApiKey, maskConnectionString, readAgentConfig, validateApiKey };
1859
+ export { type AgentClient, type AgentClientOpts, type AgentConfig, type AppendArgs, type AskOption, type AskQuestion, CLAUDE_CODE_PROVIDER, CODEX_CLI_PROVIDER, type CacheAlertResolveAction, type ConversationListResponse, ConversationWatcher, type ConversationWriter, type DbConfig, type DiscoveredProcess, LiveSessionManager, type ManagedSession, PTYManager, type PTYManagerOptions, type PermissionOption, type ProcessLiveness, type ProgressDedupeLRU, type ProviderName, type ServerConfig, type ServerWarmingUpResponse, type ServerWarmupState, type SessionActivity, type SessionCursor, type SessionLifecycle, type SessionListPage, type SessionListQuery, type SessionOwnership, type SessionResponse, type SessionRunner, type SessionSortKey, type SessionStatus, SessionStore, type SortOrder, type StartFreshSessionOptions, type StartSessionOptions, type StatusConfidence, type StatusSource, StreamerServer, type UserMessage, WSHub, type WSMessage, confidenceForSource, createAgentClient, createConversationWriter, createPool, createProgressDedupeLRU, createProgressRoutes, discoverClaudeProcesses, generateApiKey, getDbConfig, isDbEnabled, isProviderName, isProviderResumable, loadOrCreateApiKey, maskConnectionString, readAgentConfig, validateApiKey };