@codemem/server 0.23.0 → 0.25.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/static/app.js CHANGED
@@ -309,6 +309,16 @@
309
309
  async function loadSummariesPage(project, options) {
310
310
  return fetchJson(`/api/summaries?${buildProjectParams(project, options?.limit, options?.offset, options?.scope)}`);
311
311
  }
312
+ async function tracePack(payload) {
313
+ const resp = await fetch("/api/pack/trace", {
314
+ method: "POST",
315
+ headers: { "Content-Type": "application/json" },
316
+ body: JSON.stringify(payload)
317
+ });
318
+ const { text, payload: data } = await readJsonPayload(resp);
319
+ if (!resp.ok) throw new Error(payloadError(data) || text || "request failed");
320
+ return data;
321
+ }
312
322
  async function loadObserverStatus() {
313
323
  return fetchJson("/api/observer-status");
314
324
  }
@@ -1329,14 +1339,14 @@
1329
1339
  }
1330
1340
  function observationViewData(item) {
1331
1341
  const metadata = mergeMetadata(item?.metadata_json);
1332
- const summary = String(item?.subtitle || item?.body_text || "").trim();
1333
- const narrative = String(item?.narrative || metadata?.narrative || "").trim();
1342
+ const summary = String(item?.subtitle || metadata?.subtitle || "").trim();
1343
+ const narrative = String(item?.narrative || metadata?.narrative || item?.body_text || "").trim();
1334
1344
  const normSummary = normalize(summary);
1335
1345
  const normNarrative = normalize(narrative);
1336
1346
  const narrativeDistinct = Boolean(narrative) && normNarrative !== normSummary;
1337
1347
  const explicitFacts = parseJsonArray(item?.facts || metadata?.facts || []);
1338
- const fallbackFacts = explicitFacts.length ? explicitFacts : extractFactsFromBody(summary || narrative);
1339
- const derivedFacts = fallbackFacts.length ? fallbackFacts : sentenceFacts(summary);
1348
+ const fallbackFacts = explicitFacts.length ? explicitFacts : extractFactsFromBody(narrative || summary);
1349
+ const derivedFacts = fallbackFacts.length ? fallbackFacts : sentenceFacts(narrative || summary);
1340
1350
  return {
1341
1351
  summary,
1342
1352
  narrative,
@@ -1766,6 +1776,138 @@
1766
1776
  }, label);
1767
1777
  }));
1768
1778
  }
