wicked-crew 0.14.0 → 0.14.2
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/api/roster-standing.d.ts.map +1 -1
- package/dist/api/roster-standing.js +2 -0
- package/dist/api/roster-standing.js.map +1 -1
- package/dist/api/routes.d.ts +3 -0
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +36 -3
- package/dist/api/routes.js.map +1 -1
- package/dist/api/search-index.d.ts +132 -0
- package/dist/api/search-index.d.ts.map +1 -0
- package/dist/api/search-index.js +345 -0
- package/dist/api/search-index.js.map +1 -0
- package/dist/api/search-routes.d.ts +102 -0
- package/dist/api/search-routes.d.ts.map +1 -0
- package/dist/api/search-routes.js +578 -0
- package/dist/api/search-routes.js.map +1 -0
- package/dist/api/seat-bench-trail.d.ts +14 -0
- package/dist/api/seat-bench-trail.d.ts.map +1 -0
- package/dist/api/seat-bench-trail.js +70 -0
- package/dist/api/seat-bench-trail.js.map +1 -0
- package/dist/api/seat-health.d.ts +51 -2
- package/dist/api/seat-health.d.ts.map +1 -1
- package/dist/api/seat-health.js +58 -9
- package/dist/api/seat-health.js.map +1 -1
- package/dist/api/seat-standing.d.ts +7 -0
- package/dist/api/seat-standing.d.ts.map +1 -1
- package/dist/api/seat-standing.js +12 -0
- package/dist/api/seat-standing.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +16 -0
- package/dist/api/server.js.map +1 -1
- package/dist/studio/assets/{index-BA69zWzx.js → index-BjH2WWnH.js} +211 -211
- package/dist/studio/editors/wicked-deck/editor.json +1 -1
- package/dist/studio/editors/wicked-deck/index.html +1 -1
- package/dist/studio/editors/wicked-doc/editor.json +1 -1
- package/dist/studio/editors/wicked-doc/index.html +1 -1
- package/dist/studio/editors/wicked-page/editor.json +1 -1
- package/dist/studio/editors/wicked-page/index.html +1 -1
- package/dist/studio/editors/wicked-video/editor.json +1 -1
- package/dist/studio/editors/wicked-video/index.html +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/studio/testid-inventory.json +40 -4
- package/endpoint-manifest.json +11 -1
- package/package.json +3 -3
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The global search index (`GET /search`; api-types 0.127.0): an in-memory text store per entity
|
|
3
|
+
* type, scanned per query under a hard time budget.
|
|
4
|
+
*
|
|
5
|
+
* Why not SQLite FTS5: the daemon loads no SQLite library at all (wicked-core#631; enforced by
|
|
6
|
+
* tests/bus-no-write.test.ts). The engine owns every SQLite file in this process, so an FTS index
|
|
7
|
+
* belongs in the engine; until it has one, this store is the daemon's. It is a scan, not an
|
|
8
|
+
* inverted index: each entity keeps a case-folded copy of its title and (capped) body, and a query
|
|
9
|
+
* runs `indexOf` per word over them. That keeps substring and prefix matches exact and the code
|
|
10
|
+
* small, and it is fast enough for an operator's store (a few MB of text is a few ms of scanning).
|
|
11
|
+
*
|
|
12
|
+
* It never holds the event loop: the scan yields every {@link SLICE_MS}, stops when its signal
|
|
13
|
+
* aborts (the client went away: a superseded query), and stops at the time budget with
|
|
14
|
+
* `partial: true` (totals are then lower bounds).
|
|
15
|
+
*/
|
|
16
|
+
import type { SearchGroup, SearchSpan, SearchTarget, SearchType } from '../core/types.js';
|
|
17
|
+
/** Every searchable type, in the order a response lists them. */
|
|
18
|
+
export declare const SEARCH_TYPES: readonly SearchType[];
|
|
19
|
+
/** One entity as a source hands it to the index. */
|
|
20
|
+
export interface SearchDoc {
|
|
21
|
+
type: SearchType;
|
|
22
|
+
id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
/** Searchable text beyond the title (capped by the index at {@link SearchIndexOptions.maxBodyChars}). */
|
|
25
|
+
body: string;
|
|
26
|
+
/** Unix millis, when known. */
|
|
27
|
+
updatedAt: number | null;
|
|
28
|
+
projectId: string | null;
|
|
29
|
+
status: string | null;
|
|
30
|
+
target: SearchTarget;
|
|
31
|
+
/** Added to the score of every match (negative for archived/retired entities). */
|
|
32
|
+
boost?: number;
|
|
33
|
+
}
|
|
34
|
+
interface Stored {
|
|
35
|
+
doc: SearchDoc;
|
|
36
|
+
titleFold: string;
|
|
37
|
+
bodyFold: string;
|
|
38
|
+
}
|
|
39
|
+
export interface SearchIndexOptions {
|
|
40
|
+
/** Body text kept per entity; longer bodies keep their head and tail. Default 64 Ki chars. */
|
|
41
|
+
maxBodyChars?: number;
|
|
42
|
+
/** Total body text kept per type; past it the least recently updated entities leave. Default 8 Mi chars. */
|
|
43
|
+
maxCharsPerType?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface ParsedQuery {
|
|
46
|
+
/** The trimmed query as asked. */
|
|
47
|
+
text: string;
|
|
48
|
+
/** Case-folded words and phrases; every one must match. */
|
|
49
|
+
terms: string[];
|
|
50
|
+
}
|
|
51
|
+
export interface QueryOptions {
|
|
52
|
+
types: readonly SearchType[];
|
|
53
|
+
/** Hits per group. */
|
|
54
|
+
limit: number;
|
|
55
|
+
/** Offset into one type's ranked matches (the cursor). */
|
|
56
|
+
offset?: number;
|
|
57
|
+
/** Hard wall-clock budget for the scan, ms. */
|
|
58
|
+
budgetMs: number;
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
/** Clock (tests). */
|
|
61
|
+
now?: () => number;
|
|
62
|
+
/** The recency bonus's "now" (tests), unix millis. */
|
|
63
|
+
at?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface QueryResult {
|
|
66
|
+
groups: SearchGroup[];
|
|
67
|
+
partial: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** Query limits (the route answers 400 past them). */
|
|
70
|
+
export declare const MAX_QUERY_CHARS = 256;
|
|
71
|
+
export declare const MIN_QUERY_CHARS = 2;
|
|
72
|
+
/**
|
|
73
|
+
* Lower-case without changing length, so offsets in the folded copy are offsets in the original
|
|
74
|
+
* (a few code points lower-case to two units — those keep their case).
|
|
75
|
+
*/
|
|
76
|
+
export declare function fold(s: string): string;
|
|
77
|
+
/** Words and `"quoted phrases"`, folded, de-duplicated, at most {@link MAX_TERMS}. */
|
|
78
|
+
export declare function parseQuery(raw: string): ParsedQuery;
|
|
79
|
+
/** The match score of one entity, or 0 when a term is missing (every term must match). */
|
|
80
|
+
export declare function scoreOf(s: Stored, terms: readonly string[], at: number): number;
|
|
81
|
+
/** Every occurrence of every term in `folded`, merged into ascending, non-overlapping spans. */
|
|
82
|
+
export declare function spansOf(folded: string, terms: readonly string[]): SearchSpan[];
|
|
83
|
+
/**
|
|
84
|
+
* The window of the body that shows the match best: around the term occurrence whose window holds
|
|
85
|
+
* the most distinct terms. Whitespace is collapsed in the window (folded and original alike, so
|
|
86
|
+
* spans stay aligned); `…` marks a cut.
|
|
87
|
+
*/
|
|
88
|
+
export declare function snippetOf(s: Stored, terms: readonly string[]): {
|
|
89
|
+
snippet: string | null;
|
|
90
|
+
spans: SearchSpan[];
|
|
91
|
+
};
|
|
92
|
+
/** Head and tail of a long text, joined by an elision line. */
|
|
93
|
+
export declare function capText(text: string, max: number): string;
|
|
94
|
+
export declare class SearchIndex {
|
|
95
|
+
private readonly byType;
|
|
96
|
+
private readonly charsByType;
|
|
97
|
+
private readonly leftOut;
|
|
98
|
+
private readonly maxBodyChars;
|
|
99
|
+
private readonly maxCharsPerType;
|
|
100
|
+
constructor(opts?: SearchIndexOptions);
|
|
101
|
+
private mapOf;
|
|
102
|
+
/** Entities of `type` held. */
|
|
103
|
+
count(type: SearchType): number;
|
|
104
|
+
/** Body characters of `type` held (the budget {@link SearchIndexOptions.maxCharsPerType} bounds). */
|
|
105
|
+
chars(type: SearchType): number;
|
|
106
|
+
has(type: SearchType, id: string): boolean;
|
|
107
|
+
ids(type: SearchType): string[];
|
|
108
|
+
/** Add or replace one entity. */
|
|
109
|
+
upsert(input: SearchDoc): void;
|
|
110
|
+
private put;
|
|
111
|
+
remove(type: SearchType, id: string): void;
|
|
112
|
+
/** Remove every entity of `type` whose id `drop` accepts. */
|
|
113
|
+
removeWhere(type: SearchType, drop: (id: string) => boolean): void;
|
|
114
|
+
/**
|
|
115
|
+
* A full re-read of a source: hold what it holds, drop what it no longer does. Newest first, and
|
|
116
|
+
* only while the type's text budget lasts — the rest is never folded, so a re-read of a store past
|
|
117
|
+
* the budget costs one sort, not an evict per entity.
|
|
118
|
+
*/
|
|
119
|
+
replaceType(type: SearchType, docs: readonly SearchDoc[]): void;
|
|
120
|
+
/** Entities the last re-read of `type` left out to stay inside its text budget. */
|
|
121
|
+
leftOutOf(type: SearchType): number;
|
|
122
|
+
/** Over budget: the least recently updated entities leave first (undated ones before dated). */
|
|
123
|
+
private evict;
|
|
124
|
+
/**
|
|
125
|
+
* Scan `types` for `q`. Yields every {@link SLICE_MS}; throws the signal's reason when it aborts;
|
|
126
|
+
* stops at `budgetMs` with `partial: true`. Groups with no match are left out.
|
|
127
|
+
*/
|
|
128
|
+
query(q: ParsedQuery, opts: QueryOptions): Promise<QueryResult>;
|
|
129
|
+
private pushGroup;
|
|
130
|
+
}
|
|
131
|
+
export {};
|
|
132
|
+
//# sourceMappingURL=search-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-index.d.ts","sourceRoot":"","sources":["../../src/api/search-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The global search index (`GET /search`; api-types 0.127.0): an in-memory text store per entity
|
|
3
|
+
* type, scanned per query under a hard time budget.
|
|
4
|
+
*
|
|
5
|
+
* Why not SQLite FTS5: the daemon loads no SQLite library at all (wicked-core#631; enforced by
|
|
6
|
+
* tests/bus-no-write.test.ts). The engine owns every SQLite file in this process, so an FTS index
|
|
7
|
+
* belongs in the engine; until it has one, this store is the daemon's. It is a scan, not an
|
|
8
|
+
* inverted index: each entity keeps a case-folded copy of its title and (capped) body, and a query
|
|
9
|
+
* runs `indexOf` per word over them. That keeps substring and prefix matches exact and the code
|
|
10
|
+
* small, and it is fast enough for an operator's store (a few MB of text is a few ms of scanning).
|
|
11
|
+
*
|
|
12
|
+
* It never holds the event loop: the scan yields every {@link SLICE_MS}, stops when its signal
|
|
13
|
+
* aborts (the client went away: a superseded query), and stops at the time budget with
|
|
14
|
+
* `partial: true` (totals are then lower bounds).
|
|
15
|
+
*/
|
|
16
|
+
/** Every searchable type, in the order a response lists them. */
|
|
17
|
+
export const SEARCH_TYPES = ['run', 'project', 'repo', 'transcript', 'rule', 'memory', 'doc'];
|
|
18
|
+
/** The longest stretch the scan runs before giving the event loop a turn. */
|
|
19
|
+
const SLICE_MS = 4;
|
|
20
|
+
/** Query limits (the route answers 400 past them). */
|
|
21
|
+
export const MAX_QUERY_CHARS = 256;
|
|
22
|
+
export const MIN_QUERY_CHARS = 2;
|
|
23
|
+
const MAX_TERMS = 8;
|
|
24
|
+
/** Snippet window around the best match. */
|
|
25
|
+
const SNIPPET_BEFORE = 50;
|
|
26
|
+
const SNIPPET_AFTER = 110;
|
|
27
|
+
/**
|
|
28
|
+
* Lower-case without changing length, so offsets in the folded copy are offsets in the original
|
|
29
|
+
* (a few code points lower-case to two units — those keep their case).
|
|
30
|
+
*/
|
|
31
|
+
export function fold(s) {
|
|
32
|
+
const lower = s.toLowerCase();
|
|
33
|
+
if (lower.length === s.length)
|
|
34
|
+
return lower;
|
|
35
|
+
let out = '';
|
|
36
|
+
for (const ch of s) {
|
|
37
|
+
const l = ch.toLowerCase();
|
|
38
|
+
out += l.length === ch.length ? l : ch;
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/** Words and `"quoted phrases"`, folded, de-duplicated, at most {@link MAX_TERMS}. */
|
|
43
|
+
export function parseQuery(raw) {
|
|
44
|
+
const text = raw.trim();
|
|
45
|
+
const terms = [];
|
|
46
|
+
const re = /"([^"]+)"|(\S+)/g;
|
|
47
|
+
let m;
|
|
48
|
+
while ((m = re.exec(text)) !== null && terms.length < MAX_TERMS) {
|
|
49
|
+
const t = fold((m[1] ?? m[2] ?? '').trim().replace(/\s+/g, ' ').replace(/^"|"$/g, ''));
|
|
50
|
+
if (t !== '' && !terms.includes(t))
|
|
51
|
+
terms.push(t);
|
|
52
|
+
}
|
|
53
|
+
return { text, terms };
|
|
54
|
+
}
|
|
55
|
+
function isWordChar(c) {
|
|
56
|
+
return c !== undefined && /[\p{L}\p{N}_]/u.test(c);
|
|
57
|
+
}
|
|
58
|
+
function atWordStart(s, i) {
|
|
59
|
+
return i === 0 || !isWordChar(s[i - 1]);
|
|
60
|
+
}
|
|
61
|
+
/** Occurrences of `t` in `s`, at most `cap`. */
|
|
62
|
+
function countOf(s, t, first, cap) {
|
|
63
|
+
let n = 0;
|
|
64
|
+
let i = first;
|
|
65
|
+
while (i !== -1 && n < cap) {
|
|
66
|
+
n += 1;
|
|
67
|
+
i = s.indexOf(t, i + t.length);
|
|
68
|
+
}
|
|
69
|
+
return n;
|
|
70
|
+
}
|
|
71
|
+
/** The match score of one entity, or 0 when a term is missing (every term must match). */
|
|
72
|
+
export function scoreOf(s, terms, at) {
|
|
73
|
+
if (terms.length === 0)
|
|
74
|
+
return 0; // no words match nothing (never everything)
|
|
75
|
+
let score = 0;
|
|
76
|
+
for (const t of terms) {
|
|
77
|
+
const ti = s.titleFold.indexOf(t);
|
|
78
|
+
const bi = s.bodyFold.indexOf(t);
|
|
79
|
+
if (ti === -1 && bi === -1)
|
|
80
|
+
return 0;
|
|
81
|
+
if (ti !== -1) {
|
|
82
|
+
score += 10;
|
|
83
|
+
if (atWordStart(s.titleFold, ti))
|
|
84
|
+
score += 5;
|
|
85
|
+
if (ti === 0)
|
|
86
|
+
score += 3;
|
|
87
|
+
}
|
|
88
|
+
if (bi !== -1) {
|
|
89
|
+
score += 2 + Math.log2(1 + countOf(s.bodyFold, t, bi, 8));
|
|
90
|
+
if (atWordStart(s.bodyFold, bi))
|
|
91
|
+
score += 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (terms.length > 1) {
|
|
95
|
+
const phrase = terms.join(' ');
|
|
96
|
+
if (s.titleFold.includes(phrase))
|
|
97
|
+
score += 8;
|
|
98
|
+
else if (s.bodyFold.includes(phrase))
|
|
99
|
+
score += 3;
|
|
100
|
+
}
|
|
101
|
+
if (s.titleFold.trim() === terms.join(' '))
|
|
102
|
+
score += 10;
|
|
103
|
+
if (s.doc.updatedAt !== null) {
|
|
104
|
+
const days = Math.max(0, (at - s.doc.updatedAt) / 86_400_000);
|
|
105
|
+
score += 3 * Math.exp(-days / 14);
|
|
106
|
+
}
|
|
107
|
+
return Math.max(0.01, score + (s.doc.boost ?? 0));
|
|
108
|
+
}
|
|
109
|
+
/** Every occurrence of every term in `folded`, merged into ascending, non-overlapping spans. */
|
|
110
|
+
export function spansOf(folded, terms) {
|
|
111
|
+
const raw = [];
|
|
112
|
+
for (const t of terms) {
|
|
113
|
+
let i = folded.indexOf(t);
|
|
114
|
+
while (i !== -1 && raw.length < 64) {
|
|
115
|
+
raw.push([i, i + t.length]);
|
|
116
|
+
i = folded.indexOf(t, i + t.length);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
raw.sort((a, b) => a[0] - b[0]);
|
|
120
|
+
const out = [];
|
|
121
|
+
for (const sp of raw) {
|
|
122
|
+
const last = out[out.length - 1];
|
|
123
|
+
if (last !== undefined && sp[0] <= last[1])
|
|
124
|
+
last[1] = Math.max(last[1], sp[1]);
|
|
125
|
+
else
|
|
126
|
+
out.push([sp[0], sp[1]]);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The window of the body that shows the match best: around the term occurrence whose window holds
|
|
132
|
+
* the most distinct terms. Whitespace is collapsed in the window (folded and original alike, so
|
|
133
|
+
* spans stay aligned); `…` marks a cut.
|
|
134
|
+
*/
|
|
135
|
+
export function snippetOf(s, terms) {
|
|
136
|
+
const body = s.doc.body;
|
|
137
|
+
if (body.trim() === '')
|
|
138
|
+
return { snippet: null, spans: [] };
|
|
139
|
+
let best = -1;
|
|
140
|
+
let bestHeld = 0;
|
|
141
|
+
for (const t of terms) {
|
|
142
|
+
const i = s.bodyFold.indexOf(t);
|
|
143
|
+
if (i === -1)
|
|
144
|
+
continue;
|
|
145
|
+
const from = Math.max(0, i - SNIPPET_BEFORE);
|
|
146
|
+
const win = s.bodyFold.slice(from, i + SNIPPET_AFTER);
|
|
147
|
+
const held = terms.filter((x) => win.includes(x)).length;
|
|
148
|
+
if (held > bestHeld) {
|
|
149
|
+
best = i;
|
|
150
|
+
bestHeld = held;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const at = best === -1 ? 0 : best;
|
|
154
|
+
// Start at a word boundary inside the lead-in, so the window never opens mid-word.
|
|
155
|
+
let start = Math.max(0, at - SNIPPET_BEFORE);
|
|
156
|
+
if (start > 0) {
|
|
157
|
+
const sp = body.slice(start, at).search(/\s/);
|
|
158
|
+
if (sp !== -1)
|
|
159
|
+
start += sp + 1;
|
|
160
|
+
}
|
|
161
|
+
const end = Math.min(body.length, at + SNIPPET_AFTER);
|
|
162
|
+
const collapse = (x) => x.replace(/\s+/g, ' ');
|
|
163
|
+
const lead = start > 0 ? '…' : '';
|
|
164
|
+
const tail = end < body.length ? '…' : '';
|
|
165
|
+
const text = `${lead}${collapse(body.slice(start, end)).trim()}${tail}`;
|
|
166
|
+
const folded = `${lead}${collapse(s.bodyFold.slice(start, end)).trim()}${tail}`;
|
|
167
|
+
return { snippet: text, spans: best === -1 ? [] : spansOf(folded, terms) };
|
|
168
|
+
}
|
|
169
|
+
/** Head and tail of a long text, joined by an elision line. */
|
|
170
|
+
export function capText(text, max) {
|
|
171
|
+
if (text.length <= max)
|
|
172
|
+
return text;
|
|
173
|
+
const head = Math.floor(max * 0.75);
|
|
174
|
+
return `${text.slice(0, head)}\n…\n${text.slice(text.length - (max - head))}`;
|
|
175
|
+
}
|
|
176
|
+
const yieldTurn = () => new Promise((r) => setImmediate(r));
|
|
177
|
+
export class SearchIndex {
|
|
178
|
+
byType = new Map();
|
|
179
|
+
charsByType = new Map();
|
|
180
|
+
leftOut = new Map();
|
|
181
|
+
maxBodyChars;
|
|
182
|
+
maxCharsPerType;
|
|
183
|
+
constructor(opts = {}) {
|
|
184
|
+
this.maxBodyChars = opts.maxBodyChars ?? 64 * 1024;
|
|
185
|
+
this.maxCharsPerType = opts.maxCharsPerType ?? 8 * 1024 * 1024;
|
|
186
|
+
for (const t of SEARCH_TYPES) {
|
|
187
|
+
this.byType.set(t, new Map());
|
|
188
|
+
this.charsByType.set(t, 0);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
mapOf(type) {
|
|
192
|
+
return this.byType.get(type);
|
|
193
|
+
}
|
|
194
|
+
/** Entities of `type` held. */
|
|
195
|
+
count(type) {
|
|
196
|
+
return this.mapOf(type).size;
|
|
197
|
+
}
|
|
198
|
+
/** Body characters of `type` held (the budget {@link SearchIndexOptions.maxCharsPerType} bounds). */
|
|
199
|
+
chars(type) {
|
|
200
|
+
return this.charsByType.get(type) ?? 0;
|
|
201
|
+
}
|
|
202
|
+
has(type, id) {
|
|
203
|
+
return this.mapOf(type).has(id);
|
|
204
|
+
}
|
|
205
|
+
ids(type) {
|
|
206
|
+
return [...this.mapOf(type).keys()];
|
|
207
|
+
}
|
|
208
|
+
/** Add or replace one entity. */
|
|
209
|
+
upsert(input) {
|
|
210
|
+
this.put(input);
|
|
211
|
+
this.evict(input.type);
|
|
212
|
+
}
|
|
213
|
+
put(input) {
|
|
214
|
+
const doc = input.body.length > this.maxBodyChars ? { ...input, body: capText(input.body, this.maxBodyChars) } : input;
|
|
215
|
+
const map = this.mapOf(doc.type);
|
|
216
|
+
const prev = map.get(doc.id);
|
|
217
|
+
if (prev !== undefined && prev.doc.title === doc.title && prev.doc.body === doc.body) {
|
|
218
|
+
// Unchanged text: keep the folded copies, take the new facts (status, project, time).
|
|
219
|
+
prev.doc = doc;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this.remove(doc.type, doc.id);
|
|
223
|
+
map.set(doc.id, { doc, titleFold: fold(doc.title), bodyFold: fold(doc.body) });
|
|
224
|
+
this.charsByType.set(doc.type, this.chars(doc.type) + doc.body.length);
|
|
225
|
+
}
|
|
226
|
+
remove(type, id) {
|
|
227
|
+
const map = this.mapOf(type);
|
|
228
|
+
const prev = map.get(id);
|
|
229
|
+
if (prev === undefined)
|
|
230
|
+
return;
|
|
231
|
+
map.delete(id);
|
|
232
|
+
this.charsByType.set(type, this.chars(type) - prev.doc.body.length);
|
|
233
|
+
}
|
|
234
|
+
/** Remove every entity of `type` whose id `drop` accepts. */
|
|
235
|
+
removeWhere(type, drop) {
|
|
236
|
+
for (const id of this.ids(type))
|
|
237
|
+
if (drop(id))
|
|
238
|
+
this.remove(type, id);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* A full re-read of a source: hold what it holds, drop what it no longer does. Newest first, and
|
|
242
|
+
* only while the type's text budget lasts — the rest is never folded, so a re-read of a store past
|
|
243
|
+
* the budget costs one sort, not an evict per entity.
|
|
244
|
+
*/
|
|
245
|
+
replaceType(type, docs) {
|
|
246
|
+
const ordered = [...docs].sort((a, b) => (b.updatedAt ?? -1) - (a.updatedAt ?? -1));
|
|
247
|
+
const keep = new Set();
|
|
248
|
+
let chars = 0;
|
|
249
|
+
for (const d of ordered) {
|
|
250
|
+
const len = Math.min(d.body.length, this.maxBodyChars + 3);
|
|
251
|
+
if (chars + len > this.maxCharsPerType)
|
|
252
|
+
continue;
|
|
253
|
+
chars += len;
|
|
254
|
+
keep.add(d.id);
|
|
255
|
+
}
|
|
256
|
+
this.removeWhere(type, (id) => !keep.has(id));
|
|
257
|
+
for (const d of ordered)
|
|
258
|
+
if (keep.has(d.id))
|
|
259
|
+
this.put(d);
|
|
260
|
+
this.leftOut.set(type, ordered.length - keep.size);
|
|
261
|
+
}
|
|
262
|
+
/** Entities the last re-read of `type` left out to stay inside its text budget. */
|
|
263
|
+
leftOutOf(type) {
|
|
264
|
+
return this.leftOut.get(type) ?? 0;
|
|
265
|
+
}
|
|
266
|
+
/** Over budget: the least recently updated entities leave first (undated ones before dated). */
|
|
267
|
+
evict(type) {
|
|
268
|
+
if (this.chars(type) <= this.maxCharsPerType)
|
|
269
|
+
return;
|
|
270
|
+
const byAge = [...this.mapOf(type).values()].sort((a, b) => (a.doc.updatedAt ?? -1) - (b.doc.updatedAt ?? -1));
|
|
271
|
+
for (const s of byAge) {
|
|
272
|
+
if (this.chars(type) <= this.maxCharsPerType)
|
|
273
|
+
break;
|
|
274
|
+
this.remove(type, s.doc.id);
|
|
275
|
+
this.leftOut.set(type, this.leftOutOf(type) + 1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Scan `types` for `q`. Yields every {@link SLICE_MS}; throws the signal's reason when it aborts;
|
|
280
|
+
* stops at `budgetMs` with `partial: true`. Groups with no match are left out.
|
|
281
|
+
*/
|
|
282
|
+
async query(q, opts) {
|
|
283
|
+
const now = opts.now ?? (() => performance.now());
|
|
284
|
+
const at = opts.at ?? Date.now();
|
|
285
|
+
const started = now();
|
|
286
|
+
let slice = started;
|
|
287
|
+
let partial = false;
|
|
288
|
+
const groups = [];
|
|
289
|
+
outer: for (const type of SEARCH_TYPES) {
|
|
290
|
+
if (!opts.types.includes(type))
|
|
291
|
+
continue;
|
|
292
|
+
const matches = [];
|
|
293
|
+
// A snapshot: a write landing while the scan yields never changes what this scan walks.
|
|
294
|
+
const entries = [...this.mapOf(type).values()];
|
|
295
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
296
|
+
if ((i & 31) === 0) {
|
|
297
|
+
const t = now();
|
|
298
|
+
if (t - started >= opts.budgetMs) {
|
|
299
|
+
partial = true;
|
|
300
|
+
this.pushGroup(groups, type, matches, q, opts);
|
|
301
|
+
break outer;
|
|
302
|
+
}
|
|
303
|
+
if (t - slice >= SLICE_MS) {
|
|
304
|
+
await yieldTurn();
|
|
305
|
+
opts.signal?.throwIfAborted();
|
|
306
|
+
slice = now();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const s = entries[i];
|
|
310
|
+
const score = scoreOf(s, q.terms, at);
|
|
311
|
+
if (score > 0)
|
|
312
|
+
matches.push({ s, score });
|
|
313
|
+
}
|
|
314
|
+
this.pushGroup(groups, type, matches, q, opts);
|
|
315
|
+
}
|
|
316
|
+
opts.signal?.throwIfAborted();
|
|
317
|
+
return { groups, partial };
|
|
318
|
+
}
|
|
319
|
+
pushGroup(groups, type, matches, q, opts) {
|
|
320
|
+
if (matches.length === 0)
|
|
321
|
+
return;
|
|
322
|
+
matches.sort((a, b) => b.score - a.score || (b.s.doc.updatedAt ?? 0) - (a.s.doc.updatedAt ?? 0) || a.s.doc.id.localeCompare(b.s.doc.id));
|
|
323
|
+
const offset = opts.offset ?? 0;
|
|
324
|
+
const page = matches.slice(offset, offset + opts.limit);
|
|
325
|
+
const hits = page.map(({ s, score }) => {
|
|
326
|
+
const { snippet, spans } = snippetOf(s, q.terms);
|
|
327
|
+
return {
|
|
328
|
+
type,
|
|
329
|
+
id: s.doc.id,
|
|
330
|
+
title: s.doc.title,
|
|
331
|
+
title_spans: spansOf(s.titleFold, q.terms),
|
|
332
|
+
snippet,
|
|
333
|
+
snippet_spans: spans,
|
|
334
|
+
score: Math.round(score * 100) / 100,
|
|
335
|
+
updated_at: s.doc.updatedAt,
|
|
336
|
+
project_id: s.doc.projectId,
|
|
337
|
+
status: s.doc.status,
|
|
338
|
+
target: s.doc.target,
|
|
339
|
+
};
|
|
340
|
+
});
|
|
341
|
+
const next = offset + page.length;
|
|
342
|
+
groups.push({ type, total: matches.length, hits, next_cursor: next < matches.length ? String(next) : null });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=search-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-index.js","sourceRoot":"","sources":["../../src/api/search-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /search` (api-types 0.127.0): one query over runs, projects, repos, transcripts (chat
|
|
3
|
+
* transcripts and unit outputs), rules, memories and documents.
|
|
4
|
+
*
|
|
5
|
+
* The index ({@link SearchIndex}) is built LAZILY: nothing is read until the first search, and that
|
|
6
|
+
* read runs off the request path — the first request waits a short while for it and answers with
|
|
7
|
+
* the sources still reading marked `indexing`. After that the index follows the daemon:
|
|
8
|
+
* - engine frames: a run's lifecycle re-reads the run list (debounced), a finished unit's output is
|
|
9
|
+
* read once, a chat reply re-reads that chat's transcript, a closed chat leaves;
|
|
10
|
+
* - the daemon's own writes: a successful mutating request re-reads the sources its path can change
|
|
11
|
+
* (`noteWrite`), debounced;
|
|
12
|
+
* - sources written outside the daemon (memories, rules a worker lands, documents) are also re-read
|
|
13
|
+
* when a search finds them older than their TTL — in the background, never on the request.
|
|
14
|
+
* A source that fails to read is `unavailable` with its reason; the others still answer.
|
|
15
|
+
*/
|
|
16
|
+
import type { FastifyInstance } from 'fastify';
|
|
17
|
+
import type { ConformanceRule, CoreEvent, InteractiveDocIndexRow, MemoryItem, Project, RepoEntry, SearchResponse, SearchType, SessionView } from '../core/types.js';
|
|
18
|
+
import { SearchIndex, type SearchIndexOptions } from './search-index.js';
|
|
19
|
+
/** The sources the service reads. `chat` and `unit` both feed the `transcript` type. */
|
|
20
|
+
type Source = 'run' | 'project' | 'repo' | 'chat' | 'rule' | 'memory' | 'doc';
|
|
21
|
+
export interface SearchServiceDeps {
|
|
22
|
+
sessionsDetail: () => Promise<SessionView[]>;
|
|
23
|
+
workOutput: (unitId: string) => Promise<string | null>;
|
|
24
|
+
projectList: () => Promise<Project[]>;
|
|
25
|
+
listRepos: () => Promise<RepoEntry[]>;
|
|
26
|
+
listRules: () => Promise<ConformanceRule[]>;
|
|
27
|
+
/** The run→project and chat→project membership. */
|
|
28
|
+
projectOf: (id: string) => string | undefined;
|
|
29
|
+
/** What the daemon remembers about a run beyond the engine's row (unix SECONDS). */
|
|
30
|
+
runFacts?: {
|
|
31
|
+
chatIdFor(runId: string): string | undefined;
|
|
32
|
+
createdAtFor(runId: string): number | undefined;
|
|
33
|
+
endedAtFor(runId: string): number | undefined;
|
|
34
|
+
};
|
|
35
|
+
/** The chat transcript directory (`<id>.jsonl` per live chat); absent = no chat transcripts. */
|
|
36
|
+
chatsDir?: () => string;
|
|
37
|
+
/** The memory store (estate `memory.list`, shaped); absent = memories are not searched. */
|
|
38
|
+
memories?: () => Promise<MemoryItem[]>;
|
|
39
|
+
/** The documents index; absent = documents are not searched. */
|
|
40
|
+
docs?: () => Promise<InteractiveDocIndexRow[]>;
|
|
41
|
+
log?: (m: string) => void;
|
|
42
|
+
index?: SearchIndexOptions;
|
|
43
|
+
ttlMs?: Partial<Record<Source, number>>;
|
|
44
|
+
/** Write/frame bursts coalesce for this long before a re-read (ms). */
|
|
45
|
+
debounceMs?: number;
|
|
46
|
+
/** How long the first search waits for a source's first read (ms). */
|
|
47
|
+
firstReadWaitMs?: number;
|
|
48
|
+
/** The query's hard time budget (ms). */
|
|
49
|
+
budgetMs?: number;
|
|
50
|
+
}
|
|
51
|
+
export declare class SearchService {
|
|
52
|
+
readonly index: SearchIndex;
|
|
53
|
+
private readonly deps;
|
|
54
|
+
private readonly states;
|
|
55
|
+
/** Off until the first search: before it, frames and writes cost nothing. */
|
|
56
|
+
private active;
|
|
57
|
+
private closed;
|
|
58
|
+
/** The last run list, for titling unit transcripts. */
|
|
59
|
+
private runsById;
|
|
60
|
+
/** Unit outputs read (or found empty) — a backfill does not read them again; a `unitDone` does. */
|
|
61
|
+
private readonly unitsTried;
|
|
62
|
+
private readonly unitQueue;
|
|
63
|
+
private unitWorkers;
|
|
64
|
+
private readonly chatTimers;
|
|
65
|
+
constructor(deps: SearchServiceDeps);
|
|
66
|
+
private state;
|
|
67
|
+
private ttl;
|
|
68
|
+
/** Whether the source exists on this daemon. */
|
|
69
|
+
private available;
|
|
70
|
+
close(): void;
|
|
71
|
+
/** One engine frame (the daemon's fan-in calls this for every frame; cheap until the first search). */
|
|
72
|
+
ingest(event: CoreEvent): void;
|
|
73
|
+
/** A mutating request answered 2xx/3xx: re-read what its path can change. */
|
|
74
|
+
noteWrite(method: string, url: string): void;
|
|
75
|
+
private markDirty;
|
|
76
|
+
private markChat;
|
|
77
|
+
/** Re-read one source (coalesced: a refresh asked for mid-read runs once more after it). */
|
|
78
|
+
private refresh;
|
|
79
|
+
/** Before a query: read what was never read, re-read what is past its TTL (both off the request). */
|
|
80
|
+
private ensure;
|
|
81
|
+
private load;
|
|
82
|
+
private runUpdatedAt;
|
|
83
|
+
private loadRuns;
|
|
84
|
+
private enqueueUnit;
|
|
85
|
+
private drainUnits;
|
|
86
|
+
private readUnit;
|
|
87
|
+
private loadChats;
|
|
88
|
+
/** One chat's transcript file: its head and tail (bounded), as one entity. */
|
|
89
|
+
private readChat;
|
|
90
|
+
search(q: string, opts: {
|
|
91
|
+
types: readonly SearchType[];
|
|
92
|
+
limit: number;
|
|
93
|
+
offset: number;
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
}): Promise<SearchResponse>;
|
|
96
|
+
private sourceState;
|
|
97
|
+
}
|
|
98
|
+
/** Parse `types`: comma-separated; unknown → `null`. */
|
|
99
|
+
export declare function parseTypes(raw: string | undefined): SearchType[] | null;
|
|
100
|
+
export declare function registerSearchRoutes(app: FastifyInstance, service: SearchService): void;
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=search-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-routes.d.ts","sourceRoot":"","sources":["../../src/api/search-routes.ts"],"names":[],"mappings":"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"}
|