@unson/brainbase-mcp 0.10.1 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
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",
@@ -336,5 +336,5 @@
336
336
  "engines": {
337
337
  "node": ">=20"
338
338
  },
339
- "gitHead": "3fab56acebc9266f2bd256cc1795a3d7f550c311"
339
+ "gitHead": "d8334b5e94cdc643f5de90ccfc883a5e67fdd9af"
340
340
  }
@@ -148,6 +148,19 @@ function failureText(error) {
148
148
  return textOrNull(error instanceof Error ? error.message : typeof error === 'string' ? error : null) ?? '理由不明';
149
149
  }
150
150
 
151
+ /**
152
+ * A host may know the selected project's identity before its Graph list is
153
+ * available. Keep this boundary deliberately small: the early shell only
154
+ * accepts an id and display name, and never treats a host summary as Graph
155
+ * data such as goal, status or participant counts.
156
+ */
157
+ function normalizeProjectSummary(value) {
158
+ if (!isRecord(value)) return null;
159
+ const id = textOrNull(value.id ?? value.code);
160
+ if (!id) return null;
161
+ return { id, name: textOrNull(value.name) ?? id };
162
+ }
163
+
151
164
  /**
152
165
  * Mounts 「プロジェクトと関係者」.
153
166
  *
@@ -201,6 +214,9 @@ function failureText(error) {
201
214
  * @param {(projectId: string) => Promise<object>} [options.loadProjectContext]
202
215
  * Optional read-only supplementary sections (tasks/knowledge). A failed
203
216
  * context read is kept separate from the Graph detail read.
217
+ * @param {Array<{ id: string, name?: string }> | (() => Array<{ id: string, name?: string }>)} [options.projectSummaries]
218
+ * Optional host-known project identities. These are used only to draw the
219
+ * selected project's early loading shell before the Graph list is read.
204
220
  * @param {(container: Element, options: object) => { destroy?: Function }} [options.mountProjectGraph]
205
221
  * Host-injected Sigma.js graph mount. When omitted, the shared browser
206
222
  * entry is used. The workspace keeps an accessible relation list when
@@ -232,6 +248,7 @@ export function createGraphProjectsView({
232
248
  onSelect,
233
249
  ownShare,
234
250
  loadProjectContext,
251
+ projectSummaries,
235
252
  mountProjectGraph,
236
253
  } = {}) {
237
254
  if (!root) throw new TypeError('root is required');
@@ -267,6 +284,7 @@ export function createGraphProjectsView({
267
284
  let knowledgeWorkspace = null;
268
285
  let contextRequest = 0;
269
286
  let detailRequest = 0;
287
+ let hostListRequest = 0;
270
288
  let viewDestroyed = false;
271
289
 
272
290
  const correction = createGraphCorrection({
@@ -289,6 +307,46 @@ export function createGraphProjectsView({
289
307
  return state.list.state === 'ok' ? state.list.payload.projects.find((project) => project.id === id) ?? null : null;
290
308
  }
291
309
 
310
+ function projectSummaryFor(id) {
311
+ const target = textOrNull(id);
312
+ if (!target) return null;
313
+ let summaries = projectSummaries;
314
+ try {
315
+ if (typeof summaries === 'function') summaries = summaries();
316
+ } catch {
317
+ summaries = null;
318
+ }
319
+ if (Array.isArray(summaries)) {
320
+ const summary = summaries.map(normalizeProjectSummary).find((item) => item?.id === target);
321
+ if (summary) return summary;
322
+ }
323
+ const listed = listItem(target);
324
+ return listed ? { id: listed.id, name: listed.name } : { id: target, name: target };
325
+ }
326
+
327
+ function renderProjectKnowledgeLoading(id, readState, { onRetry } = {}) {
328
+ const target = textOrNull(id);
329
+ if (!target) return null;
330
+ const project = projectSummaryFor(target);
331
+ const loading = makeElement(doc, 'section', {
332
+ className: 'bb-pkw bb-pkw-loading',
333
+ attrs: { 'aria-label': 'プロジェクトの知識', 'data-project-id': target },
334
+ });
335
+ loading.append(makeElement(doc, 'div', { className: 'bb-pkw-kicker', text: 'プロジェクトの記録' }));
336
+ const header = makeElement(doc, 'header', { className: 'bb-pkw-header' });
337
+ header.append(
338
+ makeElement(doc, 'h2', { text: project.name }),
339
+ makeElement(doc, 'p', { className: 'bb-pkw-lead', text: '目的・関係・判断の根拠を、実際に記録された情報からたどれます。' }),
340
+ );
341
+ loading.append(header);
342
+ const notice = graphStateNotice(doc, readState, {
343
+ onRetry,
344
+ loadingText: 'プロジェクトの記録を読み込んでいます。',
345
+ });
346
+ if (notice) loading.append(notice);
347
+ return loading;
348
+ }
349
+
292
350
  function openCorrection(at, open) {
293
351
  state.panelAt = at;
294
352
  open();
@@ -456,6 +514,9 @@ export function createGraphProjectsView({
456
514
  const stateNotice = graphStateNotice(doc, state.list, { onRetry: () => controller.load(), loadingText: 'プロジェクトを読み込んでいます。' });
457
515
  if (stateNotice) {
458
516
  children.push(stateNotice);
517
+ if (state.selectedId) {
518
+ children.push(renderProjectKnowledgeLoading(state.selectedId, state.list, { onRetry: () => controller.load() }));
519
+ }
459
520
  return children;
460
521
  }
461
522
  const { payload } = state.list;
@@ -486,6 +547,10 @@ export function createGraphProjectsView({
486
547
  onTabChange: setKnowledgeTabChrome,
487
548
  });
488
549
  children.push(knowledgeWorkspace.element);
550
+ } else if (state.selectedId) {
551
+ children.push(renderProjectKnowledgeLoading(state.selectedId, state.detail ?? { state: 'loading' }, {
552
+ onRetry: () => controller.loadDetail(state.selectedId),
553
+ }));
489
554
  }
490
555
 
491
556
  if (payload.projects.length === 0) {
@@ -623,7 +688,10 @@ export function createGraphProjectsView({
623
688
  const project = loaded?.project ?? listItem(state.selectedId) ?? { id: state.selectedId, name: state.selectedId };
624
689
  const children = [workspaceRailHead(doc, { kicker: 'プロジェクト', title: project.name, sub: project.id })];
625
690
  if (!loaded) {
626
- children.push(graphStateNotice(doc, detail?.id === state.selectedId ? detail : { state: 'loading' }, {
691
+ const readState = detail?.id === state.selectedId
692
+ ? detail
693
+ : state.list.state === 'ok' ? { state: 'loading' } : state.list;
694
+ children.push(graphStateNotice(doc, readState, {
627
695
  onRetry: () => controller.loadDetail(state.selectedId),
628
696
  loadingText: 'このプロジェクトの関係者を読み込んでいます。',
629
697
  }));
@@ -692,13 +760,16 @@ export function createGraphProjectsView({
692
760
 
693
761
  /** Reads the host's project list (see `load`). */