1779
+ function TraceCandidateGroup({ label, candidates }) {
1780
+ if (candidates.length === 0) return null;
1781
+ return _$1("div", { className: "trace-group" }, _$1("div", { className: "section-meta" }, label), _$1("div", { className: "feed-list" }, candidates.map((candidate) => _$1("div", {
1782
+ className: "feed-card",
1783
+ key: `${label}:${candidate.id}`
1784
+ }, _$1("div", { className: "feed-card-header" }, [_$1("div", { className: "feed-card-title" }, `${candidate.rank}. ${candidate.title}`), _$1("div", { className: "feed-card-meta" }, `#${candidate.id} · ${candidate.kind}${candidate.section ? ` · ${candidate.section}` : ""}`)]), _$1("div", { className: "feed-card-body" }, [_$1("div", null, candidate.preview || "No preview available."), candidate.reasons.length ? _$1("div", { className: "section-meta" }, `Reasons: ${candidate.reasons.join(", ")}`) : null])))));
1785
+ }
1786
+ function ContextInspectorPanel() {
1787
+ const [open, setOpen] = d(false);
1788
+ const [workingSetText, setWorkingSetText] = d("");
1789
+ const [loading, setLoading] = d(false);
1790
+ const [error, setError] = d("");
1791
+ const [errorContextKey, setErrorContextKey] = d("");
1792
+ const [trace, setTrace] = d(null);
1793
+ const currentQuery = String(state.feedQuery || "").trim();
1794
+ const currentProject = state.currentProject || null;
1795
+ const currentContextKey = JSON.stringify([currentProject, currentQuery]);
1796
+ const visibleTrace = trace && trace.inputs.query === currentQuery && trace.inputs.project === currentProject ? trace : null;
1797
+ const visibleError = error && errorContextKey === currentContextKey ? error : "";
1798
+ y(() => {
1799
+ setError("");
1800
+ setErrorContextKey("");
1801
+ setTrace((currentTrace) => {
1802
+ if (!currentTrace) return null;
1803
+ if (currentTrace.inputs.query !== currentQuery || currentTrace.inputs.project !== currentProject) return null;
1804
+ return currentTrace;
1805
+ });
1806
+ }, [currentProject, currentQuery]);
1807
+ const runTrace = async () => {
1808
+ const context = currentQuery;
1809
+ const project = currentProject;
1810
+ const contextKey = JSON.stringify([project, context]);
1811
+ if (!context) {
1812
+ setError("Enter a query to inspect.");
1813
+ setErrorContextKey(contextKey);
1814
+ setTrace(null);
1815
+ return;
1816
+ }
1817
+ setLoading(true);
1818
+ setError("");
1819
+ setErrorContextKey("");
1820
+ try {
1821
+ const nextTrace = await tracePack({
1822
+ context,
1823
+ project,
1824
+ working_set_files: workingSetText.split(/\n|,/).map((value) => value.trim()).filter(Boolean)
1825
+ });
1826
+ if (String(state.feedQuery || "").trim() !== context || (state.currentProject || null) !== project) return;
1827
+ setTrace(nextTrace);
1828
+ } catch (err) {
1829
+ if (String(state.feedQuery || "").trim() !== context || (state.currentProject || null) !== project) return;
1830
+ setError(err instanceof Error ? err.message : String(err));
1831
+ setErrorContextKey(contextKey);
1832
+ setTrace(null);
1833
+ } finally {
1834
+ setLoading(false);
1835
+ }
1836
+ };
1837
+ const selected = visibleTrace?.retrieval.candidates.filter((candidate) => candidate.disposition === "selected") || [];
1838
+ const dropped = visibleTrace?.retrieval.candidates.filter((candidate) => candidate.disposition === "dropped") || [];
1839
+ const deduped = visibleTrace?.retrieval.candidates.filter((candidate) => candidate.disposition === "deduped") || [];
1840
+ const trimmed = visibleTrace?.retrieval.candidates.filter((candidate) => candidate.disposition === "trimmed") || [];
1841
+ return _$1("div", { className: "feed-inspector" }, _$1("button", {
1842
+ className: "toggle-button",
1843
+ onClick: () => setOpen(!open),
1844
+ type: "button"
1845
+ }, open ? "Hide Context Inspector" : "Open Context Inspector"), open ? _$1("div", {
1846
+ className: "feed-card",
1847
+ style: "margin-top:12px;"
1848
+ }, [
1849
+ _$1("div", { className: "feed-card-header" }, [_$1("div", { className: "feed-card-title" }, "Context Inspector"), _$1("div", { className: "feed-card-meta" }, "Tracing the current Search query")]),
1850
+ _$1("div", { className: "feed-card-body" }, [
1851
+ _$1("input", {
1852
+ className: "feed-search",
1853
+ onInput: (event) => {
1854
+ state.feedQuery = String(event.currentTarget.value || "");
1855
+ setTrace(null);
1856
+ setError("");
1857
+ setErrorContextKey("");
1858
+ updateFeedView();
1859
+ },
1860
+ placeholder: "Trace a pack query…",
1861
+ value: state.feedQuery
1862
+ }),
1863
+ _$1("textarea", {
1864
+ onInput: (event) => setWorkingSetText(String(event.currentTarget.value || "")),
1865
+ placeholder: "Optional working-set files, one per line",
1866
+ rows: 3,
1867
+ style: "margin-top:8px; width:100%;",
1868
+ value: workingSetText
1869
+ }),
1870
+ _$1("div", { style: "display:flex; gap:8px; margin-top:8px; align-items:center;" }, _$1("button", {
1871
+ className: "toggle-button active",
1872
+ disabled: loading,
1873
+ onClick: () => void runTrace(),
1874
+ type: "button"
1875
+ }, loading ? "Tracing…" : "Run trace"), visibleTrace ? _$1("span", { className: "section-meta" }, `mode=${visibleTrace.mode.selected} · candidates=${visibleTrace.retrieval.candidate_count} · tokens=${visibleTrace.output.estimated_tokens}`) : null),
1876
+ trace && !visibleTrace ? _$1("div", {
1877
+ className: "section-meta",
1878
+ style: "margin-top:8px;"
1879
+ }, "Search context changed. Run trace again for the current query and project.") : null,
1880
+ visibleError ? _$1("div", {
1881
+ className: "section-meta",
1882
+ style: "margin-top:8px; color:#d96c6c;"
1883
+ }, visibleError) : null
1884
+ ]),
1885
+ visibleTrace ? _$1(k$2, null, [
1886
+ _$1(TraceCandidateGroup, {
1887
+ label: "Selected",
1888
+ candidates: selected
1889
+ }),
1890
+ _$1(TraceCandidateGroup, {
1891
+ label: "Dropped",
1892
+ candidates: dropped
1893
+ }),
1894
+ _$1(TraceCandidateGroup, {
1895
+ label: "Deduped",
1896
+ candidates: deduped
1897
+ }),
1898
+ _$1(TraceCandidateGroup, {
1899
+ label: "Trimmed",
1900
+ candidates: trimmed
1901
+ }),
1902
+ _$1("div", { className: "feed-card-body" }, [
1903
+ visibleTrace.assembly.collapsed_groups.length ? _$1("div", { className: "section-meta" }, `Collapsed duplicates: ${visibleTrace.assembly.collapsed_groups.map((group) => `kept #${group.kept} from [${group.dropped.join(", ")}]`).join(" · ")}`) : null,
1904
+ visibleTrace.assembly.trim_reasons.length ? _$1("div", { className: "section-meta" }, `Trim reasons: ${visibleTrace.assembly.trim_reasons.join(", ")}`) : null,
1905
+ _$1("div", { className: "section-meta" }, "Final pack"),
1906
+ _$1("pre", { style: "white-space:pre-wrap; overflow:auto;" }, visibleTrace.output.pack_text)
1907
+ ])
1908
+ ]) : null
1909
+ ]) : null);
1910
+ }
1769
1911
  function FeedTabView({ items, loadingText }) {
1770
1912
  return _$1(k$2, null, _$1("div", { className: "feed-controls" }, _$1("div", {
1771
1913
  className: "section-meta",
@@ -1779,7 +1921,7 @@
1779
1921
  },
1780
1922
  placeholder: "Search title, body, tags…",
1781
1923
  value: state.feedQuery
1782
- }), _$1(FeedToggle, {
1924
+ }), _$1(ContextInspectorPanel, {}), _$1(FeedToggle, {
1783
1925
  active: state.feedScopeFilter,
1784
1926
  id: "feedScopeToggle",
1785
1927
  onSelect: (value) => {
@@ -2016,6 +2158,7 @@
2016
2158
  const usagePayload = state.lastUsagePayload || {};
2017
2159
  const raw = state.lastRawEventsPayload && typeof state.lastRawEventsPayload === "object" ? state.lastRawEventsPayload : {};
2018
2160
  const syncStatus = state.lastSyncStatus || {};
2161
+ const maintenanceJobs = Array.isArray(stats.maintenance_jobs) ? stats.maintenance_jobs : [];
2019
2162
  const reliability = stats.reliability || {};
2020
2163
  const counts = reliability.counts || {};
2021
2164
  const rates = reliability.rates || {};
@@ -2121,6 +2264,23 @@
2121
2264
  const pipelineDetail = rawPending > 0 ? "Queue is actively draining" : "Queue is clear";
2122
2265
  const syncDetail = syncDisabled ? "Sync disabled" : syncNoPeers ? "No peers configured" : syncOfflinePeers ? `${peerCount} peers offline · last sync ${formatAgeShort(syncAgeSeconds)} ago` : `${peerCount} peers · last sync ${formatAgeShort(syncAgeSeconds)} ago`;
2123
2266
  const freshnessDetail = `last pack ${formatAgeShort(packAgeSeconds)} ago`;
2267
+ const maintenanceCards = maintenanceJobs.map((job) => {
2268
+ const current = Number(job.progress?.current || 0);
2269
+ const total = typeof job.progress?.total === "number" ? job.progress.total : null;
2270
+ const unit = String(job.progress?.unit || "items");
2271
+ const pct = total && total > 0 ? ` (${Math.round(100 * current / total)}%)` : "";
2272
+ const progress = total && total > 0 ? `${current.toLocaleString()}/${total.toLocaleString()} ${unit}${pct}` : `${current.toLocaleString()} ${unit}`;
2273
+ const isFailed = job.status === "failed";
2274
+ const detail = isFailed ? String(job.error || "unknown error").trim() : void 0;
2275
+ return buildHealthCard({
2276
+ label: String(job.title || job.kind || "Background maintenance"),
2277
+ value: isFailed ? "Failed" : progress,
2278
+ detail,
2279
+ icon: isFailed ? "alert-triangle" : "loader",
2280
+ className: isFailed ? "status-attention" : void 0,
2281
+ title: isFailed ? `Error: ${job.error || "unknown"}` : `${String(job.title || "Maintenance")} in progress`
2282
+ });
2283
+ });
2124
2284
  renderHealthCards(healthGrid, [
2125
2285
  buildHealthCard({
2126
2286
  label: "Overall health",
@@ -2130,6 +2290,7 @@
2130
2290
  className: `health-primary ${statusClass}`,
2131
2291
  title: drivers.length ? `Main signals: ${drivers.join(", ")}` : "No major risk signals detected"
2132
2292
  }),
2293
+ ...maintenanceCards,
2133
2294
  buildHealthCard({
2134
2295
  label: "Pipeline health",
2135
2296
  value: `${rawPending.toLocaleString()} pending`,
@@ -11740,6 +11901,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11740
11901
  claudeCommand: "claude_command",
11741
11902
  observerProvider: "observer_provider",
11742
11903
  observerModel: "observer_model",
11904
+ observerTierRoutingEnabled: "observer_tier_routing_enabled",
11905
+ observerSimpleModel: "observer_simple_model",
11906
+ observerSimpleTemperature: "observer_simple_temperature",
11907
+ observerRichModel: "observer_rich_model",
11908
+ observerRichTemperature: "observer_rich_temperature",
11909
+ observerRichOpenAIUseResponses: "observer_rich_openai_use_responses",
11910
+ observerRichReasoningEffort: "observer_rich_reasoning_effort",
11911
+ observerRichReasoningSummary: "observer_rich_reasoning_summary",
11912
+ observerRichMaxOutputTokens: "observer_rich_max_output_tokens",
11743
11913
  observerRuntime: "observer_runtime",
11744
11914
  observerAuthSource: "observer_auth_source",
11745
11915
  observerAuthFile: "observer_auth_file",
@@ -11781,6 +11951,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11781
11951
  claudeCommand: "",
11782
11952
  observerProvider: "",
11783
11953
  observerModel: "",
11954
+ observerTierRoutingEnabled: false,
11955
+ observerSimpleModel: "",
11956
+ observerSimpleTemperature: "",
11957
+ observerRichModel: "",
11958
+ observerRichTemperature: "",
11959
+ observerRichOpenAIUseResponses: false,
11960
+ observerRichReasoningEffort: "",
11961
+ observerRichReasoningSummary: "",
11962
+ observerRichMaxOutputTokens: "",
11784
11963
  observerRuntime: "api_http",
11785
11964
  observerAuthSource: "auto",
11786
11965
  observerAuthFile: "",
@@ -11826,6 +12005,27 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11826
12005
  if (value === void 0 || value === null) return "";
11827
12006
  return String(value);
11828
12007
  }
12008
+ function asBooleanValue(value) {
12009
+ if (typeof value === "boolean") return value;
12010
+ if (typeof value === "number") return value !== 0;
12011
+ if (typeof value === "string") {
12012
+ const normalized = value.trim().toLowerCase();
12013
+ if (!normalized) return false;
12014
+ if ([
12015
+ "0",
12016
+ "false",
12017
+ "no",
12018
+ "off"
12019
+ ].includes(normalized)) return false;
12020
+ if ([
12021
+ "1",
12022
+ "true",
12023
+ "yes",
12024
+ "on"
12025
+ ].includes(normalized)) return true;
12026
+ }
12027
+ return Boolean(value);
12028
+ }
11829
12029
  function toProviderList(value) {
11830
12030
  if (!Array.isArray(value)) return [];
11831
12031
  return value.filter((item) => typeof item === "string" && item.trim().length > 0);
@@ -11879,6 +12079,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11879
12079
  }
11880
12080
  case "observer_provider":
11881
12081
  case "observer_model":
12082
+ case "observer_simple_model":
12083
+ case "observer_rich_model":
12084
+ case "observer_rich_reasoning_effort":
12085
+ case "observer_rich_reasoning_summary":
11882
12086
  case "observer_auth_file":
11883
12087
  case "sync_host":
11884
12088
  case "sync_coordinator_url":
@@ -11901,6 +12105,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11901
12105
  }
11902
12106
  case "observer_auth_timeout_ms":
11903
12107
  case "observer_max_chars":
12108
+ case "observer_simple_temperature":
12109
+ case "observer_rich_temperature":
12110
+ case "observer_rich_max_output_tokens":
11904
12111
  case "pack_observation_limit":
11905
12112
  case "pack_session_limit":
11906
12113
  case "raw_events_sweeper_interval_s":
@@ -11922,7 +12129,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11922
12129
  return Number.isFinite(parsed) ? parsed : "";
11923
12130
  }
11924
12131
  case "sync_enabled":
11925
- case "sync_mdns": return Boolean(config?.[key]);
12132
+ case "sync_mdns":
12133
+ case "observer_tier_routing_enabled":
12134
+ case "observer_rich_openai_use_responses": return asBooleanValue(config?.[key]);
11926
12135
  default: return hasOwn(config, key) ? config[key] : "";
11927
12136
  }
11928
12137
  }
@@ -11935,6 +12144,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11935
12144
  }
11936
12145
  function getObserverModelHint() {
11937
12146
  const values = settingsRenderState.values;
12147
+ if (values.observerTierRoutingEnabled) return "Tiered routing is enabled: simple/rich model selection now lives in Processing.";
11938
12148
  const inferred = inferObserverModel(values.observerRuntime.trim() || "api_http", values.observerProvider.trim(), normalizeTextValue(values.observerModel));
11939
12149
  return `${[
11940
12150
  "observer_model",
@@ -11942,6 +12152,19 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
11942
12152
  "observer_runtime"
11943
12153
  ].some((key) => hasOwn(settingsEnvOverrides, key)) ? "Env override" : inferred.source}: ${inferred.model}`;
11944
12154
  }
12155
+ function getTieredRoutingHelperText() {
12156
+ if (!settingsRenderState.values.observerTierRoutingEnabled) return "Off: codemem uses the base observer settings from the Connection tab for all batches.";
12157
+ return "On: codemem can route simpler batches to a lighter model and richer batches to a higher-quality configuration.";
12158
+ }
12159
+ function getObserverModelLabel() {
12160
+ return settingsRenderState.values.observerTierRoutingEnabled ? "Base model fallback" : "Model";
12161
+ }
12162
+ function getObserverModelTooltip() {
12163
+ return settingsRenderState.values.observerTierRoutingEnabled ? "Tiered routing is enabled, so Processing controls the simple/rich models. This base model is only a fallback." : "Leave blank to use a recommended model for your selected mode/provider.";
12164
+ }
12165
+ function getObserverModelDescription() {
12166
+ return settingsRenderState.values.observerTierRoutingEnabled ? "Tiered routing is active. Use this only as a fallback while the Processing tab owns simple/rich model selection." : "Default: `gpt-5.1-codex-mini` for Direct API; `claude-4.5-haiku` for Local Claude session.";
12167
+ }
11945
12168
  function positionHelpTooltipElement(el, anchor) {
11946
12169
  const rect = anchor.getBoundingClientRect();
11947
12170
  const margin = 8;
@@ -12273,6 +12496,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
12273
12496
  claudeCommand: claudeCommand.length ? JSON.stringify(claudeCommand, null, 2) : "",
12274
12497
  observerProvider: asInputString(effectiveOrConfigured(config, effective, "observer_provider")),
12275
12498
  observerModel: asInputString(effectiveOrConfigured(config, effective, "observer_model")),
12499
+ observerTierRoutingEnabled: asBooleanValue(effectiveOrConfigured(config, effective, "observer_tier_routing_enabled")),
12500
+ observerSimpleModel: asInputString(effectiveOrConfigured(config, effective, "observer_simple_model")),
12501
+ observerSimpleTemperature: asInputString(effectiveOrConfigured(config, effective, "observer_simple_temperature")),
12502
+ observerRichModel: asInputString(effectiveOrConfigured(config, effective, "observer_rich_model")),
12503
+ observerRichTemperature: asInputString(effectiveOrConfigured(config, effective, "observer_rich_temperature")),
12504
+ observerRichOpenAIUseResponses: asBooleanValue(effectiveOrConfigured(config, effective, "observer_rich_openai_use_responses")),
12505
+ observerRichReasoningEffort: asInputString(effectiveOrConfigured(config, effective, "observer_rich_reasoning_effort")),
12506
+ observerRichReasoningSummary: asInputString(effectiveOrConfigured(config, effective, "observer_rich_reasoning_summary")),
12507
+ observerRichMaxOutputTokens: asInputString(effectiveOrConfigured(config, effective, "observer_rich_max_output_tokens")),
12276
12508
  observerRuntime: asInputString(effectiveOrConfigured(config, effective, "observer_runtime")) || "api_http",
12277
12509
  observerAuthSource: asInputString(effectiveOrConfigured(config, effective, "observer_auth_source")) || "auto",
12278
12510
  observerAuthFile: asInputString(effectiveOrConfigured(config, effective, "observer_auth_file")),
@@ -12284,11 +12516,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
12284
12516
  packObservationLimit: asInputString(effectiveOrConfigured(config, effective, "pack_observation_limit")),
12285
12517
  packSessionLimit: asInputString(effectiveOrConfigured(config, effective, "pack_session_limit")),
12286
12518
  rawEventsSweeperIntervalS: asInputString(effectiveOrConfigured(config, effective, "raw_events_sweeper_interval_s")),
12287
- syncEnabled: Boolean(effectiveOrConfigured(config, effective, "sync_enabled")),
12519
+ syncEnabled: asBooleanValue(effectiveOrConfigured(config, effective, "sync_enabled")),
12288
12520
  syncHost: asInputString(effectiveOrConfigured(config, effective, "sync_host")),
12289
12521
  syncPort: asInputString(effectiveOrConfigured(config, effective, "sync_port")),
12290
12522
  syncInterval: asInputString(effectiveOrConfigured(config, effective, "sync_interval_s")),
12291
- syncMdns: Boolean(effectiveOrConfigured(config, effective, "sync_mdns")),
12523
+ syncMdns: asBooleanValue(effectiveOrConfigured(config, effective, "sync_mdns")),
12292
12524
  syncCoordinatorUrl: asInputString(effectiveOrConfigured(config, effective, "sync_coordinator_url")),
12293
12525
  syncCoordinatorGroup: asInputString(effectiveOrConfigured(config, effective, "sync_coordinator_group")),
12294
12526
  syncCoordinatorTimeout: asInputString(effectiveOrConfigured(config, effective, "sync_coordinator_timeout_s")),
@@ -12378,16 +12610,34 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
12378
12610
  });
12379
12611
  }
12380
12612
  const authCacheTtlInput = values.observerAuthCacheTtlS.trim();
12613
+ const simpleTemperatureInput = values.observerSimpleTemperature.trim();
12614
+ const richTemperatureInput = values.observerRichTemperature.trim();
12615
+ const richMaxOutputTokensInput = values.observerRichMaxOutputTokens.trim();
12381
12616
  const sweeperIntervalInput = values.rawEventsSweeperIntervalS.trim();
12382
12617
  const authCacheTtl = authCacheTtlInput === "" ? "" : Number(authCacheTtlInput);
12618
+ const simpleTemperature = simpleTemperatureInput === "" ? "" : Number(simpleTemperatureInput);
12619
+ const richTemperature = richTemperatureInput === "" ? "" : Number(richTemperatureInput);
12620
+ const richMaxOutputTokens = richMaxOutputTokensInput === "" ? "" : Number(richMaxOutputTokensInput);
12383
12621
  const sweeperIntervalNum = Number(sweeperIntervalInput);
12384
12622
  const sweeperInterval = sweeperIntervalInput === "" ? "" : sweeperIntervalNum;
12385
12623
  if (authCacheTtlInput !== "" && !Number.isFinite(authCacheTtl)) throw new Error("observer auth cache ttl must be a number");
12624
+ if (simpleTemperatureInput !== "" && (typeof simpleTemperature !== "number" || !Number.isFinite(simpleTemperature) || simpleTemperature < 0)) throw new Error("simple tier temperature must be a non-negative number");
12625
+ if (richTemperatureInput !== "" && (typeof richTemperature !== "number" || !Number.isFinite(richTemperature) || richTemperature < 0)) throw new Error("rich tier temperature must be a non-negative number");
12626
+ if (richMaxOutputTokensInput !== "" && (typeof richMaxOutputTokens !== "number" || !Number.isFinite(richMaxOutputTokens) || richMaxOutputTokens <= 0 || !Number.isInteger(richMaxOutputTokens))) throw new Error("rich tier max output tokens must be a positive integer");
12386
12627
  if (sweeperIntervalInput !== "" && (!Number.isFinite(sweeperIntervalNum) || sweeperIntervalNum <= 0)) throw new Error("raw-event sweeper interval must be a positive number");
12387
12628
  return {
12388
12629
  claude_command: claudeCommand,
12389
12630
  observer_provider: normalizeTextValue(values.observerProvider),
12390
12631
  observer_model: normalizeTextValue(values.observerModel),
12632
+ observer_tier_routing_enabled: values.observerTierRoutingEnabled,
12633
+ observer_simple_model: normalizeTextValue(values.observerSimpleModel),
12634
+ observer_simple_temperature: simpleTemperature,
12635
+ observer_rich_model: normalizeTextValue(values.observerRichModel),
12636
+ observer_rich_temperature: richTemperature,
12637
+ observer_rich_openai_use_responses: values.observerRichOpenAIUseResponses,
12638
+ observer_rich_reasoning_effort: normalizeTextValue(values.observerRichReasoningEffort),
12639
+ observer_rich_reasoning_summary: normalizeTextValue(values.observerRichReasoningSummary),
12640
+ observer_rich_max_output_tokens: richMaxOutputTokens,
12391
12641
  observer_runtime: normalizeTextValue(values.observerRuntime || "api_http") || "api_http",
12392
12642
  observer_auth_source: normalizeTextValue(values.observerAuthSource || "auto") || "auto",
12393
12643
  observer_auth_file: normalizeTextValue(values.observerAuthFile),
@@ -12657,6 +12907,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
12657
12907
  const observerMaxCharsDefault = state.configDefaults?.observer_max_chars || "";
12658
12908
  const showAuthFile = values.observerAuthSource === "file";
12659
12909
  const showAuthCommand = values.observerAuthSource === "command";
12910
+ const showTieredRouting = values.observerTierRoutingEnabled;
12660
12911
  return /* @__PURE__ */ u("div", {
12661
12912
  className: "modal-card",
12662
12913
  children: [
@@ -12794,11 +13045,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
12794
13045
  className: "field-label",
12795
13046
  children: [/* @__PURE__ */ u("label", {
12796
13047
  htmlFor: "observerModel",
12797
- children: "Model"
13048
+ children: getObserverModelLabel()
12798
13049
  }), /* @__PURE__ */ u("button", {
12799
13050
  "aria-label": "About model defaults",
12800
13051
  className: "help-icon",
12801
- "data-tooltip": "Leave blank to use a recommended model for your selected mode/provider.",
13052
+ "data-tooltip": getObserverModelTooltip(),
12802
13053
  type: "button",
12803
13054
  children: "?"
12804
13055
  })]
@@ -12811,7 +13062,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
12811
13062
  }),
12812
13063
  /* @__PURE__ */ u("div", {
12813
13064
  className: "small",
12814
- children: "Default: `gpt-5.1-codex-mini` for Direct API; `claude-4.5-haiku` for Local Claude session."
13065
+ children: getObserverModelDescription()
12815
13066
  }),
12816
13067
  /* @__PURE__ */ u("div", {
12817
13068
  className: "small",
@@ -13071,89 +13322,259 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
13071
13322
  "data-settings-panel": "queue",
13072
13323
  hidden: settingsActiveTab !== "queue",
13073
13324
  id: "settingsPanelQueue",
13074
- children: [/* @__PURE__ */ u("div", {
13075
- className: "settings-group",
13076
- children: [/* @__PURE__ */ u("h3", {
13077
- className: "settings-group-title",
13078
- children: "Processing"
13079
- }), /* @__PURE__ */ u(Field, { children: [
13080
- /* @__PURE__ */ u("div", {
13081
- className: "field-label",
13082
- children: [/* @__PURE__ */ u("label", {
13083
- htmlFor: "rawEventsSweeperIntervalS",
13084
- children: "Background processing interval (seconds)"
13085
- }), /* @__PURE__ */ u("button", {
13086
- "aria-label": "About background processing interval",
13087
- className: "help-icon",
13088
- "data-tooltip": "How often codemem checks for queued events to process in the background.",
13089
- type: "button",
13090
- children: "?"
13091
- })]
13092
- }),
13093
- /* @__PURE__ */ u("input", {
13094
- id: "rawEventsSweeperIntervalS",
13095
- min: "1",
13096
- onInput: onTextInput("rawEventsSweeperIntervalS"),
13097
- type: "number",
13098
- value: values.rawEventsSweeperIntervalS
13099
- }),
13100
- /* @__PURE__ */ u("div", {
13101
- className: "small",
13102
- children: "How often background flush checks pending raw events."
13103
- })
13104
- ] })]
13105
- }), /* @__PURE__ */ u("div", {
13106
- className: "settings-group settings-advanced",
13107
- hidden: hiddenUnlessAdvanced(),
13108
- children: [
13109
- /* @__PURE__ */ u("h3", {
13325
+ children: [
13326
+ /* @__PURE__ */ u("div", {
13327
+ className: "settings-group",
13328
+ children: [/* @__PURE__ */ u("h3", {
13110
13329
  className: "settings-group-title",
13111
- children: "Context Pack Limits"
13112
- }),
13113
- /* @__PURE__ */ u(Field, {
13114
- className: "field settings-advanced",
13115
- hidden: hiddenUnlessAdvanced(),
13116
- children: [
13117
- /* @__PURE__ */ u("label", {
13118
- htmlFor: "packObservationLimit",
13119
- children: "Observation limit"
13120
- }),
13121
- /* @__PURE__ */ u("input", {
13122
- id: "packObservationLimit",
13123
- min: "1",
13124
- onInput: onTextInput("packObservationLimit"),
13330
+ children: "Processing"
13331
+ }), /* @__PURE__ */ u(Field, { children: [
13332
+ /* @__PURE__ */ u("div", {
13333
+ className: "field-label",
13334
+ children: [/* @__PURE__ */ u("label", {
13335
+ htmlFor: "rawEventsSweeperIntervalS",
13336
+ children: "Background processing interval (seconds)"
13337
+ }), /* @__PURE__ */ u("button", {
13338
+ "aria-label": "About background processing interval",
13339
+ className: "help-icon",
13340
+ "data-tooltip": "How often codemem checks for queued events to process in the background.",
13341
+ type: "button",
13342
+ children: "?"
13343
+ })]
13344
+ }),
13345
+ /* @__PURE__ */ u("input", {
13346
+ id: "rawEventsSweeperIntervalS",
13347
+ min: "1",
13348
+ onInput: onTextInput("rawEventsSweeperIntervalS"),
13349
+ type: "number",
13350
+ value: values.rawEventsSweeperIntervalS
13351
+ }),
13352
+ /* @__PURE__ */ u("div", {
13353
+ className: "small",
13354
+ children: "How often background flush checks pending raw events."
13355
+ })
13356
+ ] })]
13357
+ }),
13358
+ /* @__PURE__ */ u("div", {
13359
+ className: "settings-group",
13360
+ children: [
13361
+ /* @__PURE__ */ u("h3", {
13362
+ className: "settings-group-title",
13363
+ children: "Tiered observer routing"
13364
+ }),
13365
+ /* @__PURE__ */ u("div", {
13366
+ className: "field field-checkbox",
13367
+ children: [/* @__PURE__ */ u("input", {
13368
+ checked: values.observerTierRoutingEnabled,
13369
+ className: "cm-checkbox",
13370
+ id: "observerTierRoutingEnabled",
13371
+ onChange: onCheckboxInput("observerTierRoutingEnabled"),
13372
+ type: "checkbox"
13373
+ }), /* @__PURE__ */ u("label", {
13374
+ htmlFor: "observerTierRoutingEnabled",
13375
+ children: "Enable tiered routing"
13376
+ })]
13377
+ }),
13378
+ /* @__PURE__ */ u("div", {
13379
+ className: "small",
13380
+ children: getTieredRoutingHelperText()
13381
+ }),
13382
+ /* @__PURE__ */ u(Field, {
13383
+ hidden: !showTieredRouting,
13384
+ children: [
13385
+ /* @__PURE__ */ u("div", {
13386
+ className: "field-label",
13387
+ children: [/* @__PURE__ */ u("label", {
13388
+ htmlFor: "observerSimpleModel",
13389
+ children: "Simple tier model"
13390
+ }), /* @__PURE__ */ u("button", {
13391
+ "aria-label": "About simple tier model",
13392
+ className: "help-icon",
13393
+ "data-tooltip": "Used for lighter replay batches. Leave blank to keep codemem's routing defaults or base observer fallback.",
13394
+ type: "button",
13395
+ children: "?"
13396
+ })]
13397
+ }),
13398
+ /* @__PURE__ */ u("input", {
13399
+ id: "observerSimpleModel",
13400
+ onInput: onTextInput("observerSimpleModel"),
13401
+ placeholder: "leave empty for default",
13402
+ value: values.observerSimpleModel
13403
+ }),
13404
+ /* @__PURE__ */ u("div", {
13405
+ className: "small",
13406
+ children: "Used when a batch falls below rich-routing thresholds."
13407
+ })
13408
+ ]
13409
+ }),
13410
+ /* @__PURE__ */ u(Field, {
13411
+ hidden: !showTieredRouting,
13412
+ children: [
13413
+ /* @__PURE__ */ u("div", {
13414
+ className: "field-label",
13415
+ children: [/* @__PURE__ */ u("label", {
13416
+ htmlFor: "observerRichModel",
13417
+ children: "Rich tier model"
13418
+ }), /* @__PURE__ */ u("button", {
13419
+ "aria-label": "About rich tier model",
13420
+ className: "help-icon",
13421
+ "data-tooltip": "Used for larger or more complex replay batches. Leave blank to keep codemem's rich-tier defaults.",
13422
+ type: "button",
13423
+ children: "?"
13424
+ })]
13425
+ }),
13426
+ /* @__PURE__ */ u("input", {
13427
+ id: "observerRichModel",
13428
+ onInput: onTextInput("observerRichModel"),
13429
+ placeholder: "leave empty for default",
13430
+ value: values.observerRichModel
13431
+ }),
13432
+ /* @__PURE__ */ u("div", {
13433
+ className: "small",
13434
+ children: "Used when routing detects a richer replay batch."
13435
+ })
13436
+ ]
13437
+ }),
13438
+ /* @__PURE__ */ u(Field, {
13439
+ className: "field field-checkbox",
13440
+ hidden: !showTieredRouting,
13441
+ children: [/* @__PURE__ */ u("input", {
13442
+ checked: values.observerRichOpenAIUseResponses,
13443
+ className: "cm-checkbox",
13444
+ id: "observerRichOpenAIUseResponses",
13445
+ onChange: onCheckboxInput("observerRichOpenAIUseResponses"),
13446
+ type: "checkbox"
13447
+ }), /* @__PURE__ */ u("label", {
13448
+ htmlFor: "observerRichOpenAIUseResponses",
13449
+ children: "Use OpenAI Responses API for rich tier"
13450
+ })]
13451
+ }),
13452
+ /* @__PURE__ */ u(Field, {
13453
+ className: "field settings-advanced",
13454
+ hidden: !showTieredRouting || hiddenUnlessAdvanced(),
13455
+ children: [/* @__PURE__ */ u("label", {
13456
+ htmlFor: "observerSimpleTemperature",
13457
+ children: "Simple tier temperature"
13458
+ }), /* @__PURE__ */ u("input", {
13459
+ id: "observerSimpleTemperature",
13460
+ min: "0",
13461
+ onInput: onTextInput("observerSimpleTemperature"),
13462
+ step: "0.1",
13125
13463
  type: "number",
13126
- value: values.packObservationLimit
13127
- }),
13128
- /* @__PURE__ */ u("div", {
13129
- className: "small",
13130
- children: "Default number of observations to include in a pack."
13131
- })
13132
- ]
13133
- }),
13134
- /* @__PURE__ */ u(Field, {
13135
- className: "field settings-advanced",
13136
- hidden: hiddenUnlessAdvanced(),
13137
- children: [
13138
- /* @__PURE__ */ u("label", {
13139
- htmlFor: "packSessionLimit",
13140
- children: "Session summary limit"
13141
- }),
13142
- /* @__PURE__ */ u("input", {
13143
- id: "packSessionLimit",
13464
+ value: values.observerSimpleTemperature
13465
+ })]
13466
+ }),
13467
+ /* @__PURE__ */ u(Field, {
13468
+ className: "field settings-advanced",
13469
+ hidden: !showTieredRouting || hiddenUnlessAdvanced(),
13470
+ children: [/* @__PURE__ */ u("label", {
13471
+ htmlFor: "observerRichTemperature",
13472
+ children: "Rich tier temperature"
13473
+ }), /* @__PURE__ */ u("input", {
13474
+ id: "observerRichTemperature",
13475
+ min: "0",
13476
+ onInput: onTextInput("observerRichTemperature"),
13477
+ step: "0.1",
13478
+ type: "number",
13479
+ value: values.observerRichTemperature
13480
+ })]
13481
+ }),
13482
+ /* @__PURE__ */ u(Field, {
13483
+ className: "field settings-advanced",
13484
+ hidden: !showTieredRouting || hiddenUnlessAdvanced(),
13485
+ children: [/* @__PURE__ */ u("label", {
13486
+ htmlFor: "observerRichReasoningEffort",
13487
+ children: "Rich tier reasoning effort"
13488
+ }), /* @__PURE__ */ u("input", {
13489
+ id: "observerRichReasoningEffort",
13490
+ onInput: onTextInput("observerRichReasoningEffort"),
13491
+ placeholder: "leave empty for default",
13492
+ value: values.observerRichReasoningEffort
13493
+ })]
13494
+ }),
13495
+ /* @__PURE__ */ u(Field, {
13496
+ className: "field settings-advanced",
13497
+ hidden: !showTieredRouting || hiddenUnlessAdvanced(),
13498
+ children: [/* @__PURE__ */ u("label", {
13499
+ htmlFor: "observerRichReasoningSummary",
13500
+ children: "Rich tier reasoning summary"
13501
+ }), /* @__PURE__ */ u("input", {
13502
+ id: "observerRichReasoningSummary",
13503
+ onInput: onTextInput("observerRichReasoningSummary"),
13504
+ placeholder: "leave empty for default",
13505
+ value: values.observerRichReasoningSummary
13506
+ })]
13507
+ }),
13508
+ /* @__PURE__ */ u(Field, {
13509
+ className: "field settings-advanced",
13510
+ hidden: !showTieredRouting || hiddenUnlessAdvanced(),
13511
+ children: [/* @__PURE__ */ u("label", {
13512
+ htmlFor: "observerRichMaxOutputTokens",
13513
+ children: "Rich tier max output tokens"
13514
+ }), /* @__PURE__ */ u("input", {
13515
+ id: "observerRichMaxOutputTokens",
13144
13516
  min: "1",
13145
- onInput: onTextInput("packSessionLimit"),
13517
+ onInput: onTextInput("observerRichMaxOutputTokens"),
13518
+ step: "1",
13146
13519
  type: "number",
13147
- value: values.packSessionLimit
13148
- }),
13149
- /* @__PURE__ */ u("div", {
13150
- className: "small",
13151
- children: "Default number of session summaries to include in a pack."
13152
- })
13153
- ]
13154
- })
13155
- ]
13156
- })]
13520
+ value: values.observerRichMaxOutputTokens
13521
+ })]
13522
+ })
13523
+ ]
13524
+ }),
13525
+ /* @__PURE__ */ u("div", {
13526
+ className: "settings-group settings-advanced",
13527
+ hidden: hiddenUnlessAdvanced(),
13528
+ children: [
13529
+ /* @__PURE__ */ u("h3", {
13530
+ className: "settings-group-title",
13531
+ children: "Context Pack Limits"
13532
+ }),
13533
+ /* @__PURE__ */ u(Field, {
13534
+ className: "field settings-advanced",
13535
+ hidden: hiddenUnlessAdvanced(),
13536
+ children: [
13537
+ /* @__PURE__ */ u("label", {
13538
+ htmlFor: "packObservationLimit",
13539
+ children: "Observation limit"
13540
+ }),
13541
+ /* @__PURE__ */ u("input", {
13542
+ id: "packObservationLimit",
13543
+ min: "1",
13544
+ onInput: onTextInput("packObservationLimit"),
13545
+ type: "number",
13546
+ value: values.packObservationLimit
13547
+ }),
13548
+ /* @__PURE__ */ u("div", {
13549
+ className: "small",
13550
+ children: "Default number of observations to include in a pack."
13551
+ })
13552
+ ]
13553
+ }),
13554
+ /* @__PURE__ */ u(Field, {
13555
+ className: "field settings-advanced",
13556
+ hidden: hiddenUnlessAdvanced(),
13557
+ children: [
13558
+ /* @__PURE__ */ u("label", {
13559
+ htmlFor: "packSessionLimit",
13560
+ children: "Session summary limit"
13561
+ }),
13562
+ /* @__PURE__ */ u("input", {
13563
+ id: "packSessionLimit",
13564
+ min: "1",
13565
+ onInput: onTextInput("packSessionLimit"),
13566
+ type: "number",
13567
+ value: values.packSessionLimit
13568
+ }),
13569
+ /* @__PURE__ */ u("div", {
13570
+ className: "small",
13571
+ children: "Default number of session summaries to include in a pack."
13572
+ })
13573
+ ]
13574
+ })
13575
+ ]
13576
+ })
13577
+ ]
13157
13578
  }),
13158
13579
  /* @__PURE__ */ u("div", {
13159
13580
  className: panelClass("sync"),
@@ -13360,7 +13781,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
13360
13781
  try {
13361
13782
  const runtime = await loadRuntimeInfo();
13362
13783
  if (!runtime?.version) return;
13363
- setRuntimeLabel(runtime.version, "8d28296");
13784
+ setRuntimeLabel(runtime.version, "f7fb5fd");
13364
13785
  } catch {}
13365
13786
  }
13366
13787
  var lastAnnouncedRefreshState = null;
@@ -13513,6 +13934,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
13513
13934
  }
13514
13935
  $select("projectFilter")?.addEventListener("change", () => {
13515
13936
  state.currentProject = $select("projectFilter")?.value || "";
13937
+ updateFeedView(true);
13516
13938
  refresh();
13517
13939
  });
13518
13940
  var refreshDebounceTimer = null;