@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/index.cjs CHANGED
@@ -537,11 +537,12 @@ function validateFeatureFlagValues(raw) {
537
537
  const out = {};
538
538
  const dropped = [];
539
539
  for (const [id, value] of Object.entries(raw)) {
540
- if (!findFeatureFlag(id) || typeof value !== "boolean") {
540
+ const def = findFeatureFlag(id);
541
+ if (!def || typeof value !== "boolean") {
541
542
  dropped.push(id);
542
543
  continue;
543
544
  }
544
- out[id] = value;
545
+ out[def.id] = value;
545
546
  }
546
547
  if (dropped.length > 0) {
547
548
  getLogger("feature-flags").warn(
@@ -556,15 +557,29 @@ function validateFeatureFlagValues(raw) {
556
557
  }
557
558
  function resolveFeatureFlags(opts) {
558
559
  const env = opts?.env ?? process.env;
559
- const out = {};
560
+ const values = {};
561
+ const sources = {};
560
562
  for (const def of FEATURE_FLAGS) {
561
- out[def.id] = parseBooleanEnv(env[def.env]) ?? opts?.cli?.[def.id] ?? opts?.yaml?.[def.id] ?? def.default;
563
+ const rungs = [
564
+ ["override", opts?.override?.[def.id]],
565
+ ["env", parseBooleanEnv(env[def.env])],
566
+ ["cli", opts?.cli?.[def.id]],
567
+ ["yaml", opts?.yaml?.[def.id]]
568
+ ];
569
+ const won = rungs.find(([, v]) => v !== void 0);
570
+ values[def.id] = won ? won[1] : def.default;
571
+ sources[def.id] = won ? won[0] : "default";
562
572
  }
563
- return out;
573
+ return { values, sources };
564
574
  }
565
575
  function nonDefaultFeatureFlags(values) {
566
576
  return FEATURE_FLAGS.filter((f) => values[f.id] !== f.default).map((f) => f.id);
567
577
  }
578
+ function describeFeatureFlags(resolution) {
579
+ return FEATURE_FLAGS.map(
580
+ (f) => `${f.id}=${resolution.values[f.id]}(${resolution.sources[f.id]})`
581
+ ).join(" ");
582
+ }
568
583
 
569
584
  // src/auth.ts
570
585
  function configDir() {
@@ -13267,6 +13282,7 @@ function autoResumeSkipReason(row, opts) {
13267
13282
  if (opts.now - row.status_updated_at > AUTO_RESUME_WINDOW_MS) return "too_old";
13268
13283
  if (!opts.projectExists(row.project_path)) return "project_missing";
13269
13284
  if (resumeIdForRow(row) == null) return "resume_identity_missing";
13285
+ if (!opts.historyExists(row)) return "history_missing";
13270
13286
  return null;
13271
13287
  }
13272
13288
  function planAutoResume(rows, opts) {
@@ -13528,7 +13544,34 @@ var SessionRegistryBoot = class {
13528
13544
  /** Resume only the recent sessions the user explicitly allowed us to start at boot. */
13529
13545
  async autoResumePreviousSessions(rows) {
13530
13546
  if (!this.autoResumeOnBoot) return;
13531
- const plan = planAutoResume(rows, { now: Date.now(), projectExists: import_fs25.existsSync });
13547
+ const now = Date.now();
13548
+ const baseOptions = { now, projectExists: import_fs25.existsSync, historyExists: () => true };
13549
+ const historyExists = /* @__PURE__ */ new Map();
13550
+ const preflights = /* @__PURE__ */ new Set();
13551
+ for (const row of rows) {
13552
+ if (autoResumeSkipReason(row, baseOptions) != null) {
13553
+ historyExists.set(row.session_id, false);
13554
+ continue;
13555
+ }
13556
+ while (preflights.size >= AUTO_RESUME_CONCURRENCY) {
13557
+ await Promise.race(preflights);
13558
+ }
13559
+ const preflight = (async () => {
13560
+ try {
13561
+ const target = await this.deps.resolveConversationTarget(row.session_id);
13562
+ historyExists.set(row.session_id, target.ok || target.reason !== "history_file_missing");
13563
+ } catch {
13564
+ historyExists.set(row.session_id, true);
13565
+ }
13566
+ })();
13567
+ preflights.add(preflight);
13568
+ void preflight.then(() => preflights.delete(preflight));
13569
+ }
13570
+ await Promise.all(preflights);
13571
+ const plan = planAutoResume(rows, {
13572
+ ...baseOptions,
13573
+ historyExists: (row) => historyExists.get(row.session_id) ?? false
13574
+ });
13532
13575
  const skippedBy = {};
13533
13576
  for (const { row, reason } of plan.skipped) {
13534
13577
  skippedBy[reason] = (skippedBy[reason] ?? 0) + 1;
@@ -14496,6 +14539,10 @@ var StreamerServer = class {
14496
14539
  // Resolved once at boot; see src/feature-flags.ts. Total map — every registry
14497
14540
  // id is present, so indexing it never yields undefined.
14498
14541
  featureFlags;
14542
+ // Which rung of the precedence chain decided each flag. Reported at boot and
14543
+ // over GET /api/config/feature-flags — the resolved boolean alone cannot say
14544
+ // whether a value came from the environment, the CLI, server.yaml or nowhere.
14545
+ featureFlagSources;
14499
14546
  // Derived from featureFlags.codexSystemPrompt. Kept as its own field so the
14500
14547
  // read site in startFresh() is unchanged.
14501
14548
  codexSystemPromptEnabled;
@@ -14609,10 +14656,13 @@ var StreamerServer = class {
14609
14656
  this.codexRoots = config.codexRoots ?? [(0, import_path22.join)((0, import_os12.homedir)(), ".codex", "sessions")];
14610
14657
  this.ptyGracePeriodMs = config.ptyGracePeriodMs ?? DEFAULT_PTY_GRACE_PERIOD_MS;
14611
14658
  this.defaultSystemPrompt = config.defaultSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
14612
- this.featureFlags = resolveFeatureFlags({ cli: config.featureFlags, yaml: loadFeatureFlags() });
14613
- if (config.codexSystemPromptEnabled !== void 0) {
14614
- this.featureFlags.codexSystemPrompt = config.codexSystemPromptEnabled;
14615
- }
14659
+ const flagResolution = resolveFeatureFlags({
14660
+ override: config.codexSystemPromptEnabled === void 0 ? void 0 : { codexSystemPrompt: config.codexSystemPromptEnabled },
14661
+ cli: config.featureFlags,
14662
+ yaml: loadFeatureFlags()
14663
+ });
14664
+ this.featureFlags = flagResolution.values;
14665
+ this.featureFlagSources = flagResolution.sources;
14616
14666
  this.codexSystemPromptEnabled = this.featureFlags.codexSystemPrompt;
14617
14667
  this.defaultPermissionMode = config.defaultPermissionMode ?? loadDefaultPermissionMode() ?? "acceptEdits";
14618
14668
  this.defaultModel = config.defaultModel ?? "sonnet";
@@ -14653,17 +14703,17 @@ var StreamerServer = class {
14653
14703
  selfPtyEndedAt: this.selfPtyEndedAt,
14654
14704
  resumeSession: (opts) => this.sessionHandlers.resumeSession(opts),
14655
14705
  watchConversationFile: (sessionId, historyId) => this.sessionWatchers.watchConversationFile(sessionId, historyId),
14656
- broadcastSessionList: () => this.wsHub.broadcast(this.sessionListPayload())
14706
+ broadcastSessionList: () => this.wsHub.broadcast(this.sessionListPayload()),
14707
+ resolveConversationTarget: (sessionId) => this.resolveConversationTarget(sessionId)
14657
14708
  });
14658
14709
  this.includeAgents = parseIncludeAgentsEnv(process.env.THREADBASE_INCLUDE_AGENTS);
14659
14710
  this.agentEntrypoints = parseAgentEntrypointsEnv(process.env.THREADBASE_AGENT_ENTRYPOINTS);
14660
- const enabledFlags = nonDefaultFeatureFlags(this.featureFlags);
14661
- if (enabledFlags.length > 0) {
14662
- this.log.info(`Feature flags active: ${enabledFlags.join(", ")}`, {
14663
- event: "config.feature_flags_active",
14664
- flags: enabledFlags
14665
- });
14666
- }
14711
+ this.log.info(`Feature flags: ${describeFeatureFlags(flagResolution)}`, {
14712
+ event: "config.feature_flags",
14713
+ values: this.featureFlags,
14714
+ sources: this.featureFlagSources,
14715
+ nonDefault: nonDefaultFeatureFlags(this.featureFlags)
14716
+ });
14667
14717
  const rawRoot = process.env.THREADBASE_BROWSE_ROOT ?? loadBrowseRoot() ?? config.browseRoot;
14668
14718
  if (rawRoot) {
14669
14719
  (0, import_promises7.realpath)(rawRoot).then((resolved) => {
@@ -15686,7 +15736,11 @@ var StreamerServer = class {
15686
15736
  * the absence of that field is the signal that this endpoint is read-only.
15687
15737
  */
15688
15738
  getFeatureFlagsConfig() {
15689
- return { registry: FEATURE_FLAGS, values: this.featureFlags };
15739
+ return {
15740
+ registry: FEATURE_FLAGS,
15741
+ values: this.featureFlags,
15742
+ sources: this.featureFlagSources
15743
+ };
15690
15744
  }
15691
15745
  getClaudeFlagsConfig() {
15692
15746
  return {
@@ -15859,13 +15913,17 @@ var StreamerServer = class {
15859
15913
  conv = await this.conversationHandlers.findConversationByUuid(boundId);
15860
15914
  }
15861
15915
  }
15916
+ const cachedConvMeta = this.cache?.getMetaById(historyId);
15917
+ const cachedPath = cachedConvMeta?.filePath ? toNativeFilePath(cachedConvMeta.filePath) : null;
15918
+ const cachedCodexPath = (registryProvider === CODEX_CLI_PROVIDER || cachedConvMeta?.provider === CODEX_CLI_PROVIDER) && cachedPath != null && (0, import_fs27.existsSync)(cachedPath) ? cachedPath : null;
15862
15919
  const jsonlCwd = jsonlPath ? await this.conversationHandlers.readCwdFromJsonl(jsonlPath) : null;
15863
- const projectPath = jsonlCwd ?? conv?.projectPath;
15920
+ const projectPath = jsonlCwd ?? conv?.projectPath ?? (cachedCodexPath ? cachedConvMeta?.projectPath : null);
15864
15921
  if (!projectPath) {
15865
- if (!conv && !jsonlPath) return { ok: false, reason: "history_file_missing" };
15922
+ if (!conv && !jsonlPath && !cachedCodexPath) {
15923
+ return { ok: false, reason: "history_file_missing" };
15924
+ }
15866
15925
  return { ok: false, reason: "no_project_path" };
15867
15926
  }
15868
- const cachedConvMeta = this.cache?.getMetaById(historyId);
15869
15927
  const provider = coerceProviderForRunner(
15870
15928
  conv?.provider ?? cachedConvMeta?.provider ?? registryProvider
15871
15929
  );
@@ -15879,7 +15937,7 @@ var StreamerServer = class {
15879
15937
  // conversation. Kept separate from `jsonlPath` deliberately: feeding it to
15880
15938
  // conversationBusy() would newly arm the mtime heuristic for Codex, which
15881
15939
  // is exactly the over-broad signal the report ruled out.
15882
- historyPath: jsonlPath ?? conv?.filePath ?? null,
15940
+ historyPath: jsonlPath ?? conv?.filePath ?? cachedCodexPath ?? null,
15883
15941
  conv,
15884
15942
  projectPath,
15885
15943
  provider