@unson/brainbase-mcp 0.41.13 → 0.43.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.
- package/dist/local-web-host.d.ts +6 -0
- package/dist/local-web-host.js +35 -1
- package/dist/personal-memory.d.ts +101 -0
- package/dist/personal-memory.js +204 -0
- package/dist/ssot.d.ts +3 -1
- package/dist/ssot.js +4 -0
- package/package.json +11 -2
- package/ui/area-nav.css +56 -4
- package/ui/area-nav.js +130 -10
- package/ui/inbox.css +189 -0
- package/ui/inbox.js +578 -0
- package/ui/judgment-history.js +74 -3
- package/ui/local-web-shell.css +4 -2
- package/ui/local-web-shell.js +107 -51
- package/ui/personal-memory-http-port.js +72 -0
- package/ui/personal-memory.css +254 -0
- package/ui/personal-memory.js +677 -0
package/dist/local-web-host.d.ts
CHANGED
|
@@ -195,6 +195,12 @@ export declare function createWorldModelReadModule(context: LocalWebModuleContex
|
|
|
195
195
|
* correction is never saved through a path that skipped the host check.
|
|
196
196
|
*/
|
|
197
197
|
export declare function createGraphWebModule(context: LocalWebModuleContext): LocalWebModule;
|
|
198
|
+
/**
|
|
199
|
+
* 「あなたの記憶」: the owner's `personal-kg.jsonl`, read only. The memory
|
|
200
|
+
* bodies are private, so these reads require the launch token and the same
|
|
201
|
+
* origin like every write on this host.
|
|
202
|
+
*/
|
|
203
|
+
export declare function createPersonalMemoryModule(context: LocalWebModuleContext): LocalWebModule;
|
|
198
204
|
/** 今日 (value proofs), 目的と現状 (objectives + world model), プロジェクトと関係者 and 情報と関係 (Graph). */
|
|
199
205
|
export declare function defaultLocalWebModules(context: LocalWebModuleContext): LocalWebModule[];
|
|
200
206
|
/** Local single-owner host. The caller owns `listen()` and must bind to a loopback address. */
|
package/dist/local-web-host.js
CHANGED
|
@@ -12,6 +12,7 @@ import { createJudgmentHistoryHttpHandler, JUDGMENT_HISTORY_HTTP_PREFIX } from '
|
|
|
12
12
|
import { createJudgmentHistoryReader, createLocalJudgmentHistoryFeedbackWriter, createLocalJudgmentHistorySource } from './judgment-history.js';
|
|
13
13
|
import { defaultJudgmentJournalRoot, JudgmentValueProofJournalCache } from './judgment-value-proof-review.js';
|
|
14
14
|
import { createMeetingMinutesHttpHandler } from './meeting-minutes-http.js';
|
|
15
|
+
import { createPersonalMemoryHttpHandler, createPersonalMemoryJudgmentIndex, readLocalPersonalMemories } from './personal-memory.js';
|
|
15
16
|
import { createMeetingMinutesLineageHttpHandler } from './meeting-minutes-lineage-http.js';
|
|
16
17
|
import { createMeetingMinutesLineageCorrectionHook, } from './meeting-minutes-lineage-lifecycle.js';
|
|
17
18
|
import { createMeetingMinutesStore } from './meeting-minutes.js';
|
|
@@ -891,6 +892,38 @@ export function createGraphWebModule(context) {
|
|
|
891
892
|
handle: handler
|
|
892
893
|
};
|
|
893
894
|
}
|
|
895
|
+
/**
|
|
896
|
+
* 「あなたの記憶」: the owner's `personal-kg.jsonl`, read only. The memory
|
|
897
|
+
* bodies are private, so these reads require the launch token and the same
|
|
898
|
+
* origin like every write on this host.
|
|
899
|
+
*/
|
|
900
|
+
export function createPersonalMemoryModule(context) {
|
|
901
|
+
const readMemories = () => readLocalPersonalMemories(context.dataDir);
|
|
902
|
+
const index = createPersonalMemoryJudgmentIndex({
|
|
903
|
+
source: createLocalJudgmentHistorySource({
|
|
904
|
+
root: context.journalRoot,
|
|
905
|
+
entrypoint: context.judgmentHistoryEntrypoint ?? 'codex'
|
|
906
|
+
}),
|
|
907
|
+
memoryIds: async () => {
|
|
908
|
+
const list = await readMemories();
|
|
909
|
+
return list.status === 'ok' ? list.items.map((item) => item.id) : null;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
return {
|
|
913
|
+
id: 'personal-memory',
|
|
914
|
+
uiFiles: ['personal-memory.js', 'personal-memory-http-port.js', 'personal-memory.css'],
|
|
915
|
+
handle: createPersonalMemoryHttpHandler({
|
|
916
|
+
readMemories,
|
|
917
|
+
index,
|
|
918
|
+
authorize(request) {
|
|
919
|
+
if (isTrustedLocalWrite(request, context.token))
|
|
920
|
+
return;
|
|
921
|
+
throw rejectUntrustedWrite(request, context.token)
|
|
922
|
+
?? new LocalWebHttpError(403, 'web_token_required', 'A valid launch token is required');
|
|
923
|
+
}
|
|
924
|
+
})
|
|
925
|
+
};
|
|
926
|
+
}
|
|
894
927
|
/** 今日 (value proofs), 目的と現状 (objectives + world model), プロジェクトと関係者 and 情報と関係 (Graph). */
|
|
895
928
|
export function defaultLocalWebModules(context) {
|
|
896
929
|
return [
|
|
@@ -900,7 +933,8 @@ export function defaultLocalWebModules(context) {
|
|
|
900
933
|
createMeetingMinutesModule(context),
|
|
901
934
|
createObjectiveFoundationModule(context),
|
|
902
935
|
createWorldModelReadModule(context),
|
|
903
|
-
createGraphWebModule(context)
|
|
936
|
+
createGraphWebModule(context),
|
|
937
|
+
createPersonalMemoryModule(context)
|
|
904
938
|
];
|
|
905
939
|
}
|
|
906
940
|
function escapeAttribute(value) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { JudgmentHistorySource } from './judgment-history.js';
|
|
3
|
+
import type { PersonalKgEntry } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* 「あなたの記憶」 (docs/specs/personal-memory-viewer.md): the owner's own
|
|
6
|
+
* memories read from the local `personal-kg.jsonl`, and the judgments that
|
|
7
|
+
* used each one, found back from the judgment journal's `personal_knowledge`
|
|
8
|
+
* references. Read only. A memory body travels in the response body only,
|
|
9
|
+
* never in a URL or a log line.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PERSONAL_MEMORY_HTTP_PREFIX = "/api/personal-memory";
|
|
12
|
+
export type PersonalMemoryState = 'active' | 'superseded' | 'retracted' | 'unrecorded';
|
|
13
|
+
export interface PersonalMemoryItem {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly kind: string | null;
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly tags: readonly string[];
|
|
18
|
+
readonly state: PersonalMemoryState;
|
|
19
|
+
readonly supersededBy: string | null;
|
|
20
|
+
readonly origin: {
|
|
21
|
+
readonly organizationLabel: string | null;
|
|
22
|
+
readonly projectLabel: string | null;
|
|
23
|
+
readonly workplace: string | null;
|
|
24
|
+
readonly inferred: boolean;
|
|
25
|
+
};
|
|
26
|
+
readonly occurredAt: string | null;
|
|
27
|
+
readonly updatedAt: string | null;
|
|
28
|
+
readonly recallScope: 'organization' | 'general' | 'unknown';
|
|
29
|
+
readonly history: readonly {
|
|
30
|
+
readonly state: PersonalMemoryState;
|
|
31
|
+
readonly at: string;
|
|
32
|
+
readonly reason: string | null;
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
export type PersonalMemoryList = {
|
|
36
|
+
readonly status: 'ok';
|
|
37
|
+
readonly items: readonly PersonalMemoryItem[];
|
|
38
|
+
} | {
|
|
39
|
+
readonly status: 'unavailable';
|
|
40
|
+
readonly reason: string;
|
|
41
|
+
};
|
|
42
|
+
export interface PersonalMemoryJudgment {
|
|
43
|
+
readonly decisionId: string;
|
|
44
|
+
readonly title: string | null;
|
|
45
|
+
readonly at: string;
|
|
46
|
+
readonly kind: string | null;
|
|
47
|
+
}
|
|
48
|
+
export type PersonalMemoryJudgments = {
|
|
49
|
+
readonly status: 'ok';
|
|
50
|
+
readonly items: readonly PersonalMemoryJudgment[];
|
|
51
|
+
readonly total: number;
|
|
52
|
+
} | {
|
|
53
|
+
readonly status: 'unavailable';
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
};
|
|
56
|
+
export type PersonalMemoryJudgmentCounts = {
|
|
57
|
+
readonly status: 'ok';
|
|
58
|
+
readonly counts: Readonly<Record<string, number>>;
|
|
59
|
+
} | {
|
|
60
|
+
readonly status: 'unavailable';
|
|
61
|
+
readonly reason: string;
|
|
62
|
+
};
|
|
63
|
+
/** The local writer keeps no state, origin organization or recall scope: the spec's fixed values. */
|
|
64
|
+
export declare function personalMemoryFromEntry(entry: PersonalKgEntry): PersonalMemoryItem;
|
|
65
|
+
/** An empty file is zero memories; a missing or malformed one is unavailable, never zero. */
|
|
66
|
+
export declare function readLocalPersonalMemories(dataDir: string): Promise<PersonalMemoryList>;
|
|
67
|
+
export interface PersonalMemoryJudgmentIndex {
|
|
68
|
+
judgmentsUsing(id: string, limit: number): Promise<PersonalMemoryJudgments>;
|
|
69
|
+
counts(): Promise<PersonalMemoryJudgmentCounts>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Finds judgments back from their `personal_knowledge` references. A
|
|
73
|
+
* reference names a memory by its id, so the journal is bound to these
|
|
74
|
+
* memories only when at least one recorded reference is one of their ids.
|
|
75
|
+
* When every reference names a memory kept elsewhere (for example the
|
|
76
|
+
* organization's personal KG), "not used" cannot be told from "used under
|
|
77
|
+
* another id", and the answer is unavailable instead of zero.
|
|
78
|
+
*/
|
|
79
|
+
export declare function createPersonalMemoryJudgmentIndex(options: {
|
|
80
|
+
readonly source: JudgmentHistorySource;
|
|
81
|
+
/** The memory ids, or null when the memories cannot be read. */
|
|
82
|
+
readonly memoryIds: () => Promise<readonly string[] | null>;
|
|
83
|
+
/**
|
|
84
|
+
* How long one journal read answers later requests. A large journal takes
|
|
85
|
+
* tens of seconds to scan, and one opened memory asks for its judgments and
|
|
86
|
+
* the list's counts at the same time.
|
|
87
|
+
*/
|
|
88
|
+
readonly reuseMs?: number;
|
|
89
|
+
readonly now?: () => number;
|
|
90
|
+
}): PersonalMemoryJudgmentIndex;
|
|
91
|
+
/**
|
|
92
|
+
* GET routes under `/api/personal-memory`. `authorize` runs before any read
|
|
93
|
+
* and throws to refuse the request (the local host requires its launch token
|
|
94
|
+
* on these reads, not only on writes).
|
|
95
|
+
*/
|
|
96
|
+
export declare function createPersonalMemoryHttpHandler(options: {
|
|
97
|
+
readonly readMemories: () => Promise<PersonalMemoryList>;
|
|
98
|
+
readonly index: PersonalMemoryJudgmentIndex;
|
|
99
|
+
readonly authorize: (request: IncomingMessage) => void;
|
|
100
|
+
readonly basePath?: string;
|
|
101
|
+
}): (request: IncomingMessage, response: ServerResponse) => Promise<boolean>;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { requestUrl, writeJson } from './local-web-security.js';
|
|
4
|
+
import { readPersonalKg } from './ssot.js';
|
|
5
|
+
/**
|
|
6
|
+
* 「あなたの記憶」 (docs/specs/personal-memory-viewer.md): the owner's own
|
|
7
|
+
* memories read from the local `personal-kg.jsonl`, and the judgments that
|
|
8
|
+
* used each one, found back from the judgment journal's `personal_knowledge`
|
|
9
|
+
* references. Read only. A memory body travels in the response body only,
|
|
10
|
+
* never in a URL or a log line.
|
|
11
|
+
*/
|
|
12
|
+
export const PERSONAL_MEMORY_HTTP_PREFIX = '/api/personal-memory';
|
|
13
|
+
/** The local writer keeps no state, origin organization or recall scope: the spec's fixed values. */
|
|
14
|
+
export function personalMemoryFromEntry(entry) {
|
|
15
|
+
return {
|
|
16
|
+
id: entry.id,
|
|
17
|
+
kind: entry.type,
|
|
18
|
+
text: entry.text,
|
|
19
|
+
tags: [...entry.tags ?? []],
|
|
20
|
+
state: 'unrecorded',
|
|
21
|
+
supersededBy: null,
|
|
22
|
+
origin: { organizationLabel: '手元', projectLabel: null, workplace: null, inferred: false },
|
|
23
|
+
occurredAt: null,
|
|
24
|
+
updatedAt: entry.updatedAt ?? null,
|
|
25
|
+
recallScope: 'general',
|
|
26
|
+
history: []
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function newestFirst(left, right) {
|
|
30
|
+
if (left === right)
|
|
31
|
+
return 0;
|
|
32
|
+
if (left === null)
|
|
33
|
+
return 1;
|
|
34
|
+
if (right === null)
|
|
35
|
+
return -1;
|
|
36
|
+
return left < right ? 1 : -1;
|
|
37
|
+
}
|
|
38
|
+
/** An empty file is zero memories; a missing or malformed one is unavailable, never zero. */
|
|
39
|
+
export async function readLocalPersonalMemories(dataDir) {
|
|
40
|
+
try {
|
|
41
|
+
await stat(join(dataDir, 'personal-kg.jsonl'));
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return { status: 'unavailable', reason: 'personal_kg_missing' };
|
|
45
|
+
}
|
|
46
|
+
let entries;
|
|
47
|
+
try {
|
|
48
|
+
entries = await readPersonalKg(dataDir);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return { status: 'unavailable', reason: 'personal_kg_invalid' };
|
|
52
|
+
}
|
|
53
|
+
const items = entries.map(personalMemoryFromEntry);
|
|
54
|
+
items.sort((left, right) => newestFirst(left.updatedAt, right.updatedAt));
|
|
55
|
+
return { status: 'ok', items };
|
|
56
|
+
}
|
|
57
|
+
function personalReferences(record) {
|
|
58
|
+
const refs = new Set();
|
|
59
|
+
for (const reference of record.judgment.selected_references ?? []) {
|
|
60
|
+
if (reference.kind === 'personal_knowledge' && reference.ref)
|
|
61
|
+
refs.add(reference.ref);
|
|
62
|
+
}
|
|
63
|
+
return refs;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Finds judgments back from their `personal_knowledge` references. A
|
|
67
|
+
* reference names a memory by its id, so the journal is bound to these
|
|
68
|
+
* memories only when at least one recorded reference is one of their ids.
|
|
69
|
+
* When every reference names a memory kept elsewhere (for example the
|
|
70
|
+
* organization's personal KG), "not used" cannot be told from "used under
|
|
71
|
+
* another id", and the answer is unavailable instead of zero.
|
|
72
|
+
*/
|
|
73
|
+
export function createPersonalMemoryJudgmentIndex(options) {
|
|
74
|
+
async function build() {
|
|
75
|
+
const [snapshot, ids] = await Promise.all([options.source.read(), options.memoryIds()]);
|
|
76
|
+
if (ids === null)
|
|
77
|
+
return { status: 'unavailable', reason: 'personal_kg_unavailable' };
|
|
78
|
+
if (snapshot.status !== 'available' || !snapshot.records) {
|
|
79
|
+
return { status: 'unavailable', reason: `journal_${snapshot.status === 'partial' ? 'partial' : 'unavailable'}` };
|
|
80
|
+
}
|
|
81
|
+
const known = new Set(ids);
|
|
82
|
+
const byMemory = new Map(ids.map((id) => [id, []]));
|
|
83
|
+
let referenced = 0;
|
|
84
|
+
let bound = 0;
|
|
85
|
+
for (const record of snapshot.records) {
|
|
86
|
+
const refs = personalReferences(record);
|
|
87
|
+
for (const ref of refs) {
|
|
88
|
+
referenced += 1;
|
|
89
|
+
if (!known.has(ref))
|
|
90
|
+
continue;
|
|
91
|
+
bound += 1;
|
|
92
|
+
byMemory.get(ref)?.push({ decisionId: record.record_id, title: record.judgment.summary, at: record.recorded_at, kind: null });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (referenced > 0 && bound === 0)
|
|
96
|
+
return { status: 'unavailable', reason: 'reference_source_mismatch' };
|
|
97
|
+
for (const judgments of byMemory.values())
|
|
98
|
+
judgments.sort((left, right) => newestFirst(left.at, right.at));
|
|
99
|
+
return { status: 'ok', byMemory };
|
|
100
|
+
}
|
|
101
|
+
const reuseMs = options.reuseMs ?? 30_000;
|
|
102
|
+
const now = options.now ?? (() => Date.now());
|
|
103
|
+
let current = null;
|
|
104
|
+
function shared() {
|
|
105
|
+
if (current && now() - current.startedAt < reuseMs)
|
|
106
|
+
return current.promise;
|
|
107
|
+
const entry = { promise: build(), startedAt: now() };
|
|
108
|
+
current = entry;
|
|
109
|
+
// A failed read, a missing journal and unreadable memories are not reused: the owner may fix them now.
|
|
110
|
+
const forget = () => { if (current === entry)
|
|
111
|
+
current = null; };
|
|
112
|
+
entry.promise.then((index) => {
|
|
113
|
+
if (index.status === 'unavailable' && (index.reason === 'journal_unavailable' || index.reason === 'personal_kg_unavailable'))
|
|
114
|
+
forget();
|
|
115
|
+
}, forget);
|
|
116
|
+
return entry.promise;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
async judgmentsUsing(id, limit) {
|
|
120
|
+
const index = await shared();
|
|
121
|
+
if (index.status !== 'ok')
|
|
122
|
+
return index;
|
|
123
|
+
const judgments = index.byMemory.get(id) ?? [];
|
|
124
|
+
return { status: 'ok', items: judgments.slice(0, Math.max(0, limit)), total: judgments.length };
|
|
125
|
+
},
|
|
126
|
+
async counts() {
|
|
127
|
+
const index = await shared();
|
|
128
|
+
if (index.status !== 'ok')
|
|
129
|
+
return index;
|
|
130
|
+
return { status: 'ok', counts: Object.fromEntries([...index.byMemory].map(([id, judgments]) => [id, judgments.length])) };
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const DEFAULT_JUDGMENT_LIMIT = 5;
|
|
135
|
+
const MAX_JUDGMENT_LIMIT = 500;
|
|
136
|
+
/**
|
|
137
|
+
* GET routes under `/api/personal-memory`. `authorize` runs before any read
|
|
138
|
+
* and throws to refuse the request (the local host requires its launch token
|
|
139
|
+
* on these reads, not only on writes).
|
|
140
|
+
*/
|
|
141
|
+
export function createPersonalMemoryHttpHandler(options) {
|
|
142
|
+
const basePath = (options.basePath ?? PERSONAL_MEMORY_HTTP_PREFIX).replace(/\/+$/u, '');
|
|
143
|
+
return async (request, response) => {
|
|
144
|
+
const url = requestUrl(request);
|
|
145
|
+
if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`))
|
|
146
|
+
return false;
|
|
147
|
+
if (request.method !== 'GET') {
|
|
148
|
+
writeJson(response, 405, { error: { code: 'method_not_allowed', message: 'Use GET' } });
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
options.authorize(request);
|
|
152
|
+
const unavailable = (result) => writeJson(response, 503, { status: 'unavailable', reason: result.reason });
|
|
153
|
+
if (url.pathname === basePath) {
|
|
154
|
+
if (url.searchParams.get('view') === 'judgment-counts') {
|
|
155
|
+
const counts = await options.index.counts();
|
|
156
|
+
if (counts.status === 'ok')
|
|
157
|
+
writeJson(response, 200, counts);
|
|
158
|
+
else
|
|
159
|
+
unavailable(counts);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
const list = await options.readMemories();
|
|
163
|
+
if (list.status === 'ok')
|
|
164
|
+
writeJson(response, 200, { status: 'ok', items: list.items, next_cursor: null });
|
|
165
|
+
else
|
|
166
|
+
unavailable(list);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
const rest = url.pathname.slice(basePath.length + 1).split('/');
|
|
170
|
+
let id;
|
|
171
|
+
try {
|
|
172
|
+
id = decodeURIComponent(rest[0] ?? '');
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
writeJson(response, 400, { error: { code: 'invalid_id', message: 'The memory id is not valid' } });
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (!id || rest.length > 2 || (rest.length === 2 && rest[1] !== 'judgments')) {
|
|
179
|
+
writeJson(response, 404, { error: { code: 'not_found', message: 'Not found' } });
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
if (rest.length === 2) {
|
|
183
|
+
const requested = Number.parseInt(url.searchParams.get('limit') ?? '', 10);
|
|
184
|
+
const limit = Number.isInteger(requested) && requested > 0 ? Math.min(requested, MAX_JUDGMENT_LIMIT) : DEFAULT_JUDGMENT_LIMIT;
|
|
185
|
+
const judgments = await options.index.judgmentsUsing(id, limit);
|
|
186
|
+
if (judgments.status === 'ok')
|
|
187
|
+
writeJson(response, 200, judgments);
|
|
188
|
+
else
|
|
189
|
+
unavailable(judgments);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
const list = await options.readMemories();
|
|
193
|
+
if (list.status !== 'ok') {
|
|
194
|
+
unavailable(list);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
const item = list.items.find((candidate) => candidate.id === id);
|
|
198
|
+
if (item)
|
|
199
|
+
writeJson(response, 200, { status: 'ok', item });
|
|
200
|
+
else
|
|
201
|
+
writeJson(response, 404, { status: 'not_found' });
|
|
202
|
+
return true;
|
|
203
|
+
};
|
|
204
|
+
}
|
package/dist/ssot.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type CanonicalGraphMigrationPlan } from './ontology-migration.js';
|
|
2
|
-
import type { PersonalOs } from './types.js';
|
|
2
|
+
import type { PersonalKgEntry, PersonalOs } from './types.js';
|
|
3
3
|
export declare function initializePersonalOs(dataDir: string): Promise<void>;
|
|
4
4
|
export declare function loadPersonalOs(dataDir: string): Promise<PersonalOs>;
|
|
5
|
+
/** Only `personal-kg.jsonl`, validated line by line as `loadPersonalOs` does. */
|
|
6
|
+
export declare function readPersonalKg(dataDir: string): Promise<PersonalKgEntry[]>;
|
|
5
7
|
export declare function mutatePersonalOs(dataDir: string, mutator: (current: PersonalOs) => PersonalOs | Promise<PersonalOs>): Promise<PersonalOs>;
|
|
6
8
|
export declare function mutatePersonalOsWithSidecar<T>(dataDir: string, sidecarPath: string, mutator: (current: PersonalOs, sidecarContent?: string) => {
|
|
7
9
|
next: PersonalOs;
|
package/dist/ssot.js
CHANGED
|
@@ -74,6 +74,10 @@ export async function loadPersonalOs(dataDir) {
|
|
|
74
74
|
return loadPersonalOsUnlocked(dataDir);
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
/** Only `personal-kg.jsonl`, validated line by line as `loadPersonalOs` does. */
|
|
78
|
+
export async function readPersonalKg(dataDir) {
|
|
79
|
+
return readJsonl(join(dataDir, 'personal-kg.jsonl'), personalKgSchema, 'personal-kg.jsonl');
|
|
80
|
+
}
|
|
77
81
|
export async function mutatePersonalOs(dataDir, mutator) {
|
|
78
82
|
return withSsotLock(dataDir, async () => {
|
|
79
83
|
await recoverTransactions(dataDir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unson/brainbase-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Local-first Brainbase judgment OS that lets supported AI agents reuse a user's approved goals, priorities, decisions, and judgment criteria.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -295,6 +295,10 @@
|
|
|
295
295
|
"types": "./dist/streamable-http.d.ts",
|
|
296
296
|
"import": "./dist/streamable-http.js"
|
|
297
297
|
},
|
|
298
|
+
"./personal-memory": {
|
|
299
|
+
"types": "./dist/personal-memory.d.ts",
|
|
300
|
+
"import": "./dist/personal-memory.js"
|
|
301
|
+
},
|
|
298
302
|
"./meeting-minutes": {
|
|
299
303
|
"types": "./dist/meeting-minutes.d.ts",
|
|
300
304
|
"import": "./dist/meeting-minutes.js"
|
|
@@ -328,6 +332,11 @@
|
|
|
328
332
|
"./ui/world-model-view.css": "./ui/world-model-view.css",
|
|
329
333
|
"./ui/people-and-companies-view": "./ui/people-and-companies-view.js",
|
|
330
334
|
"./ui/people-and-companies-view.css": "./ui/people-and-companies-view.css",
|
|
335
|
+
"./ui/personal-memory": "./ui/personal-memory.js",
|
|
336
|
+
"./ui/personal-memory.css": "./ui/personal-memory.css",
|
|
337
|
+
"./ui/personal-memory-http-port": "./ui/personal-memory-http-port.js",
|
|
338
|
+
"./ui/inbox": "./ui/inbox.js",
|
|
339
|
+
"./ui/inbox.css": "./ui/inbox.css",
|
|
331
340
|
"./ui/area-nav": "./ui/area-nav.js",
|
|
332
341
|
"./ui/area-nav.css": "./ui/area-nav.css",
|
|
333
342
|
"./ui/local-web-shell": "./ui/local-web-shell.js",
|
|
@@ -487,5 +496,5 @@
|
|
|
487
496
|
"engines": {
|
|
488
497
|
"node": ">=20"
|
|
489
498
|
},
|
|
490
|
-
"gitHead": "
|
|
499
|
+
"gitHead": "a8166d088899a27bb8267af9e6dd99d3550c7b63"
|
|
491
500
|
}
|
package/ui/area-nav.css
CHANGED
|
@@ -1,15 +1,66 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Area navigation (ledger D10
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Area navigation (ledger D10): the destinations on the left, grouped under
|
|
3
|
+
* small headings with 設定 at the bottom, and the tabs of a destination with
|
|
4
|
+
* several screens above the page. Uses only the shared tokens in
|
|
5
|
+
* brainbase-tokens.css; a host gives the links its own look through
|
|
5
6
|
* `linkClassName`.
|
|
6
7
|
*/
|
|
7
8
|
.bb-area-nav {
|
|
9
|
+
flex: 1 1 auto;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
gap: 2px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.bb-area-section,
|
|
16
|
+
.bb-area-section-links {
|
|
8
17
|
display: grid;
|
|
9
18
|
gap: 2px;
|
|
10
19
|
}
|
|
11
20
|
|
|
21
|
+
/* Space between the groups; the footer group sits at the bottom of the nav. */
|
|
22
|
+
.bb-area-section + .bb-area-section {
|
|
23
|
+
margin-top: var(--bb-space-4);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.bb-area-section.is-footer {
|
|
27
|
+
margin-top: auto;
|
|
28
|
+
padding-top: var(--bb-space-4);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.bb-area-section-label {
|
|
32
|
+
padding: 0 10px 4px;
|
|
33
|
+
color: var(--bb-color-rail-muted, var(--bb-color-muted));
|
|
34
|
+
font-size: 11px;
|
|
35
|
+
font-weight: 700;
|
|
36
|
+
letter-spacing: .04em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.bb-area-section-link {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 4px;
|
|
43
|
+
border-radius: 6px;
|
|
44
|
+
padding-top: 4px;
|
|
45
|
+
text-decoration: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* A heading that opens a screen says so with a chevron (not part of its name). */
|
|
49
|
+
.bb-area-section-link::after {
|
|
50
|
+
content: "›";
|
|
51
|
+
font-size: 13px;
|
|
52
|
+
line-height: 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.bb-area-section-link:hover,
|
|
56
|
+
.bb-area-section-link[aria-current="page"] {
|
|
57
|
+
color: var(--bb-color-accent);
|
|
58
|
+
}
|
|
59
|
+
|
|
12
60
|
.bb-area-link[hidden],
|
|
61
|
+
.bb-area-section[hidden],
|
|
62
|
+
.bb-area-section-label[hidden],
|
|
63
|
+
.bb-area-section-links[hidden],
|
|
13
64
|
.bb-area-tabs[hidden] {
|
|
14
65
|
display: none;
|
|
15
66
|
}
|
|
@@ -67,7 +118,8 @@
|
|
|
67
118
|
}
|
|
68
119
|
|
|
69
120
|
.bb-area-tab:focus-visible,
|
|
70
|
-
.bb-area-link:focus-visible
|
|
121
|
+
.bb-area-link:focus-visible,
|
|
122
|
+
.bb-area-section-link:focus-visible {
|
|
71
123
|
outline: 2px solid var(--bb-color-accent);
|
|
72
124
|
outline-offset: -2px;
|
|
73
125
|
}
|