694
762
  async function loadHostList({ keep = false } = {}) {
763
+ const request = ++hostListRequest;
764
+ if (viewDestroyed) return state.list;
695
765
  if (!(keep && state.list.state === 'ok')) {
696
766
  state.list = { state: 'loading' };
697
767
  controller.render();
698
768
  }
699
- state.list = normalizeProjectList(await client.read('/projects'));
769
+ const result = await client.read('/projects');
770
+ if (viewDestroyed || request !== hostListRequest) return state.list;
771
+ state.list = normalizeProjectList(result);
700
772
  if (state.list.state !== 'ok') {
701
- if (!keep) state.selectedId = null;
702
773
  controller.render();
703
774
  return state.list;
704
775
  }
@@ -711,16 +782,21 @@ export function createGraphProjectsView({
711
782
  contextRequest += 1;
712
783
  state.selectedId = projects[0]?.id ?? null;
713
784
  if (state.selectedId) {
785
+ if (viewDestroyed || request !== hostListRequest) return state.list;
714
786
  const reading = controller.loadDetail(state.selectedId);
715
787
  notifySelect(state.selectedId);
716
788
  await reading;
789
+ if (viewDestroyed || request !== hostListRequest) return state.list;
717
790
  return state.list;
718
791
  }
719
792
  } else if (state.detail?.id !== state.selectedId) {
720
793
  // The project the host asked for is listed; read it for the rail.
794
+ if (viewDestroyed || request !== hostListRequest) return state.list;
721
795
  await controller.loadDetail(state.selectedId);
796
+ if (viewDestroyed || request !== hostListRequest) return state.list;
722
797
  return state.list;
723
798
  }
799
+ if (viewDestroyed || request !== hostListRequest) return state.list;
724
800
  controller.render();
725
801
  return state.list;
726
802
  }
@@ -744,6 +820,7 @@ export function createGraphProjectsView({
744
820
  destroy() {
745
821
  if (viewDestroyed) return;
746
822
  viewDestroyed = true;
823
+ hostListRequest += 1;
747
824
  detailRequest += 1;
748
825
  contextRequest += 1;
749
826
  knowledgeWorkspace?.destroy();