@unson/brainbase-mcp 0.41.3 → 0.41.6
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/judgment-history-http.js +52 -11
- package/dist/judgment-history.d.ts +70 -2
- package/dist/judgment-history.js +381 -122
- package/package.json +2 -2
- package/ui/brainbase-tokens.css +4 -0
- package/ui/foundation-overview.js +30 -11
- package/ui/judgment-history.css +250 -23
- package/ui/judgment-history.js +497 -115
- package/ui/local-web-shell.js +4 -1
- package/ui/people-and-companies-view.css +143 -0
- package/ui/people-and-companies-view.js +87 -11
- package/ui/workspace-kit.css +43 -0
- package/ui/workspace-kit.js +131 -4
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import { JUDGMENT_HISTORY_ENTRYPOINTS, createJudgmentHistoryReader, JudgmentHistoryFeedbackConflictError } from './judgment-history.js';
|
|
1
|
+
import { JUDGMENT_HISTORY_ENTRYPOINTS, MAX_JUDGMENT_HISTORY_RESOLUTION_BATCH, createJudgmentHistoryReader, judgmentHistoryRequestTimings, JudgmentHistoryFeedbackConflictError, withJudgmentHistoryRequestTimings } from './judgment-history.js';
|
|
2
2
|
import { LocalWebHttpError, readJsonObjectBody, writeJson } from './local-web-security.js';
|
|
3
3
|
export const JUDGMENT_HISTORY_HTTP_VERSION = 'judgment-history-http.v1';
|
|
4
4
|
export const JUDGMENT_HISTORY_HTTP_PREFIX = '/api/judgment-history';
|
|
5
5
|
export const JUDGMENT_HISTORY_FEEDBACK_BODY_LIMIT_BYTES = 64 * 1024;
|
|
6
|
+
function serverTimingHeader(timings, handlerMs) {
|
|
7
|
+
const parts = [`total;dur=${handlerMs.toFixed(1)}`];
|
|
8
|
+
if (timings) {
|
|
9
|
+
parts.push(`journal;dur=${timings.journal_ms.toFixed(1)}`);
|
|
10
|
+
parts.push(`page;dur=${timings.page_ms.toFixed(1)}`);
|
|
11
|
+
parts.push(`cache;desc="hits=${timings.cache_hits},misses=${timings.cache_misses}"`);
|
|
12
|
+
parts.push(`parallel;desc="${timings.parallel_limit}"`);
|
|
13
|
+
}
|
|
14
|
+
parts.push('order;desc="newest-first"');
|
|
15
|
+
return parts.join(', ');
|
|
16
|
+
}
|
|
6
17
|
function methodNotAllowed() {
|
|
7
18
|
const error = new Error('Use GET or POST');
|
|
8
19
|
error.statusCode = 405;
|
|
@@ -14,6 +25,8 @@ function matchRoute(pathname, basePath) {
|
|
|
14
25
|
return { name: 'home' };
|
|
15
26
|
if (pathname === `${basePath}/feedback`)
|
|
16
27
|
return { name: 'feedback' };
|
|
28
|
+
if (pathname === `${basePath}/resolutions`)
|
|
29
|
+
return { name: 'resolutions' };
|
|
17
30
|
if (!pathname.startsWith(`${basePath}/records/`))
|
|
18
31
|
return null;
|
|
19
32
|
const encoded = pathname.slice(`${basePath}/records/`.length);
|
|
@@ -48,6 +61,17 @@ function readerError(statusCode, code, message) {
|
|
|
48
61
|
error.code = code;
|
|
49
62
|
return error;
|
|
50
63
|
}
|
|
64
|
+
function parseResolutionIds(url) {
|
|
65
|
+
for (const key of url.searchParams.keys()) {
|
|
66
|
+
if (key !== 'id')
|
|
67
|
+
throw readerError(400, 'unknown_query', `Unknown query parameter: ${key}`);
|
|
68
|
+
}
|
|
69
|
+
const ids = url.searchParams.getAll('id');
|
|
70
|
+
if (ids.length < 1 || ids.length > MAX_JUDGMENT_HISTORY_RESOLUTION_BATCH) {
|
|
71
|
+
throw readerError(400, 'invalid_resolution_ids', `id must be repeated between 1 and ${MAX_JUDGMENT_HISTORY_RESOLUTION_BATCH} times`);
|
|
72
|
+
}
|
|
73
|
+
return ids;
|
|
74
|
+
}
|
|
51
75
|
function parseQuery(url) {
|
|
52
76
|
const allowed = new Set(['period', 'project', 'entrypoint', 'cursor', 'limit']);
|
|
53
77
|
for (const key of url.searchParams.keys()) {
|
|
@@ -90,7 +114,18 @@ export function createJudgmentHistoryHttpHandler(options) {
|
|
|
90
114
|
}
|
|
91
115
|
const basePath = (options.basePath ?? JUDGMENT_HISTORY_HTTP_PREFIX).replace(/\/+$/u, '');
|
|
92
116
|
const reader = options.reader;
|
|
93
|
-
return
|
|
117
|
+
return (request, response) => withJudgmentHistoryRequestTimings(async () => {
|
|
118
|
+
const started = performance.now();
|
|
119
|
+
const writeTimed = (statusCode, body) => {
|
|
120
|
+
if (!response.headersSent) {
|
|
121
|
+
const scoped = judgmentHistoryRequestTimings();
|
|
122
|
+
const timings = scoped.bound
|
|
123
|
+
? scoped.timings
|
|
124
|
+
: (typeof reader.timings === 'function' ? reader.timings() : null);
|
|
125
|
+
response.setHeader('Server-Timing', serverTimingHeader(timings, performance.now() - started));
|
|
126
|
+
}
|
|
127
|
+
writeJson(response, statusCode, body);
|
|
128
|
+
};
|
|
94
129
|
const url = new URL(request.url ?? '/', 'http://localhost');
|
|
95
130
|
let route;
|
|
96
131
|
try {
|
|
@@ -98,7 +133,7 @@ export function createJudgmentHistoryHttpHandler(options) {
|
|
|
98
133
|
}
|
|
99
134
|
catch (error) {
|
|
100
135
|
const normalized = toError(error);
|
|
101
|
-
|
|
136
|
+
writeTimed(normalized.statusCode, {
|
|
102
137
|
error: { code: normalized.code, message: normalized.message }
|
|
103
138
|
});
|
|
104
139
|
return true;
|
|
@@ -125,24 +160,30 @@ export function createJudgmentHistoryHttpHandler(options) {
|
|
|
125
160
|
feedback_event: result.event,
|
|
126
161
|
...result.event
|
|
127
162
|
};
|
|
128
|
-
|
|
163
|
+
writeTimed(result.created ? 201 : 200, responseBody);
|
|
129
164
|
}
|
|
130
165
|
else {
|
|
131
166
|
if (request.method !== 'GET')
|
|
132
167
|
throw methodNotAllowed();
|
|
133
|
-
if (route.name === '
|
|
134
|
-
|
|
168
|
+
if (route.name === 'resolutions') {
|
|
169
|
+
if (typeof reader.findByResolutionIds !== 'function') {
|
|
170
|
+
throw readerError(501, 'resolution_batch_unavailable', 'This reader cannot look up resolution ids in one read');
|
|
171
|
+
}
|
|
172
|
+
writeTimed(200, await reader.findByResolutionIds(parseResolutionIds(url)));
|
|
173
|
+
}
|
|
174
|
+
else if (route.name === 'home') {
|
|
175
|
+
writeTimed(200, await reader.home(parseQuery(url)));
|
|
135
176
|
}
|
|
136
177
|
else {
|
|
137
178
|
const result = await reader.detail(route.recordId);
|
|
138
179
|
if (result.status === 'unavailable') {
|
|
139
|
-
|
|
180
|
+
writeTimed(200, result);
|
|
140
181
|
}
|
|
141
182
|
else if (result.status === 'available' && !result.record) {
|
|
142
183
|
throw readerError(404, 'judgment_history_not_found', 'No judgment history record matches this id');
|
|
143
184
|
}
|
|
144
185
|
else {
|
|
145
|
-
|
|
186
|
+
writeTimed(200, result);
|
|
146
187
|
}
|
|
147
188
|
}
|
|
148
189
|
}
|
|
@@ -150,19 +191,19 @@ export function createJudgmentHistoryHttpHandler(options) {
|
|
|
150
191
|
catch (error) {
|
|
151
192
|
const normalized = toError(error);
|
|
152
193
|
if (error instanceof JudgmentHistoryFeedbackConflictError) {
|
|
153
|
-
|
|
194
|
+
writeTimed(normalized.statusCode, {
|
|
154
195
|
error: { code: normalized.code, message: normalized.message },
|
|
155
196
|
feedback_event: error.existing
|
|
156
197
|
});
|
|
157
198
|
}
|
|
158
199
|
else {
|
|
159
|
-
|
|
200
|
+
writeTimed(normalized.statusCode, {
|
|
160
201
|
error: { code: normalized.code, message: normalized.message }
|
|
161
202
|
});
|
|
162
203
|
}
|
|
163
204
|
}
|
|
164
205
|
return true;
|
|
165
|
-
};
|
|
206
|
+
});
|
|
166
207
|
}
|
|
167
208
|
/** Convenience composition seam for adapters that want the HTTP contract without a local journal. */
|
|
168
209
|
export function createJudgmentHistoryHttpReader(options) {
|
|
@@ -85,11 +85,34 @@ export interface JudgmentHistorySourceSnapshot {
|
|
|
85
85
|
readonly coverage: JudgmentHistoryCoverage;
|
|
86
86
|
/** Stable source revision used to bind pagination cursors. */
|
|
87
87
|
readonly snapshot_id?: string;
|
|
88
|
+
/** Measurements for this read. Concurrent reads each carry their own. */
|
|
89
|
+
readonly timings?: JudgmentHistoryReadTimings;
|
|
90
|
+
}
|
|
91
|
+
export interface JudgmentHistoryReadTimings {
|
|
92
|
+
/** Wall time of the latest home, detail, or batch read, including paging. */
|
|
93
|
+
readonly total_ms: number;
|
|
94
|
+
/** Time spent discovering and reading journal artifacts. */
|
|
95
|
+
readonly journal_ms: number;
|
|
96
|
+
/** Time spent filtering and slicing the newest-first page. */
|
|
97
|
+
readonly page_ms: number;
|
|
98
|
+
readonly cache_hits: number;
|
|
99
|
+
readonly cache_misses: number;
|
|
100
|
+
readonly parallel_limit: number;
|
|
101
|
+
readonly scanned_files: number;
|
|
88
102
|
}
|
|
89
103
|
export interface JudgmentHistorySource {
|
|
90
104
|
readonly storage: JudgmentHistoryStorage;
|
|
91
105
|
readonly read: () => Promise<JudgmentHistorySourceSnapshot>;
|
|
92
|
-
|
|
106
|
+
/** Timings of the latest completed `read`. Overlapping reads use `snapshot.timings`. */
|
|
107
|
+
readonly timings?: () => JudgmentHistoryReadTimings | null;
|
|
108
|
+
}
|
|
109
|
+
/** Isolates Server-Timing for one HTTP request from other in-flight reads. */
|
|
110
|
+
export declare function withJudgmentHistoryRequestTimings<T>(fn: () => Promise<T>): Promise<T>;
|
|
111
|
+
/** Timings published by the in-flight request, when one is bound. */
|
|
112
|
+
export declare function judgmentHistoryRequestTimings(): {
|
|
113
|
+
readonly bound: boolean;
|
|
114
|
+
readonly timings: JudgmentHistoryReadTimings | null;
|
|
115
|
+
};
|
|
93
116
|
export interface JudgmentHistoryQuery {
|
|
94
117
|
readonly period?: JudgmentHistoryPeriod;
|
|
95
118
|
readonly project?: string;
|
|
@@ -128,9 +151,18 @@ export interface JudgmentHistoryReaderOptions {
|
|
|
128
151
|
/** Organization adapters provide trusted scope authorization here. */
|
|
129
152
|
readonly authorizeProject?: (project: string) => boolean | Promise<boolean>;
|
|
130
153
|
}
|
|
154
|
+
export interface JudgmentHistoryResolutionBatch {
|
|
155
|
+
readonly contract_version: typeof JUDGMENT_HISTORY_CONTRACT_VERSION;
|
|
156
|
+
readonly status: JudgmentHistoryStatus;
|
|
157
|
+
readonly records: readonly (JudgmentHistoryRecord | null)[];
|
|
158
|
+
readonly coverage: JudgmentHistoryCoverage;
|
|
159
|
+
}
|
|
131
160
|
export interface JudgmentHistoryReader {
|
|
132
161
|
readonly home: (query?: JudgmentHistoryQuery) => Promise<JudgmentHistoryHome>;
|
|
133
162
|
readonly detail: (recordId: string) => Promise<JudgmentHistoryDetail>;
|
|
163
|
+
/** One journal read for many resolution ids. `records` keeps the request order. */
|
|
164
|
+
readonly findByResolutionIds: (resolutionIds: readonly string[]) => Promise<JudgmentHistoryResolutionBatch>;
|
|
165
|
+
readonly timings: () => JudgmentHistoryReadTimings | null;
|
|
134
166
|
}
|
|
135
167
|
export declare class JudgmentHistoryReaderError extends Error {
|
|
136
168
|
readonly statusCode: number;
|
|
@@ -144,7 +176,34 @@ export interface LocalJudgmentHistorySourceOptions {
|
|
|
144
176
|
readonly max_files?: number;
|
|
145
177
|
/** Maximum size of one journal artifact, in bytes. */
|
|
146
178
|
readonly max_record_bytes?: number;
|
|
147
|
-
|
|
179
|
+
/** Maximum journal artifact reads in flight. */
|
|
180
|
+
readonly read_parallel?: number;
|
|
181
|
+
/** Reuses parsed artifacts while (session, turn, mtime) is unchanged. */
|
|
182
|
+
readonly cache?: JudgmentHistoryJournalScanCache;
|
|
183
|
+
}
|
|
184
|
+
export declare const DEFAULT_LOCAL_JUDGMENT_HISTORY_READ_PARALLEL = 8;
|
|
185
|
+
export declare const MAX_JUDGMENT_HISTORY_RESOLUTION_BATCH = 100;
|
|
186
|
+
/** Parsed artifacts kept for one local source. The oldest entry is dropped past this. */
|
|
187
|
+
export declare const DEFAULT_JUDGMENT_HISTORY_JOURNAL_CACHE_LIMIT = 4096;
|
|
188
|
+
/**
|
|
189
|
+
* Remembers parsed journal artifacts for one local source.
|
|
190
|
+
* The key is (session, turn, file name, mtime). A later read of the same turn
|
|
191
|
+
* misses when that file's mtime changes, and the previous bytes are not reused.
|
|
192
|
+
* A lookup refreshes recency. Past `limit`, the least recently used entry is dropped.
|
|
193
|
+
*/
|
|
194
|
+
export declare class JudgmentHistoryJournalScanCache {
|
|
195
|
+
#private;
|
|
196
|
+
constructor(limit?: number);
|
|
197
|
+
get limit(): number;
|
|
198
|
+
get size(): number;
|
|
199
|
+
lookup(session: string, turn: string, fileName: string, mtimeNs: bigint): BoundedJsonResult | null;
|
|
200
|
+
store(session: string, turn: string, fileName: string, mtimeNs: bigint, result: BoundedJsonResult): void;
|
|
201
|
+
noteMiss(): void;
|
|
202
|
+
get hits(): number;
|
|
203
|
+
get misses(): number;
|
|
204
|
+
}
|
|
205
|
+
/** Runs `map` with at most `limit` calls in flight. Results keep input order. */
|
|
206
|
+
export declare function mapWithConcurrencyLimit<T, R>(items: readonly T[], limit: number, map: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
148
207
|
export interface LocalJudgmentHistoryFeedbackWriterOptions {
|
|
149
208
|
readonly root: string;
|
|
150
209
|
readonly reader: JudgmentHistoryReader;
|
|
@@ -160,9 +219,17 @@ export declare class JudgmentHistoryFeedbackConflictError extends JudgmentHistor
|
|
|
160
219
|
readonly existing: JudgmentHistoryFeedbackEvent;
|
|
161
220
|
constructor(existing: JudgmentHistoryFeedbackEvent);
|
|
162
221
|
}
|
|
222
|
+
interface JsonObject {
|
|
223
|
+
[key: string]: unknown;
|
|
224
|
+
}
|
|
163
225
|
export declare const DEFAULT_LOCAL_JUDGMENT_HISTORY_SCAN_CAP = 4096;
|
|
164
226
|
export declare const DEFAULT_LOCAL_JUDGMENT_HISTORY_RECORD_BYTES: number;
|
|
165
227
|
export declare function canonicalJson(value: unknown): string;
|
|
228
|
+
interface BoundedJsonResult {
|
|
229
|
+
readonly value: JsonObject | null;
|
|
230
|
+
readonly missing: boolean;
|
|
231
|
+
readonly invalid: boolean;
|
|
232
|
+
}
|
|
166
233
|
/** Reads only the configured local owner journal; it never searches or uploads another journal. */
|
|
167
234
|
export declare function createLocalJudgmentHistorySource(options: LocalJudgmentHistorySourceOptions): JudgmentHistorySource;
|
|
168
235
|
/**
|
|
@@ -176,3 +243,4 @@ export declare function appendLocalJudgmentHistoryFeedback(options: LocalJudgmen
|
|
|
176
243
|
}): Promise<JudgmentHistoryFeedbackWriteResult>;
|
|
177
244
|
/** Reader shared by local, organization, Mana, and Company OS adapters. */
|
|
178
245
|
export declare function createJudgmentHistoryReader(options: JudgmentHistoryReaderOptions): JudgmentHistoryReader;
|
|
246
|
+
export {};
|