@codemem/server 0.20.0-alpha.5 → 0.20.0-alpha.7

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.
@@ -4,12 +4,14 @@
4
4
  * Ports Python's viewer_routes/observer_status.py.
5
5
  * Returns observer runtime info, credential availability, and queue status.
6
6
  */
7
- import type { MemoryStore, RawEventSweeper } from "@codemem/core";
7
+ import type { ObserverClient } from "@codemem/core";
8
+ import { type MemoryStore, type RawEventSweeper } from "@codemem/core";
8
9
  import { Hono } from "hono";
9
10
  type StoreFactory = () => MemoryStore;
10
11
  export interface ObserverStatusDeps {
11
12
  getStore: StoreFactory;
12
13
  getSweeper: () => RawEventSweeper | null;
14
+ getObserver?: () => ObserverClient | null;
13
15
  }
14
16
  export declare function observerStatusRoutes(deps?: ObserverStatusDeps): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
15
17
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"observer-status.d.ts","sourceRoot":"","sources":["../../src/routes/observer-status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,YAAY,CAAC;IACvB,UAAU,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;CACzC;AAiBD,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,kBAAkB,8EA2C7D"}
1
+ {"version":3,"file":"observer-status.d.ts","sourceRoot":"","sources":["../../src/routes/observer-status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,KAAK,WAAW,EAA6B,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAClG,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,YAAY,CAAC;IACvB,UAAU,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;CAC1C;AA6BD,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,kBAAkB,8EAiD7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"raw-events.d.ts","sourceRoot":"","sources":["../../src/routes/raw-events.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIlE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AAqFtC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,8EA0UvF"}
1
+ {"version":3,"file":"raw-events.d.ts","sourceRoot":"","sources":["../../src/routes/raw-events.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIlE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AA2FtC,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,8EA0UvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/routes/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAIjD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AAyGtC,wBAAgB,UAAU,CAAC,QAAQ,EAAE,YAAY,8EAyRhD"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/routes/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAqBhE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AAmXtC,wBAAgB,UAAU,CAAC,QAAQ,EAAE,YAAY,8EAuiBhD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemem/server",
3
- "version": "0.20.0-alpha.5",
3
+ "version": "0.20.0-alpha.7",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -18,7 +18,7 @@
18
18
  "@hono/node-server": "^1.14.3",
19
19
  "drizzle-orm": "^0.45.1",
20
20
  "hono": "^4.7.10",
21
- "@codemem/core": "^0.20.0-alpha.5"
21
+ "@codemem/core": "^0.20.0-alpha.7"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/better-sqlite3": "^7.6.13",
package/static/app.js CHANGED
@@ -245,7 +245,7 @@
245
245
  return params.toString();
246
246
  }
247
247
  async function loadMemoriesPage(project, options) {
248
- return fetchJson(`/api/memories?${buildProjectParams(project, options?.limit, options?.offset, options?.scope)}`);
248
+ return fetchJson(`/api/observations?${buildProjectParams(project, options?.limit, options?.offset, options?.scope)}`);
249
249
  }
250
250
  async function updateMemoryVisibility(memoryId, visibility) {
251
251
  const resp = await fetch("/api/memories/visibility", {
@@ -287,10 +287,11 @@
287
287
  }
288
288
  return parsed;
289
289
  }
290
- async function loadSyncStatus(includeDiagnostics, project = "") {
290
+ async function loadSyncStatus(includeDiagnostics, project = "", options) {
291
291
  const params = new URLSearchParams();
292
292
  if (includeDiagnostics) params.set("includeDiagnostics", "1");
293
293
  if (project) params.set("project", project);
294
+ if (options?.includeJoinRequests) params.set("includeJoinRequests", "1");
294
295
  return fetchJson(`/api/sync/status${params.size ? `?${params.toString()}` : ""}`);
295
296
  }
296
297
  async function createCoordinatorInvite(payload) {
@@ -727,8 +728,32 @@
727
728
  const metadata = item?.metadata_json;
728
729
  if (looksLikeSummary(metadata)) return metadata;
729
730
  if (looksLikeSummary(metadata?.summary)) return metadata.summary;
731
+ const bodyText = String(item?.body_text || "").trim();
732
+ if (bodyText.includes("## ")) {
733
+ const headingMap = {
734
+ request: "request",
735
+ completed: "completed",
736
+ learned: "learned",
737
+ investigated: "investigated",
738
+ "next steps": "next_steps",
739
+ notes: "notes"
740
+ };
741
+ const parsed = {};
742
+ const sectionRe = /(?:^|\n)##\s+([^\n]+)\n([\s\S]*?)(?=\n##\s+|$)/g;
743
+ for (let match = sectionRe.exec(bodyText); match; match = sectionRe.exec(bodyText)) {
744
+ const key = headingMap[String(match[1] || "").trim().toLowerCase()];
745
+ const content = String(match[2] || "").trim();
746
+ if (key && content) parsed[key] = content;
747
+ }
748
+ if (looksLikeSummary(parsed)) return parsed;
749
+ }
730
750
  return null;
731
751
  }
752
+ function isSummaryLikeItem(item, metadata) {
753
+ if (String(item?.kind || "").toLowerCase() === "session_summary") return true;
754
+ if (metadata?.is_summary === true) return true;
755
+ return String(metadata?.source || "").trim().toLowerCase() === "observer_summary";
756
+ }
732
757
  function observationViewData(item) {
733
758
  const metadata = mergeMetadata(item?.metadata_json);
734
759
  const summary = String(item?.subtitle || item?.body_text || "").trim();
@@ -869,6 +894,22 @@
869
894
  function renderFacts(facts) {
870
895
  const trimmed = facts.map((f) => String(f || "").trim()).filter(Boolean);
871
896
  if (!trimmed.length) return null;
897
+ if (trimmed.every((f) => /.+?:\s+.+/.test(f))) {
898
+ const container = el("div", "feed-body facts");
899
+ trimmed.forEach((fact) => {
900
+ const splitAt = fact.indexOf(":");
901
+ const labelText = fact.slice(0, splitAt).trim();
902
+ const contentText = fact.slice(splitAt + 1).trim();
903
+ if (!labelText || !contentText) return;
904
+ const row = el("div", "summary-section");
905
+ const label = el("div", "summary-section-label", labelText);
906
+ const value = el("div", "summary-section-content");
907
+ value.innerHTML = renderMarkdownSafe(contentText);
908
+ row.append(label, value);
909
+ container.appendChild(row);
910
+ });
911
+ if (container.childElementCount > 0) return container;
912
+ }
872
913
  const container = el("div", "feed-body");
873
914
  const list = document.createElement("ul");
874
915
  trimmed.forEach((f) => {
@@ -912,9 +953,10 @@
912
953
  }
913
954
  function renderFeedItem(item) {
914
955
  const kindValue = String(item.kind || "session_summary").toLowerCase();
915
- const isSessionSummary = kindValue === "session_summary";
916
956
  const metadata = mergeMetadata(item?.metadata_json);
917
- const card = el("div", `feed-item ${kindValue}`.trim());
957
+ const isSessionSummary = isSummaryLikeItem(item, metadata);
958
+ const displayKindValue = isSessionSummary ? "session_summary" : kindValue;
959
+ const card = el("div", `feed-item ${displayKindValue}`.trim());
918
960
  const rowKey = itemKey(item);
919
961
  card.dataset.key = rowKey;
920
962
  if (state.newItemKeys.has(rowKey)) {
@@ -930,7 +972,7 @@
930
972
  const displayTitle = isSessionSummary && metadata?.request ? metadata.request : defaultTitle;
931
973
  const title = el("div", "feed-title title");
932
974
  title.innerHTML = highlightText(displayTitle, state.feedQuery);
933
- const kind = el("span", `kind-pill ${kindValue}`.trim(), kindValue.replace(/_/g, " "));
975
+ const kind = el("span", `kind-pill ${displayKindValue}`.trim(), displayKindValue.replace(/_/g, " "));
934
976
  titleWrap.append(kind, title);
935
977
  const rightWrap = el("div", "feed-actions");
936
978
  const createdAtRaw = item.created_at || item.created_at_utc;
@@ -939,7 +981,10 @@
939
981
  const footerRight = el("div", "feed-footer-right");
940
982
  let bodyNode = el("div", "feed-body");
941
983
  if (isSessionSummary) {
942
- const summaryObj = getSummaryObject({ metadata_json: metadata });
984
+ const summaryObj = getSummaryObject({
985
+ ...item,
986
+ metadata_json: metadata
987
+ });
943
988
  bodyNode = (summaryObj ? renderSummaryObject(summaryObj) : null) || renderNarrative(String(item.body_text || "")) || bodyNode;
944
989
  } else {
945
990
  const data = observationViewData({
@@ -1074,8 +1119,8 @@
1074
1119
  return card;
1075
1120
  }
1076
1121
  function filterByType(items) {
1077
- if (state.feedTypeFilter === "observations") return items.filter((i) => String(i.kind || "").toLowerCase() !== "session_summary");
1078
- if (state.feedTypeFilter === "summaries") return items.filter((i) => String(i.kind || "").toLowerCase() === "session_summary");
1122
+ if (state.feedTypeFilter === "observations") return items.filter((i) => !isSummaryLikeItem(i, mergeMetadata(i?.metadata_json)));
1123
+ if (state.feedTypeFilter === "summaries") return items.filter((i) => isSummaryLikeItem(i, mergeMetadata(i?.metadata_json)));
1079
1124
  return items;
1080
1125
  }
1081
1126
  function filterByQuery(items) {
@@ -2724,9 +2769,48 @@
2724
2769
  //#endregion
2725
2770
  //#region src/tabs/sync/index.ts
2726
2771
  var lastSyncHash = "";
2772
+ var cachedSyncStatus = null;
2773
+ var HEALTH_SYNC_STATUS_CACHE_TTL_MS = 15e3;
2774
+ function syncStatusCacheKey(project) {
2775
+ return `project:${project || ""}|includeJoinRequests:false`;
2776
+ }
2777
+ function readCachedSyncStatus(project) {
2778
+ const key = syncStatusCacheKey(project);
2779
+ if (!cachedSyncStatus) return null;
2780
+ if (cachedSyncStatus.key !== key) return null;
2781
+ if (Date.now() >= cachedSyncStatus.expiresAtMs) return null;
2782
+ return cachedSyncStatus.payload;
2783
+ }
2784
+ function writeCachedSyncStatus(project, payload) {
2785
+ cachedSyncStatus = {
2786
+ key: syncStatusCacheKey(project),
2787
+ expiresAtMs: Date.now() + HEALTH_SYNC_STATUS_CACHE_TTL_MS,
2788
+ payload
2789
+ };
2790
+ }
2791
+ function normalizeSyncStatusForCache(payload) {
2792
+ if (!payload || typeof payload !== "object") return payload;
2793
+ return {
2794
+ ...payload,
2795
+ join_requests: []
2796
+ };
2797
+ }
2727
2798
  async function loadSyncData() {
2728
2799
  try {
2729
- const payload = await loadSyncStatus(true, state.currentProject || "");
2800
+ const project = state.currentProject || "";
2801
+ const includeJoinRequests = state.activeTab === "sync";
2802
+ const useCache = state.activeTab === "health";
2803
+ let payload;
2804
+ if (useCache) {
2805
+ payload = readCachedSyncStatus(project);
2806
+ if (!payload) {
2807
+ payload = await loadSyncStatus(true, project, { includeJoinRequests: false });
2808
+ writeCachedSyncStatus(project, normalizeSyncStatusForCache(payload));
2809
+ }
2810
+ } else {
2811
+ payload = await loadSyncStatus(true, project, { includeJoinRequests });
2812
+ writeCachedSyncStatus(project, normalizeSyncStatusForCache(payload));
2813
+ }
2730
2814
  let actorsPayload = null;
2731
2815
  let actorLoadError = false;
2732
2816
  try {
@@ -2743,7 +2827,7 @@
2743
2827
  state.lastSyncPeers = payload.peers || [];
2744
2828
  state.lastSyncSharingReview = payload.sharing_review || [];
2745
2829
  state.lastSyncCoordinator = payload.coordinator || null;
2746
- state.lastSyncJoinRequests = payload.join_requests || [];
2830
+ if (Array.isArray(payload.join_requests)) state.lastSyncJoinRequests = payload.join_requests;
2747
2831
  state.lastSyncAttempts = payload.attempts || [];
2748
2832
  state.lastSyncLegacyDevices = payload.legacy_devices || [];
2749
2833
  renderSyncStatus();
@@ -2871,6 +2955,10 @@
2871
2955
  model: DEFAULT_ANTHROPIC_MODEL,
2872
2956
  source: "Recommended (Anthropic provider)"
2873
2957
  };
2958
+ if (provider === "opencode") return {
2959
+ model: "opencode/gpt-5.1-codex-mini",
2960
+ source: "Recommended (OpenCode Zen provider)"
2961
+ };
2874
2962
  if (provider && provider !== "openai") return {
2875
2963
  model: "provider default",
2876
2964
  source: "Recommended (provider default)"