dsh-phone-bridge 0.2.5 → 0.2.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.
Files changed (2) hide show
  1. package/index.js +85 -15
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -943,36 +943,106 @@ export function apply(ctx, config = {}) {
943
943
  }
944
944
  const sessionController = ctx.get("sessionController");
945
945
  if (!sessionController) throw new Error("sessionController is unavailable");
946
- if (typeof sessionController.page !== "function") {
947
- throw new Error("this DSH build does not expose sessionController.page");
948
- }
949
946
 
950
947
  const signal = new AbortController().signal;
951
- // throughSeq: -1 means "the newest page"; maxMessages stays well above the
952
- // requested count because assistant and tool events sit between the user's.
953
- const page = await sessionController.page({
954
- address: { kind: "session", sessionId },
955
- throughSeq: -1,
956
- maxMessages: 200,
957
- }, signal);
948
+ const traces = {};
949
+
950
+ // Two ways in, tried in order.
951
+ //
952
+ // inspect() hands back the event list outright (SessionInspection.events),
953
+ // so it needs no cursor. page() does need one: throughSeq is an absolute
954
+ // sequence, and -1 is not "newest" but "nothing", since paginate starts at
955
+ // end = throughSeq + 1 = 0 and walks backwards from -1. That misreading is
956
+ // what made the first version answer ok with zero messages.
957
+ let events = [];
958
+ let used = null;
959
+
960
+ if (typeof sessionController.inspect === "function") {
961
+ try {
962
+ const inspection = await sessionController.inspect(sessionId, signal);
963
+ const list = Array.isArray(inspection?.events) ? inspection.events : [];
964
+ traces.inspect = {
965
+ eventCount: list.length,
966
+ firstSeq: list[0]?.seq ?? null,
967
+ lastSeq: list[list.length - 1]?.seq ?? null,
968
+ };
969
+ if (list.length) {
970
+ events = list;
971
+ used = "inspect";
972
+ }
973
+ } catch (error) {
974
+ traces.inspect = { error: String(error?.message ?? error) };
975
+ }
976
+ }
977
+
978
+ if (!events.length && typeof sessionController.page === "function") {
979
+ try {
980
+ // Page from whatever cursor inspect gave us, else from the very start.
981
+ const cursor = Number(traces.inspect?.lastSeq ?? 0);
982
+ const page = await sessionController.page({
983
+ address: { kind: "session", sessionId },
984
+ throughSeq: Number.isSafeInteger(cursor) && cursor >= 0 ? cursor : 0,
985
+ maxMessages: 400,
986
+ }, signal);
987
+ const list = (page?.records ?? []).map((record) => record?.event).filter(Boolean);
988
+ traces.page = {
989
+ cursor,
990
+ recordCount: list.length,
991
+ hasMore: page?.hasMore ?? null,
992
+ firstSeq: list[0]?.seq ?? null,
993
+ lastSeq: list[list.length - 1]?.seq ?? null,
994
+ };
995
+ if (list.length) {
996
+ events = list;
997
+ used = "page";
998
+ }
999
+ } catch (error) {
1000
+ traces.page = { error: String(error?.message ?? error) };
1001
+ }
1002
+ }
958
1003
 
959
1004
  const texts = [];
960
- for (const record of page?.records ?? []) {
961
- const event = record?.event;
962
- if (event?.type !== "user/message") continue;
1005
+ const typeTally = {};
1006
+ let firstUserEvent = null;
1007
+ let userEventCount = 0;
1008
+ for (const event of events) {
1009
+ if (!event) continue;
1010
+ const key = event.type ?? "(none)";
1011
+ typeTally[key] = (typeTally[key] ?? 0) + 1;
1012
+ if (event.type !== "user/message") continue;
1013
+ userEventCount += 1;
1014
+ if (!firstUserEvent) firstUserEvent = event;
963
1015
  if (event?.data?.source?.kind !== "user") continue;
964
1016
  const text = textFromContentBlocks(event.data.content);
965
1017
  if (!text) continue;
966
1018
  texts.push({ at: event.time ?? null, text });
967
1019
  }
968
1020
 
969
- sendJson(res, 200, {
1021
+ const payload = {
970
1022
  ok: true,
971
1023
  sessionId,
1024
+ source: used,
972
1025
  total: texts.length,
973
1026
  // Newest last, so the caller can slice from the end.
974
1027
  messages: texts.slice(-limit),
975
- });
1028
+ };
1029
+
1030
+ // Opt-in shape report. The history event format is not a documented
1031
+ // surface, so when a read comes back empty the only quick way to find out
1032
+ // why is to look at what actually arrived. Loopback-only, and truncated.
1033
+ if ((url.searchParams.get("debug") ?? "") === "1") {
1034
+ payload.diagnostics = {
1035
+ used,
1036
+ eventCount: events.length,
1037
+ userEventCount,
1038
+ eventTypes: typeTally,
1039
+ traces,
1040
+ firstEventKeys: events[0] ? Object.keys(events[0]) : [],
1041
+ firstUserEventSample: firstUserEvent ? JSON.stringify(firstUserEvent).slice(0, 1500) : null,
1042
+ };
1043
+ }
1044
+
1045
+ sendJson(res, 200, payload);
976
1046
  } catch (error) {
977
1047
  sendJson(res, 500, { ok: false, error: String(error?.message ?? error) });
978
1048
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-phone-bridge",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Drive an existing DSH desktop session from a chat app, plus a sidebar delete button and trash panel",
5
5
  "type": "module",
6
6
  "main": "index.js",