@sumeru/server 0.1.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.
Files changed (95) hide show
  1. package/LICENSE +18 -0
  2. package/dist/.build-fingerprint +1 -0
  3. package/dist/config.d.ts +14 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +142 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/envelope.d.ts +28 -0
  8. package/dist/envelope.d.ts.map +1 -0
  9. package/dist/envelope.js +43 -0
  10. package/dist/envelope.js.map +1 -0
  11. package/dist/export/bundle.d.ts +28 -0
  12. package/dist/export/bundle.d.ts.map +1 -0
  13. package/dist/export/bundle.js +78 -0
  14. package/dist/export/bundle.js.map +1 -0
  15. package/dist/export/handler.d.ts +24 -0
  16. package/dist/export/handler.d.ts.map +1 -0
  17. package/dist/export/handler.js +102 -0
  18. package/dist/export/handler.js.map +1 -0
  19. package/dist/export/index.d.ts +3 -0
  20. package/dist/export/index.d.ts.map +1 -0
  21. package/dist/export/index.js +3 -0
  22. package/dist/export/index.js.map +1 -0
  23. package/dist/handler.d.ts +24 -0
  24. package/dist/handler.d.ts.map +1 -0
  25. package/dist/handler.js +622 -0
  26. package/dist/handler.js.map +1 -0
  27. package/dist/index.d.ts +12 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +10 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/ocas/index.d.ts +3 -0
  32. package/dist/ocas/index.d.ts.map +1 -0
  33. package/dist/ocas/index.js +3 -0
  34. package/dist/ocas/index.js.map +1 -0
  35. package/dist/ocas/schemas.d.ts +41 -0
  36. package/dist/ocas/schemas.d.ts.map +1 -0
  37. package/dist/ocas/schemas.js +108 -0
  38. package/dist/ocas/schemas.js.map +1 -0
  39. package/dist/ocas/store.d.ts +58 -0
  40. package/dist/ocas/store.d.ts.map +1 -0
  41. package/dist/ocas/store.js +139 -0
  42. package/dist/ocas/store.js.map +1 -0
  43. package/dist/search/handler.d.ts +54 -0
  44. package/dist/search/handler.d.ts.map +1 -0
  45. package/dist/search/handler.js +178 -0
  46. package/dist/search/handler.js.map +1 -0
  47. package/dist/search/index.d.ts +4 -0
  48. package/dist/search/index.d.ts.map +1 -0
  49. package/dist/search/index.js +3 -0
  50. package/dist/search/index.js.map +1 -0
  51. package/dist/search/sqlite-index.d.ts +49 -0
  52. package/dist/search/sqlite-index.d.ts.map +1 -0
  53. package/dist/search/sqlite-index.js +508 -0
  54. package/dist/search/sqlite-index.js.map +1 -0
  55. package/dist/search/types.d.ts +143 -0
  56. package/dist/search/types.d.ts.map +1 -0
  57. package/dist/search/types.js +10 -0
  58. package/dist/search/types.js.map +1 -0
  59. package/dist/session/cwd.d.ts +31 -0
  60. package/dist/session/cwd.d.ts.map +1 -0
  61. package/dist/session/cwd.js +54 -0
  62. package/dist/session/cwd.js.map +1 -0
  63. package/dist/session/id.d.ts +12 -0
  64. package/dist/session/id.d.ts.map +1 -0
  65. package/dist/session/id.js +76 -0
  66. package/dist/session/id.js.map +1 -0
  67. package/dist/session/index.d.ts +5 -0
  68. package/dist/session/index.d.ts.map +1 -0
  69. package/dist/session/index.js +4 -0
  70. package/dist/session/index.js.map +1 -0
  71. package/dist/session/store.d.ts +89 -0
  72. package/dist/session/store.d.ts.map +1 -0
  73. package/dist/session/store.js +258 -0
  74. package/dist/session/store.js.map +1 -0
  75. package/dist/sse/buffer.d.ts +53 -0
  76. package/dist/sse/buffer.d.ts.map +1 -0
  77. package/dist/sse/buffer.js +119 -0
  78. package/dist/sse/buffer.js.map +1 -0
  79. package/dist/sse/index.d.ts +3 -0
  80. package/dist/sse/index.d.ts.map +1 -0
  81. package/dist/sse/index.js +3 -0
  82. package/dist/sse/index.js.map +1 -0
  83. package/dist/sse/messages.d.ts +30 -0
  84. package/dist/sse/messages.d.ts.map +1 -0
  85. package/dist/sse/messages.js +489 -0
  86. package/dist/sse/messages.js.map +1 -0
  87. package/dist/start.d.ts +22 -0
  88. package/dist/start.d.ts.map +1 -0
  89. package/dist/start.js +86 -0
  90. package/dist/start.js.map +1 -0
  91. package/dist/types.d.ts +252 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +10 -0
  94. package/dist/types.js.map +1 -0
  95. package/package.json +31 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Phase 5 — search HTTP handlers.
