@threadbase-sh/streamer 1.52.3 → 1.52.5

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.cjs CHANGED
@@ -6160,11 +6160,12 @@ function validateFeatureFlagValues(raw2) {
6160
6160
  const out = {};
6161
6161
  const dropped = [];
6162
6162
  for (const [id, value] of Object.entries(raw2)) {
6163
- if (!findFeatureFlag(id) || typeof value !== "boolean") {
6163
+ const def = findFeatureFlag(id);
6164
+ if (!def || typeof value !== "boolean") {
6164
6165
  dropped.push(id);
6165
6166
  continue;
6166
6167
  }
6167
- out[id] = value;
6168
+ out[def.id] = value;
6168
6169
  }
6169
6170
  if (dropped.length > 0) {
6170
6171
  getLogger("feature-flags").warn(
@@ -6184,7 +6185,8 @@ function parseFeatureFlagArgs(entries) {
6184
6185
  const eq = entry.indexOf("=");
6185
6186
  const id = (eq === -1 ? entry : entry.slice(0, eq)).trim();
6186
6187
  const rawValue = eq === -1 ? "true" : entry.slice(eq + 1).trim().toLowerCase();
6187
- if (!findFeatureFlag(id)) {
6188
+ const def = findFeatureFlag(id);
6189
+ if (!def) {
6188
6190
  errors.push(
6189
6191
  `Unknown feature flag "${id}". Known flags: ${FEATURE_FLAGS.map((f2) => f2.id).join(", ")}`
6190
6192
  );
@@ -6194,21 +6196,35 @@ function parseFeatureFlagArgs(entries) {
6194
6196
  errors.push(`Invalid value "${rawValue}" for feature flag "${id}" \u2014 expected true/false`);
6195
6197
  continue;
6196
6198
  }
6197
- values[id] = rawValue === "true";
6199
+ values[def.id] = rawValue === "true";
6198
6200
  }
6199
6201
  return { values, errors };
6200
6202
  }
6201
6203
  function resolveFeatureFlags(opts) {
6202
6204
  const env = opts?.env ?? process.env;
6203
- const out = {};
6205
+ const values = {};
6206
+ const sources = {};
6204
6207
  for (const def of FEATURE_FLAGS) {
6205
- out[def.id] = parseBooleanEnv(env[def.env]) ?? opts?.cli?.[def.id] ?? opts?.yaml?.[def.id] ?? def.default;
6208
+ const rungs = [
6209
+ ["override", opts?.override?.[def.id]],
6210
+ ["env", parseBooleanEnv(env[def.env])],
6211
+ ["cli", opts?.cli?.[def.id]],
6212
+ ["yaml", opts?.yaml?.[def.id]]
6213
+ ];
6214
+ const won = rungs.find(([, v2]) => v2 !== void 0);
6215
+ values[def.id] = won ? won[1] : def.default;
6216
+ sources[def.id] = won ? won[0] : "default";
6206
6217
  }
6207
- return out;
6218
+ return { values, sources };
6208
6219
  }
6209
6220
  function nonDefaultFeatureFlags(values) {
6210
6221
  return FEATURE_FLAGS.filter((f2) => values[f2.id] !== f2.default).map((f2) => f2.id);
6211
6222
  }
6223
+ function describeFeatureFlags(resolution) {
6224
+ return FEATURE_FLAGS.map(
6225
+ (f2) => `${f2.id}=${resolution.values[f2.id]}(${resolution.sources[f2.id]})`
6226
+ ).join(" ");
6227
+ }
6212
6228
  var FEATURE_FLAGS;
6213
6229
  var init_feature_flags = __esm({
6214
6230
  "src/feature-flags.ts"() {
@@ -151031,6 +151047,7 @@ function autoResumeSkipReason(row, opts) {
151031
151047
  if (opts.now - row.status_updated_at > AUTO_RESUME_WINDOW_MS) return "too_old";
151032
151048
  if (!opts.projectExists(row.project_path)) return "project_missing";
151033
151049
  if (resumeIdForRow(row) == null) return "resume_identity_missing";
151050
+ if (!opts.historyExists(row)) return "history_missing";
151034
151051
  return null;
151035
151052
  }
151036
151053
  function planAutoResume(rows, opts) {
@@ -151292,7 +151309,34 @@ var SessionRegistryBoot = class {
151292
151309
  /** Resume only the recent sessions the user explicitly allowed us to start at boot. */
151293
151310
  async autoResumePreviousSessions(rows) {
151294
151311
  if (!this.autoResumeOnBoot) return;
151295
- const plan = planAutoResume(rows, { now: Date.now(), projectExists: import_fs36.existsSync });
151312
+ const now = Date.now();
151313
+ const baseOptions = { now, projectExists: import_fs36.existsSync, historyExists: () => true };
151314
+ const historyExists = /* @__PURE__ */ new Map();
151315
+ const preflights = /* @__PURE__ */ new Set();
151316
+ for (const row of rows) {
151317
+ if (autoResumeSkipReason(row, baseOptions) != null) {
151318
+ historyExists.set(row.session_id, false);
151319
+ continue;
151320
+ }
151321
+ while (preflights.size >= AUTO_RESUME_CONCURRENCY) {
151322
+ await Promise.race(preflights);
151323
+ }
151324
+ const preflight = (async () => {
151325
+ try {
151326
+ const target = await this.deps.resolveConversationTarget(row.session_id);
151327
+ historyExists.set(row.session_id, target.ok || target.reason !== "history_file_missing");
151328
+ } catch {
151329
+ historyExists.set(row.session_id, true);
151330
+ }
151331
+ })();
151332
+ preflights.add(preflight);
151333
+ void preflight.then(() => preflights.delete(preflight));
151334
+ }
151335
+ await Promise.all(preflights);
151336
+ const plan = planAutoResume(rows, {
151337
+ ...baseOptions,
151338
+ historyExists: (row) => historyExists.get(row.session_id) ?? false
151339
+ });
151296
151340
  const skippedBy = {};
151297
151341
  for (const { row, reason } of plan.skipped) {
151298
151342
  skippedBy[reason] = (skippedBy[reason] ?? 0) + 1;
@@ -152290,6 +152334,10 @@ var StreamerServer = class {
152290
152334
  // Resolved once at boot; see src/feature-flags.ts. Total map — every registry
152291
152335
  // id is present, so indexing it never yields undefined.
152292
152336
  featureFlags;
152337
+ // Which rung of the precedence chain decided each flag. Reported at boot and
152338
+ // over GET /api/config/feature-flags — the resolved boolean alone cannot say
152339
+ // whether a value came from the environment, the CLI, server.yaml or nowhere.
152340
+ featureFlagSources;
152293
152341
  // Derived from featureFlags.codexSystemPrompt. Kept as its own field so the
152294
152342
  // read site in startFresh() is unchanged.
152295
152343
  codexSystemPromptEnabled;
@@ -152403,10 +152451,13 @@ var StreamerServer = class {
152403
152451
  this.codexRoots = config2.codexRoots ?? [(0, import_path33.join)((0, import_os15.homedir)(), ".codex", "sessions")];
152404
152452
  this.ptyGracePeriodMs = config2.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
152405
152453
  this.defaultSystemPrompt = config2.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
152406
- this.featureFlags = resolveFeatureFlags({ cli: config2.featureFlags, yaml: loadFeatureFlags() });
152407
- if (config2.codexSystemPromptEnabled !== void 0) {
152408
- this.featureFlags.codexSystemPrompt = config2.codexSystemPromptEnabled;
152409
- }
152454
+ const flagResolution = resolveFeatureFlags({
152455
+ override: config2.codexSystemPromptEnabled === void 0 ? void 0 : { codexSystemPrompt: config2.codexSystemPromptEnabled },
152456
+ cli: config2.featureFlags,
152457
+ yaml: loadFeatureFlags()
152458
+ });
152459
+ this.featureFlags = flagResolution.values;
152460
+ this.featureFlagSources = flagResolution.sources;
152410
152461
  this.codexSystemPromptEnabled = this.featureFlags.codexSystemPrompt;
152411
152462
  this.defaultPermissionMode = config2.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
152412
152463
  this.defaultModel = config2.defaultModel ?? "sonnet";
@@ -152447,17 +152498,17 @@ var StreamerServer = class {
152447
152498
  selfPtyEndedAt: this.selfPtyEndedAt,
152448
152499
  resumeSession: (opts) => this.sessionHandlers.resumeSession(opts),
152449
152500
  watchConversationFile: (sessionId, historyId) => this.sessionWatchers.watchConversationFile(sessionId, historyId),
152450
- broadcastSessionList: () => this.wsHub.broadcast(this.sessionListPayload())
152501
+ broadcastSessionList: () => this.wsHub.broadcast(this.sessionListPayload()),
152502
+ resolveConversationTarget: (sessionId) => this.resolveConversationTarget(sessionId)
152451
152503
  });
152452
152504
  this.includeAgents = parseIncludeAgentsEnv(process.env.THREADBASE_INCLUDE_AGENTS);
152453
152505
  this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
152454
- const enabledFlags = nonDefaultFeatureFlags(this.featureFlags);
152455
- if (enabledFlags.length > 0) {
152456
- this.log.info(`Feature flags active: ${enabledFlags.join(", ")}`, {
152457
- event: "config.feature_flags_active",
152458
- flags: enabledFlags
152459
- });
152460
- }
152506
+ this.log.info(`Feature flags: ${describeFeatureFlags(flagResolution)}`, {
152507
+ event: "config.feature_flags",
152508
+ values: this.featureFlags,
152509
+ sources: this.featureFlagSources,
152510
+ nonDefault: nonDefaultFeatureFlags(this.featureFlags)
152511
+ });
152461
152512
  const rawRoot = process.env.THREADBASE_BROWSE_ROOT ?? loadBrowseRoot() ?? config2.browseRoot;
152462
152513
  if (rawRoot) {
152463
152514
  (0, import_promises16.realpath)(rawRoot).then((resolved) => {
@@ -153480,7 +153531,11 @@ var StreamerServer = class {
153480
153531
  * the absence of that field is the signal that this endpoint is read-only.
153481
153532
  */
153482
153533
  getFeatureFlagsConfig() {
153483
- return { registry: FEATURE_FLAGS, values: this.featureFlags };
153534
+ return {
153535
+ registry: FEATURE_FLAGS,
153536
+ values: this.featureFlags,
153537
+ sources: this.featureFlagSources
153538
+ };
153484
153539
  }
153485
153540
  getClaudeFlagsConfig() {
153486
153541
  return {
@@ -153653,13 +153708,17 @@ var StreamerServer = class {
153653
153708
  conv = await this.conversationHandlers.findConversationByUuid(boundId);
153654
153709
  }
153655
153710
  }
153711
+ const cachedConvMeta = this.cache?.getMetaById(historyId);
153712
+ const cachedPath = cachedConvMeta?.filePath ? toNativeFilePath(cachedConvMeta.filePath) : null;
153713
+ const cachedCodexPath = (registryProvider === CODEX_CLI_PROVIDER || cachedConvMeta?.provider === CODEX_CLI_PROVIDER) && cachedPath != null && (0, import_fs38.existsSync)(cachedPath) ? cachedPath : null;
153656
153714
  const jsonlCwd = jsonlPath ? await this.conversationHandlers.readCwdFromJsonl(jsonlPath) : null;
153657
- const projectPath = jsonlCwd ?? conv?.projectPath;
153715
+ const projectPath = jsonlCwd ?? conv?.projectPath ?? (cachedCodexPath ? cachedConvMeta?.projectPath : null);
153658
153716
  if (!projectPath) {
153659
- if (!conv && !jsonlPath) return { ok: false, reason: "history_file_missing" };
153717
+ if (!conv && !jsonlPath && !cachedCodexPath) {
153718
+ return { ok: false, reason: "history_file_missing" };
153719
+ }
153660
153720
  return { ok: false, reason: "no_project_path" };
153661
153721
  }
153662
- const cachedConvMeta = this.cache?.getMetaById(historyId);
153663
153722
  const provider = coerceProviderForRunner(
153664
153723
  conv?.provider ?? cachedConvMeta?.provider ?? registryProvider
153665
153724
  );
@@ -153673,7 +153732,7 @@ var StreamerServer = class {
153673
153732
  // conversation. Kept separate from `jsonlPath` deliberately: feeding it to
153674
153733
  // conversationBusy() would newly arm the mtime heuristic for Codex, which
153675
153734
  // is exactly the over-broad signal the report ruled out.
153676
- historyPath: jsonlPath ?? conv?.filePath ?? null,
153735
+ historyPath: jsonlPath ?? conv?.filePath ?? cachedCodexPath ?? null,
153677
153736
  conv,
153678
153737
  projectPath,
153679
153738
  provider
@@ -157684,7 +157743,7 @@ async function runProdDoctor(opts, deps = {}) {
157684
157743
  );
157685
157744
  }
157686
157745
  }
157687
- const featureFlags = deps.featureFlags ?? resolveFeatureFlags({ yaml: loadFeatureFlags() });
157746
+ const featureFlags = deps.featureFlags ?? resolveFeatureFlags({ yaml: loadFeatureFlags() }).values;
157688
157747
  if (featureFlags.ptyHost) {
157689
157748
  const probe = deps.probePtyHost ?? (() => probePtyHostStatus(hostSocketPath(getInstanceId())));
157690
157749
  try {
@@ -157705,6 +157764,33 @@ async function runProdDoctor(opts, deps = {}) {
157705
157764
  function toPowerShellLiteral(value) {
157706
157765
  return `'${value.replace(/'/g, "''")}'`;
157707
157766
  }
157767
+ function windowsFollowScript(paths, lines) {
157768
+ const list = `@(${paths.map(toPowerShellLiteral).join(", ")})`;
157769
+ return [
157770
+ "$ErrorActionPreference = 'Stop'",
157771
+ `$paths = ${list}`,
157772
+ `foreach ($f in $paths) { Get-Content -LiteralPath $f -Tail ${lines} }`,
157773
+ "$offsets = @{}",
157774
+ "foreach ($f in $paths) { $offsets[$f] = (Get-Item -LiteralPath $f).Length }",
157775
+ "while ($true) {",
157776
+ " foreach ($f in $paths) {",
157777
+ " try {",
157778
+ " $len = (Get-Item -LiteralPath $f).Length",
157779
+ " if ($len -lt $offsets[$f]) { $offsets[$f] = 0 }",
157780
+ " if ($len -gt $offsets[$f]) {",
157781
+ " $stream = [IO.File]::Open($f, 'Open', 'Read', 'ReadWrite')",
157782
+ " [void]$stream.Seek($offsets[$f], 'Begin')",
157783
+ " $reader = New-Object IO.StreamReader($stream)",
157784
+ " $reader.ReadToEnd()",
157785
+ " $reader.Dispose(); $stream.Dispose()",
157786
+ " $offsets[$f] = $len",
157787
+ " }",
157788
+ " } catch { }",
157789
+ " }",
157790
+ " Start-Sleep -Milliseconds 400",
157791
+ "}"
157792
+ ].join("; ");
157793
+ }
157708
157794
  var defaultSpawnTail = ({ files, lines, follow }) => {
157709
157795
  const existing = files.filter((f2) => (0, import_node_fs23.existsSync)(f2));
157710
157796
  if (existing.length === 0) {
@@ -157719,13 +157805,17 @@ var defaultSpawnTail = ({ files, lines, follow }) => {
157719
157805
  "-NoProfile",
157720
157806
  "-NonInteractive",
157721
157807
  "-Command",
157722
- `Get-Content -LiteralPath @(${existing.map(toPowerShellLiteral).join(", ")}) -Tail ${lines}${follow ? " -Wait" : ""}`
157808
+ follow ? windowsFollowScript(existing, lines) : `$ErrorActionPreference = 'Stop'; Get-Content -LiteralPath @(${existing.map(toPowerShellLiteral).join(", ")}) -Tail ${lines}`
157723
157809
  ] : ["-n", String(lines), ...follow ? ["-F"] : [], ...existing];
157810
+ const program3 = isWindows3 ? "powershell.exe" : "tail";
157724
157811
  return new Promise((resolve4) => {
157725
- const child = (0, import_node_child_process8.spawn)(isWindows3 ? "powershell.exe" : "tail", args, {
157812
+ const child = (0, import_node_child_process8.spawn)(program3, args, {
157726
157813
  stdio: ["ignore", "inherit", "inherit"]
157727
157814
  });
157728
- child.on("error", (err) => resolve4({ ok: false, message: `tail failed: ${err.message}` }));
157815
+ child.on(
157816
+ "error",
157817
+ (err) => resolve4({ ok: false, message: `${program3} failed: ${err.message}` })
157818
+ );
157729
157819
  child.on("exit", (code) => resolve4({ ok: code === 0 }));
157730
157820
  });
157731
157821
  };