@unson/brainbase-mcp 0.11.0 → 0.12.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/contracts/judgment-value-proof/schema.json +19 -1
- package/dist/judgment-value-proof.d.ts +15 -0
- package/dist/judgment-value-proof.js +17 -0
- package/dist/knowledge-promotion.d.ts +61 -3
- package/dist/knowledge-promotion.js +112 -1
- package/dist/local-web-host.d.ts +4 -4
- package/dist/local-web-host.js +137 -12
- package/package.json +2 -2
- package/ui/graph-registry-view.js +27 -1
- package/ui/local-web-shell.js +80 -8
- package/ui/project-workspace.js +1 -1
- package/ui/value-proof-review.css +54 -0
- package/ui/value-proof-review.js +127 -5
- package/ui/world-model-view.css +61 -0
- package/ui/world-model-view.js +215 -10
package/ui/world-model-view.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
2
|
+
* World Model view (現状と見通し).
|
|
3
3
|
*
|
|
4
4
|
* Shows recorded observations apart from the variables and models that are
|
|
5
5
|
* the owner's way of seeing them (which may be hypotheses), plus model
|
|
6
6
|
* adoptions. The host injects the fetcher and base path; this module keeps
|
|
7
|
-
* no global state
|
|
7
|
+
* no global state. The empty state also offers the host's first-user
|
|
8
|
+
* variable-and-observation registration route. Each section loads on its own: a failed
|
|
8
9
|
* or unverifiable section is reported in place and never shown as zero items.
|
|
9
10
|
*
|
|
10
11
|
* Drawn with the organization edition's screen pattern (workspace-kit): a
|
|
11
|
-
* section title under the page head, then one
|
|
12
|
-
*
|
|
12
|
+
* section title under the page head, then one ledger per block. Rows are not
|
|
13
|
+
* selectable, so the view never writes to the host's rail; the only write is
|
|
14
|
+
* the explicit first-registration form shown for a confirmed empty state.
|
|
13
15
|
*
|
|
14
16
|
* A section the host cannot serve answers `{ status: 'unavailable', reason }`
|
|
15
17
|
* (HTTP 200). It is shown as unavailable, never as zero items; a host with no
|
|
@@ -333,7 +335,138 @@ function unavailableSummary(doc, state, unavailableNotice) {
|
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
/**
|
|
336
|
-
*
|
|
338
|
+
* The current Personal Web is a read-mostly World Model surface. Show the
|
|
339
|
+
* first-registration entry only after variables and observations have both
|
|
340
|
+
* been confirmed empty;
|
|
341
|
+
* an unknown, partial or unavailable section must not be presented as an
|
|
342
|
+
* empty World Model.
|
|
343
|
+
*/
|
|
344
|
+
function registrationField(doc, { id, label, type = 'text', value = '', required = true, attrs = {}, options = [] } = {}) {
|
|
345
|
+
const field = makeElement(doc, 'div', { className: 'bb-wm-registration-field' });
|
|
346
|
+
const labelElement = makeElement(doc, 'label', { text: label, attrs: { for: id } });
|
|
347
|
+
const input = makeElement(doc, type === 'select' ? 'select' : 'input', {
|
|
348
|
+
className: 'bb-wm-registration-input',
|
|
349
|
+
attrs: { id, name: id, type: type === 'select' ? undefined : type, required, ...attrs },
|
|
350
|
+
});
|
|
351
|
+
if (type === 'select') {
|
|
352
|
+
for (const option of options) {
|
|
353
|
+
input.append(makeElement(doc, 'option', { text: option.label, attrs: { value: option.value } }));
|
|
354
|
+
}
|
|
355
|
+
input.value = value;
|
|
356
|
+
} else {
|
|
357
|
+
input.value = value;
|
|
358
|
+
}
|
|
359
|
+
field.append(labelElement, input);
|
|
360
|
+
return { field, input, id };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function registrationValue(raw, valueKind) {
|
|
364
|
+
const value = String(raw ?? '').trim();
|
|
365
|
+
if (!value) return { error: '最初の観測値を入力してください。' };
|
|
366
|
+
if (valueKind === 'number') {
|
|
367
|
+
const number = Number(value);
|
|
368
|
+
return Number.isFinite(number) ? { value: number } : { error: '数値の観測値を入力してください。' };
|
|
369
|
+
}
|
|
370
|
+
if (valueKind === 'boolean') {
|
|
371
|
+
if (value === 'true' || value === 'はい') return { value: true };
|
|
372
|
+
if (value === 'false' || value === 'いいえ') return { value: false };
|
|
373
|
+
return { error: '真偽値は「true」または「false」で入力してください。' };
|
|
374
|
+
}
|
|
375
|
+
return { value };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function emptyRegistrationEntry(doc, state, callbacks, registration = {}) {
|
|
379
|
+
if (state.variables?.state !== 'empty' || state.observations?.state !== 'empty') return null;
|
|
380
|
+
const formFields = {};
|
|
381
|
+
const form = makeElement(doc, 'form', {
|
|
382
|
+
className: 'bb-wm-registration-form',
|
|
383
|
+
attrs: { 'aria-label': '最初の変数と観測値を登録' },
|
|
384
|
+
});
|
|
385
|
+
const fields = [
|
|
386
|
+
registrationField(doc, { id: 'bb-wm-registration-meaning', label: '何を記録するか', attrs: { autocomplete: 'off' } }),
|
|
387
|
+
registrationField(doc, { id: 'bb-wm-registration-subject', label: '対象', attrs: { autocomplete: 'off' } }),
|
|
388
|
+
registrationField(doc, {
|
|
389
|
+
id: 'bb-wm-registration-value-kind', label: '値の種類', type: 'select', value: 'number',
|
|
390
|
+
options: [
|
|
391
|
+
{ value: 'number', label: '数値' },
|
|
392
|
+
{ value: 'boolean', label: 'はい/いいえ' },
|
|
393
|
+
{ value: 'string', label: '文字' },
|
|
394
|
+
{ value: 'state', label: '状態' },
|
|
395
|
+
],
|
|
396
|
+
}),
|
|
397
|
+
registrationField(doc, { id: 'bb-wm-registration-unit', label: '単位(数値の場合)', required: false, attrs: { autocomplete: 'off' } }),
|
|
398
|
+
registrationField(doc, {
|
|
399
|
+
id: 'bb-wm-registration-aggregation', label: '集計方法', type: 'select', value: 'last',
|
|
400
|
+
options: [
|
|
401
|
+
{ value: 'last', label: '最新の値' },
|
|
402
|
+
{ value: 'sum', label: '合計' },
|
|
403
|
+
{ value: 'average', label: '平均' },
|
|
404
|
+
{ value: 'count', label: '件数' },
|
|
405
|
+
{ value: 'none', label: '集計しない' },
|
|
406
|
+
],
|
|
407
|
+
}),
|
|
408
|
+
registrationField(doc, { id: 'bb-wm-registration-granularity', label: '記録の単位', value: '日', attrs: { autocomplete: 'off' } }),
|
|
409
|
+
registrationField(doc, { id: 'bb-wm-registration-method', label: '測り方', attrs: { autocomplete: 'off' } }),
|
|
410
|
+
registrationField(doc, { id: 'bb-wm-registration-value', label: '最初の観測値', attrs: { autocomplete: 'off' } }),
|
|
411
|
+
];
|
|
412
|
+
for (const { field, input, id } of fields) {
|
|
413
|
+
formFields[id] = input;
|
|
414
|
+
form.append(field);
|
|
415
|
+
}
|
|
416
|
+
const status = makeElement(doc, 'p', {
|
|
417
|
+
className: 'bb-wm-registration-status',
|
|
418
|
+
text: registration.message ?? '登録すると、この画面の記録として読み直します。',
|
|
419
|
+
attrs: { 'aria-live': 'polite' },
|
|
420
|
+
});
|
|
421
|
+
const submit = workspaceButton(doc, {
|
|
422
|
+
text: registration.state === 'saving' ? '登録しています…' : '変数と観測値を登録',
|
|
423
|
+
variant: 'primary',
|
|
424
|
+
disabled: registration.state === 'saving',
|
|
425
|
+
attrs: { type: 'submit' },
|
|
426
|
+
});
|
|
427
|
+
form.addEventListener('submit', (event) => {
|
|
428
|
+
event?.preventDefault?.();
|
|
429
|
+
const parsed = registrationValue(formFields['bb-wm-registration-value'].value, formFields['bb-wm-registration-value-kind'].value);
|
|
430
|
+
const payload = {
|
|
431
|
+
meaning: String(formFields['bb-wm-registration-meaning'].value ?? '').trim(),
|
|
432
|
+
subject: String(formFields['bb-wm-registration-subject'].value ?? '').trim(),
|
|
433
|
+
valueKind: String(formFields['bb-wm-registration-value-kind'].value ?? '').trim(),
|
|
434
|
+
unit: String(formFields['bb-wm-registration-unit'].value ?? '').trim(),
|
|
435
|
+
aggregation: String(formFields['bb-wm-registration-aggregation'].value ?? '').trim(),
|
|
436
|
+
granularity: String(formFields['bb-wm-registration-granularity'].value ?? '').trim(),
|
|
437
|
+
measurementMethod: String(formFields['bb-wm-registration-method'].value ?? '').trim(),
|
|
438
|
+
value: parsed.value,
|
|
439
|
+
};
|
|
440
|
+
if (!payload.meaning || !payload.subject || !payload.granularity || !payload.measurementMethod) {
|
|
441
|
+
status.textContent = '何を記録するか、対象、記録の単位、測り方を入力してください。';
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (payload.valueKind === 'number' && !payload.unit) {
|
|
445
|
+
status.textContent = '数値を記録する場合は単位を入力してください。';
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (parsed.error) {
|
|
449
|
+
status.textContent = parsed.error;
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
return callbacks.onRegister?.(payload);
|
|
453
|
+
});
|
|
454
|
+
form.append(status, submit);
|
|
455
|
+
const guidance = workspaceNotice(doc, {
|
|
456
|
+
label: '最初の登録',
|
|
457
|
+
text: [
|
|
458
|
+
makeElement(doc, 'p', { text: 'ここから変数と最初の観測値を登録できます。登録者と記録日時は、この手元のWebホストが決めます。' }),
|
|
459
|
+
form,
|
|
460
|
+
makeElement(doc, 'p', { className: 'bb-wm-registration-boundary', text: 'モデルの作成・採用や、既存観測の訂正はこの初回登録の対象外です。確認と権限が必要な別の手順で扱います。' }),
|
|
461
|
+
],
|
|
462
|
+
});
|
|
463
|
+
guidance.className += ' bb-wm-registration-guidance';
|
|
464
|
+
return guidance;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Render the whole view into a host-owned root: a section title, optional first-registration form,
|
|
469
|
+
* and ledgers.
|
|
337
470
|
* `options.unavailableNotice` is the host's `{ title, guidance }` shown once
|
|
338
471
|
* when every section is unavailable.
|
|
339
472
|
*/
|
|
@@ -343,7 +476,7 @@ export function renderWorldModelView(root, state, callbacks = {}, options = {})
|
|
|
343
476
|
const surface = makeElement(doc, 'section', { className: 'bb-wm', attrs: { 'data-contract-version': WORLD_MODEL_VIEW_CONTRACT_VERSION, 'aria-label': '現状と見通し' } });
|
|
344
477
|
surface.append(workspaceSectionTitle(doc, {
|
|
345
478
|
title: '現状と見通し',
|
|
346
|
-
lead: '
|
|
479
|
+
lead: '見方(変数とモデル。仮説を含みます)と、記録された観測を分けて表示します。最初の変数と観測値はここから登録できます。',
|
|
347
480
|
}));
|
|
348
481
|
const unavailable = unavailableSummary(doc, state, options.unavailableNotice);
|
|
349
482
|
if (unavailable) {
|
|
@@ -352,10 +485,20 @@ export function renderWorldModelView(root, state, callbacks = {}, options = {})
|
|
|
352
485
|
return surface;
|
|
353
486
|
}
|
|
354
487
|
|
|
488
|
+
if (options.registration?.state === 'error' && text(options.registration.message)) {
|
|
489
|
+
surface.append(workspaceNotice(doc, {
|
|
490
|
+
label: '登録結果',
|
|
491
|
+
tone: 'danger',
|
|
492
|
+
text: options.registration.message,
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
|
|
355
496
|
const variables = namesOf(state.variables);
|
|
356
497
|
const models = namesOf(state.models);
|
|
357
498
|
|
|
358
499
|
const view = block(doc, '変数とモデル', '見方:変数とモデル', '何を測り、どう関係すると考えているか。認識の状態は、確かめた度合いです。観測ではありません。');
|
|
500
|
+
const registrationGuidance = emptyRegistrationEntry(doc, state, callbacks, options.registration);
|
|
501
|
+
if (registrationGuidance) view.append(registrationGuidance);
|
|
359
502
|
const statuses = {
|
|
360
503
|
variables: sectionStatus(doc, '変数', 'variables', state.variables, callbacks, '変数はまだ登録がありません。'),
|
|
361
504
|
models: sectionStatus(doc, 'モデル', 'models', state.models, callbacks, 'モデルはまだ登録がありません。'),
|
|
@@ -407,13 +550,14 @@ async function readError(response) {
|
|
|
407
550
|
}
|
|
408
551
|
|
|
409
552
|
/**
|
|
410
|
-
* Mounts the
|
|
553
|
+
* Mounts the World Model view.
|
|
411
554
|
*
|
|
412
555
|
* @param {object} options
|
|
413
556
|
* @param {Element} options.root Where the view is drawn.
|
|
414
557
|
* @param {Element} [options.rail] Accepted for the host's screen contract; the view never writes to it.
|
|
415
558
|
* @param {Document} [options.document] The document to build elements with (default: the global one).
|
|
416
559
|
* @param {Function} [options.fetcher] `fetch`-compatible function for the sections.
|
|
560
|
+
* @param {string} [options.token] Local Web write token sent with registration requests.
|
|
417
561
|
* @param {string} [options.basePath='/api/world-model'] Where `{variables,models,observations,adoptions}` are served.
|
|
418
562
|
* @param {boolean} [options.autoLoad=true] Reads every section on mount.
|
|
419
563
|
* @param {{ title: string, guidance?: string, reasonLabels?: Record<string, string> }} [options.unavailableNotice]
|
|
@@ -425,10 +569,11 @@ async function readError(response) {
|
|
|
425
569
|
*/
|
|
426
570
|
export function createWorldModelView({
|
|
427
571
|
root,
|
|
428
|
-
/** Accepted for the host's screen contract; the
|
|
572
|
+
/** Accepted for the host's screen contract; the view keeps the rail to the screen's editor. */
|
|
429
573
|
rail: _rail,
|
|
430
574
|
document: explicitDocument,
|
|
431
575
|
fetcher,
|
|
576
|
+
token,
|
|
432
577
|
basePath = '/api/world-model',
|
|
433
578
|
autoLoad = true,
|
|
434
579
|
unavailableNotice,
|
|
@@ -441,12 +586,16 @@ export function createWorldModelView({
|
|
|
441
586
|
const base = String(basePath).replace(/\/+$/u, '');
|
|
442
587
|
const state = Object.fromEntries(WORLD_MODEL_SECTIONS.map((section) => [section, { state: 'loading', items: null }]));
|
|
443
588
|
|
|
444
|
-
const
|
|
589
|
+
const registration = { state: 'idle', message: null };
|
|
590
|
+
const callbacks = {
|
|
591
|
+
onRetry: (section) => controller.loadSection(section),
|
|
592
|
+
onRegister: (payload) => controller.register(payload),
|
|
593
|
+
};
|
|
445
594
|
|
|
446
595
|
const controller = {
|
|
447
596
|
get state() { return state; },
|
|
448
597
|
render() {
|
|
449
|
-
renderWorldModelView(root, state, callbacks, { document: doc, unavailableNotice });
|
|
598
|
+
renderWorldModelView(root, state, callbacks, { document: doc, unavailableNotice, registration });
|
|
450
599
|
return controller;
|
|
451
600
|
},
|
|
452
601
|
async loadSection(section) {
|
|
@@ -477,6 +626,62 @@ export function createWorldModelView({
|
|
|
477
626
|
await Promise.all(WORLD_MODEL_SECTIONS.map((section) => controller.loadSection(section)));
|
|
478
627
|
return state;
|
|
479
628
|
},
|
|
629
|
+
async register(payload) {
|
|
630
|
+
if (!request) {
|
|
631
|
+
registration.state = 'error';
|
|
632
|
+
registration.message = '登録先が設定されていません。';
|
|
633
|
+
controller.render();
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
registration.state = 'saving';
|
|
637
|
+
registration.message = '登録しています…';
|
|
638
|
+
controller.render();
|
|
639
|
+
const write = async (path, body) => {
|
|
640
|
+
const response = await request(`${base}${path}`, {
|
|
641
|
+
method: 'POST',
|
|
642
|
+
headers: {
|
|
643
|
+
'Content-Type': 'application/json',
|
|
644
|
+
...(token ? { 'X-Brainbase-Review-Token': token } : {}),
|
|
645
|
+
},
|
|
646
|
+
body: JSON.stringify(body),
|
|
647
|
+
});
|
|
648
|
+
if (!response.ok) {
|
|
649
|
+
const error = await readError(response);
|
|
650
|
+
throw new Error(error.message ?? error.code ?? `HTTP ${response.status}`);
|
|
651
|
+
}
|
|
652
|
+
return response.json();
|
|
653
|
+
};
|
|
654
|
+
let variableReference = null;
|
|
655
|
+
try {
|
|
656
|
+
const created = await write('/variables', {
|
|
657
|
+
meaning: payload.meaning,
|
|
658
|
+
subject: payload.subject,
|
|
659
|
+
valueKind: payload.valueKind,
|
|
660
|
+
...(payload.unit ? { unit: payload.unit } : {}),
|
|
661
|
+
aggregation: payload.aggregation,
|
|
662
|
+
granularity: payload.granularity,
|
|
663
|
+
measurementMethod: payload.measurementMethod,
|
|
664
|
+
});
|
|
665
|
+
const reference = created?.reference;
|
|
666
|
+
if (!reference || typeof reference.id !== 'string' || typeof reference.revision !== 'string') {
|
|
667
|
+
throw new Error('変数の登録結果を読み取れませんでした');
|
|
668
|
+
}
|
|
669
|
+
variableReference = reference;
|
|
670
|
+
await write('/observations', { variableRef: reference, value: payload.value });
|
|
671
|
+
registration.state = 'idle';
|
|
672
|
+
registration.message = null;
|
|
673
|
+
await controller.load();
|
|
674
|
+
} catch (error) {
|
|
675
|
+
registration.state = 'error';
|
|
676
|
+
const detail = error instanceof Error ? error.message : '登録できませんでした';
|
|
677
|
+
registration.message = variableReference
|
|
678
|
+
? `変数は登録されましたが、最初の観測値を保存できませんでした。${detail}`
|
|
679
|
+
: detail;
|
|
680
|
+
await controller.load();
|
|
681
|
+
controller.render();
|
|
682
|
+
}
|
|
683
|
+
return registration;
|
|
684
|
+
},
|
|
480
685
|
};
|
|
481
686
|
controller.render();
|
|
482
687
|
if (autoLoad) void controller.load();
|