3
+ *
4
+ * Wired into `createHandler` so:
5
+ * - `GET /sessions?q=...` runs cross-gateway search
6
+ * - `GET /gateways/:name/sessions?q=...` runs per-gateway search
7
+ *
8
+ * When `q` is missing or empty after trimming, the per-gateway route falls
9
+ * back to the existing `@sumeru/session-list` behavior; the top-level route
10
+ * 400s because there is no listing fallback.
11
+ */
12
+ import { errorEnvelope, searchResultEnvelope } from "../envelope.js";
13
+ /** Default limit when `?limit=` is absent. */
14
+ const DEFAULT_LIMIT = 50;
15
+ /** Hard cap on `?limit=` per spec. */
16
+ const LIMIT_CAP = 100;
17
+ /** Hard cap on `?q=` length to keep FTS5 inputs and log lines bounded. */
18
+ const QUERY_MAX = 1024;
19
+ /**
20
+ * Parse `q`, `limit`, `offset`, and (top-level only) `gateway` from a query
21
+ * string. Trimming `q` to empty is treated as "absent" — the caller decides
22
+ * whether that means fall through to listing or 400.
23
+ */
24
+ export function parseSearchParams(queryString, allowGatewayFilter) {
25
+ const params = new URLSearchParams(queryString);
26
+ const rawQ = params.get("q");
27
+ const trimmed = rawQ === null ? "" : rawQ.trim();
28
+ if (trimmed.length === 0) {
29
+ return {
30
+ ok: false,
31
+ status: 400,
32
+ error: "invalid_request",
33
+ message: "Query parameter 'q' is required and must be a non-empty string",
34
+ };
35
+ }
36
+ if (trimmed.length > QUERY_MAX) {
37
+ return {
38
+ ok: false,
39
+ status: 400,
40
+ error: "invalid_request",
41
+ message: `Query parameter 'q' must be at most ${QUERY_MAX} characters`,
42
+ };
43
+ }
44
+ const limitRes = parsePositiveOrEmpty(params.get("limit"), "limit");
45
+ if (!limitRes.ok) {
46
+ return {
47
+ ok: false,
48
+ status: 400,
49
+ error: "invalid_request",
50
+ message: limitRes.message,
51
+ };
52
+ }
53
+ const offsetRes = parseNonNegOrEmpty(params.get("offset"), "offset");
54
+ if (!offsetRes.ok) {
55
+ return {
56
+ ok: false,
57
+ status: 400,
58
+ error: "invalid_request",
59
+ message: offsetRes.message,
60
+ };
61
+ }
62
+ const gatewayRaw = params.get("gateway");
63
+ const gateway = allowGatewayFilter && gatewayRaw !== null && gatewayRaw.length > 0
64
+ ? gatewayRaw
65
+ : null;
66
+ const limit = limitRes.value === null ? DEFAULT_LIMIT : limitRes.value;
67
+ const offset = offsetRes.value === null ? 0 : offsetRes.value;
68
+ return {
69
+ ok: true,
70
+ value: {
71
+ query: trimmed,
72
+ gateway,
73
+ limit: Math.min(limit, LIMIT_CAP),
74
+ offset,
75
+ },
76
+ };
77
+ }
78
+ /** Helper that returns `null` for absent params, otherwise a non-negative int. */
79
+ function parseNonNegOrEmpty(raw, name) {
80
+ if (raw === null)
81
+ return { ok: true, value: null };
82
+ const trimmed = raw.trim();
83
+ if (trimmed.length === 0 || !/^[0-9]+$/.test(trimmed)) {
84
+ return {
85
+ ok: false,
86
+ message: `Query parameter '${name}' must be a non-negative integer (got '${raw}')`,
87
+ };
88
+ }
89
+ const n = Number.parseInt(trimmed, 10);
90
+ if (!Number.isFinite(n) || n < 0) {
91
+ return {
92
+ ok: false,
93
+ message: `Query parameter '${name}' must be a non-negative integer (got '${raw}')`,
94
+ };
95
+ }
96
+ return { ok: true, value: n };
97
+ }
98
+ /** `?limit=` accepts non-negative integers; spec phrasing matches history endpoint. */
99
+ function parsePositiveOrEmpty(raw, name) {
100
+ return parseNonNegOrEmpty(raw, name);
101
+ }
102
+ /**
103
+ * Run a cross-gateway search and write the response. Caller must have
104
+ * already validated method == GET / HEAD.
105
+ */
106
+ export function handleSearchTopLevel(res, queryString, searchIndex) {
107
+ const parsed = parseSearchParams(queryString, /* allowGatewayFilter */ true);
108
+ if (!parsed.ok) {
109
+ writeJson(res, parsed.status, errorEnvelope(parsed.error, parsed.message));
110
+ return;
111
+ }
112
+ runAndWrite(res, parsed.value, searchIndex, parsed.value.gateway);
113
+ }
114
+ /**
115
+ * Run a per-gateway search and write the response. The route's gateway is
116
+ * authoritative; any `?gateway=` param is ignored (per spec).
117
+ */
118
+ export function handleSearchPerGateway(res, queryString, searchIndex, gateway) {
119
+ // `allowGatewayFilter: false` so `?gateway=...` never overrides the path.
120
+ const parsed = parseSearchParams(queryString, /* allowGatewayFilter */ false);
121
+ if (!parsed.ok) {
122
+ writeJson(res, parsed.status, errorEnvelope(parsed.error, parsed.message));
123
+ return;
124
+ }
125
+ runAndWrite(res, parsed.value, searchIndex, gateway);
126
+ }
127
+ /**
128
+ * Detect whether a query string carries a search request (`?q=` non-empty
129
+ * after trimming). Used by the per-gateway sessions route to decide between
130
+ * the Phase-2 listing and Phase-5 search.
131
+ */
132
+ export function isSearchRequest(queryString) {
133
+ const params = new URLSearchParams(queryString);
134
+ const rawQ = params.get("q");
135
+ if (rawQ === null)
136
+ return false;
137
+ return rawQ.trim().length > 0;
138
+ }
139
+ function runAndWrite(res, parsed, searchIndex, gateway) {
140
+ const result = searchIndex.search({
141
+ query: parsed.query,
142
+ gateway,
143
+ limit: parsed.limit,
144
+ offset: parsed.offset,
145
+ stripHighlights: false,
146
+ });
147
+ const hits = result.results.map((r) => ({
148
+ id: r.id,
149
+ gateway: r.gateway,
150
+ status: r.status,
151
+ relevance: r.relevance,
152
+ matchContext: r.matchContext,
153
+ turns: r.turns,
154
+ lastActiveAt: r.lastActiveAt,
155
+ }));
156
+ const value = {
157
+ query: parsed.query,
158
+ gateway,
159
+ total: result.total,
160
+ offset: parsed.offset,
161
+ limit: parsed.limit,
162
+ results: hits,
163
+ };
164
+ const payload = JSON.stringify(searchResultEnvelope(value));
165
+ res.statusCode = 200;
166
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
167
+ res.setHeader("Cache-Control", "no-store");
168
+ res.setHeader("Content-Length", Buffer.byteLength(payload).toString());
169
+ res.end(payload);
170
+ }
171
+ function writeJson(res, status, body) {
172
+ const payload = JSON.stringify(body);
173
+ res.statusCode = status;
174
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
175
+ res.setHeader("Content-Length", Buffer.byteLength(payload).toString());
176
+ res.end(payload);
177
+ }
178
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/search/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAIrE,8CAA8C;AAC9C,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,sCAAsC;AACtC,MAAM,SAAS,GAAG,GAAG,CAAC;AACtB,0EAA0E;AAC1E,MAAM,SAAS,GAAG,IAAI,CAAC;AAgBvB;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAChC,WAAmB,EACnB,kBAA2B;IAE3B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,gEAAgE;SACzE,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAChC,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,uCAAuC,SAAS,aAAa;SACtE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,QAAQ,CAAC,OAAO;SACzB,CAAC;IACH,CAAC;IACD,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IACrE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QACnB,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,SAAS,CAAC,OAAO;SAC1B,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,OAAO,GACZ,kBAAkB,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QACjE,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvE,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC;IAC9D,OAAO;QACN,EAAE,EAAE,IAAI;QACR,KAAK,EAAE;YACN,KAAK,EAAE,OAAO;YACd,OAAO;YACP,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC;YACjC,MAAM;SACN;KACD,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,SAAS,kBAAkB,CAC1B,GAAkB,EAClB,IAAY;IAEZ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,OAAO;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,oBAAoB,IAAI,0CAA0C,GAAG,IAAI;SAClF,CAAC;IACH,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO;YACN,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,oBAAoB,IAAI,0CAA0C,GAAG,IAAI;SAClF,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED,uFAAuF;AACvF,SAAS,oBAAoB,CAC5B,GAAkB,EAClB,IAAY;IAEZ,OAAO,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CACnC,GAAmB,EACnB,WAAmB,EACnB,WAAwB;IAExB,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,EAAE,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3E,OAAO;IACR,CAAC;IACD,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACrC,GAAmB,EACnB,WAAmB,EACnB,WAAwB,EACxB,OAAe;IAEf,0EAA0E;IAC1E,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,EAAE,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC9E,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3E,OAAO;IACR,CAAC;IACD,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IAClD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,WAAW,CACnB,GAAmB,EACnB,MAA4B,EAC5B,WAAwB,EACxB,OAAsB;IAEtB,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;QACjC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO;QACP,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,eAAe,EAAE,KAAK;KACtB,CAAC,CAAC;IACH,MAAM,IAAI,GAAsB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1D,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,YAAY,EAAE,CAAC,CAAC,YAAY;KAC5B,CAAC,CAAC,CAAC;IACJ,MAAM,KAAK,GAAsB;QAChC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO;QACP,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,IAAI;KACb,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;IACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;IACjE,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC3C,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,SAAS,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC;IACxB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;IACjE,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { handleSearchPerGateway, handleSearchTopLevel, isSearchRequest, parseSearchParams, } from "./handler.js";
2
+ export { createSearchIndex, quoteFtsPhrase, rebuildSearchIndex, searchSessions, } from "./sqlite-index.js";
3
+ export type { IndexTurnInput, PersistedSessionRow, SearchHit, SearchIndex, SearchOptions, SearchRebuildOcas, SearchResult, SessionMetaInput, } from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,cAAc,GACd,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,GAChB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { handleSearchPerGateway, handleSearchTopLevel, isSearchRequest, parseSearchParams, } from "./handler.js";
2
+ export { createSearchIndex, quoteFtsPhrase, rebuildSearchIndex, searchSessions, } from "./sqlite-index.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,cAAc,GACd,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Phase 5 — FTS5 search index.
3
+ *
4
+ * This module owns the SQLite FTS5 schema that backs Sumeru's session search.
5
+ * The index lives in `<ocasDir>/_store.db` — the same file `@ocas/fs` opens
6
+ * for variables and tags. We open a second `DatabaseSync` handle on the same
7
+ * file (safe because `@ocas/fs` enables WAL).
8
+ *
9
+ * The schema:
10
+ * - `sumeru_turn_index` — one row per turn (PK = turn ocas hash)
11
+ * - `sumeru_turn_fts` — contentless FTS5 virtual table (mirrors content)
12
+ * - `sumeru_session_index` — one row per session (PK = session id)
13
+ *
14
+ * Triggers keep `sumeru_turn_fts` in lockstep with `sumeru_turn_index`.
15
+ *
16
+ * All write paths are idempotent on the turn hash / session id, so re-indexing
17
+ * the same node is a no-op.
18
+ */
19
+ import type { SearchIndex, SearchOptions, SearchRebuildOcas, SearchResult } from "./types.js";
20
+ /**
21
+ * Open a second SQLite handle on the same `_store.db` file `@ocas/fs` writes
22
+ * vars/tags into. Creates the FTS5 schema if not present. Retries up to 3×
23
+ * on `SQLITE_BUSY` with a 50 ms backoff.
24
+ *
25
+ * Throws `failed to create FTS5 index: <cause>` on persistent failure — the
26
+ * caller (`openSumeruOcas`) prepends `failed to open ocas store at <dir>: `
27
+ * so all boot failures share the same prefix.
28
+ */
29
+ export declare function createSearchIndex(dbPath: string): SearchIndex;
30
+ /**
31
+ * Walk every `@sumeru/session-meta` and `@sumeru/turn` node in the ocas store
32
+ * and rebuild the FTS5 index from scratch. The internal `rebuild` closure
33
+ * handles full enumeration via `listByType` and uses `sumeru_session_turns`
34
+ * for turn→session association.
35
+ *
36
+ * Callers no longer need to supply `roots` — the store is the source of truth.
37
+ */
38
+ export declare function rebuildSearchIndex(index: SearchIndex, ocas: SearchRebuildOcas): void;
39
+ /** Wrap a query in `"..."` and double internal `"` to force FTS5 phrase mode. */
40
+ export declare function quoteFtsPhrase(raw: string): string;
41
+ /**
42
+ * Public wrapper that exposes the same `searchSessions` signature the spec
43
+ * documents. Equivalent to calling `index.search(opts)` with `stripHighlights`
44
+ * defaulted to `false`.
45
+ */
46
+ export declare function searchSessions(index: SearchIndex, opts: Omit<SearchOptions, "stripHighlights"> & {
47
+ stripHighlights?: boolean;
48
+ }): SearchResult;
49
+ //# sourceMappingURL=sqlite-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-index.d.ts","sourceRoot":"","sources":["../../src/search/sqlite-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAIX,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EAEZ,MAAM,YAAY,CAAC;AA2DpB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CA6Z7D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,iBAAiB,GACrB,IAAI,CAEN;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC7B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,GAAG;IAC9C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B,GACC,YAAY,CAQd"}