@unson/brainbase-mcp 0.40.0 → 0.41.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/foundation-http.d.ts +12 -2
- package/dist/foundation-http.js +183 -8
- package/dist/graph-web.d.ts +2 -0
- package/dist/graph-web.js +10 -1
- package/dist/judgment-history.js +3 -1
- package/dist/local-web-host.js +1 -1
- package/dist/portable-graph.d.ts +2 -2
- package/dist/portable-graph.js +4 -3
- package/dist/types.d.ts +32 -1
- package/package.json +2 -2
- package/ui/foundation-overview.js +17 -1
- package/ui/graph-own-share.js +4 -2
- package/ui/graph-projects-view.js +167 -11
- package/ui/graph-registry-view.js +135 -5
- package/ui/graph-view-shared.js +6 -1
- package/ui/judgment-history.js +5 -1
- package/ui/meeting-minutes.js +7 -3
- package/ui/objective-editor-http-port.js +21 -8
- package/ui/objective-editor.js +186 -24
- package/ui/objective-readback-contract.js +177 -0
- package/ui/outcome-knowledge.css +18 -1
- package/ui/outcome-knowledge.js +523 -55
- package/ui/project-workspace.js +177 -4
- package/ui/world/world-canvas-ui.css +32 -0
- package/ui/world/world-canvas-ui.js +30 -2
- package/ui/world/world-view.js +5 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Browser-side guard for the canonical Objective detail response.
|
|
3
|
+
*
|
|
4
|
+
* The canonical host owns source/provenance and exact digest generation and
|
|
5
|
+
* verification. This module checks the digest shape and cross-field
|
|
6
|
+
* agreement, but never recomputes the hash or supplies a source, timestamp,
|
|
7
|
+
* or fallback record.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const OBJECTIVE_READBACK_CONTRACT_VERSION = 'brainbase.objective-readback.v1';
|
|
11
|
+
|
|
12
|
+
const STATUSES = new Set(['ready', 'error', 'unknown']);
|
|
13
|
+
const PROVENANCE_KINDS = new Set(['candidate', 'document', 'observation', 'decision', 'import']);
|
|
14
|
+
const EPISTEMIC_STATES = new Set(['unverified', 'hypothesis', 'supported', 'verified', 'refuted']);
|
|
15
|
+
const ADOPTION_STATES = new Set(['draft', 'proposed', 'approved', 'retired']);
|
|
16
|
+
const FOUNDATION_USES = new Set(['draft', 'judgment', 'evaluation', 'execution']);
|
|
17
|
+
const FOUNDATION_STORAGES = new Set(['candidate', 'ontology', 'evidence', 'execution']);
|
|
18
|
+
const VISIBILITIES = new Set(['private', 'project', 'organization', 'public']);
|
|
19
|
+
const POSITIVE_REVISION = /^[1-9]\d*$/u;
|
|
20
|
+
const FOUNDATION_DIGEST = /^sha256:[0-9a-f]{64}$/u;
|
|
21
|
+
|
|
22
|
+
function objectValue(value) {
|
|
23
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function nonEmpty(value) {
|
|
27
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function canonicalTimestamp(value) {
|
|
31
|
+
if (!nonEmpty(value)) return false;
|
|
32
|
+
const date = new Date(value);
|
|
33
|
+
return !Number.isNaN(date.getTime()) && date.toISOString() === value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseRfc3339(value) {
|
|
37
|
+
if (!nonEmpty(value)) return null;
|
|
38
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}:\d{2})$/u.exec(value);
|
|
39
|
+
if (!match) return null;
|
|
40
|
+
const year = Number(match[1]);
|
|
41
|
+
const month = Number(match[2]);
|
|
42
|
+
const day = Number(match[3]);
|
|
43
|
+
const hour = Number(match[4]);
|
|
44
|
+
const minute = Number(match[5]);
|
|
45
|
+
const second = Number(match[6]);
|
|
46
|
+
if (month < 1 || month > 12 || day < 1 || day > new Date(Date.UTC(year, month, 0)).getUTCDate()
|
|
47
|
+
|| hour > 23 || minute > 59 || second > 59) return null;
|
|
48
|
+
const zone = match[8];
|
|
49
|
+
let offsetMinutes = 0;
|
|
50
|
+
if (zone !== 'Z') {
|
|
51
|
+
const offsetHour = Number(zone.slice(1, 3));
|
|
52
|
+
const offsetMinute = Number(zone.slice(4, 6));
|
|
53
|
+
if (offsetHour > 23 || offsetMinute > 59) return null;
|
|
54
|
+
offsetMinutes = (zone[0] === '-' ? -1 : 1) * (offsetHour * 60 + offsetMinute);
|
|
55
|
+
}
|
|
56
|
+
const milliseconds = Number(`${match[7] ?? ''}000`.slice(0, 3));
|
|
57
|
+
const timestamp = Date.UTC(year, month - 1, day, hour, minute, second, milliseconds)
|
|
58
|
+
- offsetMinutes * 60_000;
|
|
59
|
+
return Number.isFinite(timestamp) ? timestamp : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function stringArray(value) {
|
|
63
|
+
return Array.isArray(value) && value.every((entry) => nonEmpty(entry));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function scope(value) {
|
|
67
|
+
const object = objectValue(value);
|
|
68
|
+
if (!object || !stringArray(object.subjectIds)) return false;
|
|
69
|
+
const validFrom = parseRfc3339(object.validFrom);
|
|
70
|
+
if (validFrom === null) return false;
|
|
71
|
+
if (object.validUntil === undefined) return true;
|
|
72
|
+
const validUntil = parseRfc3339(object.validUntil);
|
|
73
|
+
return validUntil !== null && validUntil > validFrom;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function acl(value) {
|
|
77
|
+
const object = objectValue(value);
|
|
78
|
+
return Boolean(object)
|
|
79
|
+
&& nonEmpty(object.ownerId)
|
|
80
|
+
&& VISIBILITIES.has(object.visibility)
|
|
81
|
+
&& stringArray(object.readerIds)
|
|
82
|
+
&& stringArray(object.writerIds);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function provenance(value) {
|
|
86
|
+
return Array.isArray(value) && value.every((entry) => {
|
|
87
|
+
const object = objectValue(entry);
|
|
88
|
+
return Boolean(object)
|
|
89
|
+
&& nonEmpty(object.sourceId)
|
|
90
|
+
&& typeof object.sourceKind === 'string'
|
|
91
|
+
&& PROVENANCE_KINDS.has(object.sourceKind)
|
|
92
|
+
&& Array.isArray(object.evidenceIds)
|
|
93
|
+
&& object.evidenceIds.every((id) => nonEmpty(id));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function record(value) {
|
|
98
|
+
const object = objectValue(value);
|
|
99
|
+
const definition = objectValue(object?.definition);
|
|
100
|
+
if (!object || !definition || definition.type !== 'objective'
|
|
101
|
+
|| !nonEmpty(definition.id) || typeof definition.revision !== 'string' || !POSITIVE_REVISION.test(definition.revision)
|
|
102
|
+
|| !nonEmpty(definition.meaning)
|
|
103
|
+
|| !ADOPTION_STATES.has(definition.adoptionState)
|
|
104
|
+
|| !Array.isArray(definition.authorizedUses)
|
|
105
|
+
|| !definition.authorizedUses.every((use) => FOUNDATION_USES.has(use))
|
|
106
|
+
|| !definition.authorizedUses.includes('draft')
|
|
107
|
+
|| !acl(definition.acl)
|
|
108
|
+
|| !FOUNDATION_STORAGES.has(definition.storage)
|
|
109
|
+
|| !scope(definition.scope)
|
|
110
|
+
|| !provenance(definition.provenance)
|
|
111
|
+
|| (definition.epistemicState !== undefined && !EPISTEMIC_STATES.has(definition.epistemicState))
|
|
112
|
+
|| typeof object.digest !== 'string' || !FOUNDATION_DIGEST.test(object.digest)) return null;
|
|
113
|
+
return object;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function source(value) {
|
|
117
|
+
const object = objectValue(value);
|
|
118
|
+
if (!object || object.source_type !== 'foundation_revision_store'
|
|
119
|
+
|| !nonEmpty(object.source_id) || !nonEmpty(object.source_version)
|
|
120
|
+
|| !FOUNDATION_DIGEST.test(object.digest) || !provenance(object.provenance)) return null;
|
|
121
|
+
return object;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function reason(value) {
|
|
125
|
+
const object = objectValue(value);
|
|
126
|
+
return object && nonEmpty(object.code) && nonEmpty(object.message) ? object : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function sameIdentity(left, right) {
|
|
130
|
+
return left?.definition?.id === right?.definition?.id
|
|
131
|
+
&& left?.definition?.revision === right?.definition?.revision
|
|
132
|
+
&& left?.digest === right?.digest;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function matchesExpected(recordValue, expected) {
|
|
136
|
+
if (expected === undefined || expected === null) return true;
|
|
137
|
+
const object = objectValue(expected);
|
|
138
|
+
if (!object || !nonEmpty(object.id)) return false;
|
|
139
|
+
return recordValue.definition.id === object.id
|
|
140
|
+
&& (object.revision === undefined || object.revision === null || recordValue.definition.revision === object.revision);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Returns the original payload only when its status semantics and source
|
|
145
|
+
* metadata are internally consistent. `null` means the response is unsafe
|
|
146
|
+
* to render as a canonical Objective.
|
|
147
|
+
*/
|
|
148
|
+
export function normalizeObjectiveReadback(payload, expected) {
|
|
149
|
+
const object = objectValue(payload);
|
|
150
|
+
if (!object
|
|
151
|
+
|| object.contract_version !== OBJECTIVE_READBACK_CONTRACT_VERSION
|
|
152
|
+
|| !STATUSES.has(object.status)
|
|
153
|
+
|| !canonicalTimestamp(object.checked_at)) return null;
|
|
154
|
+
|
|
155
|
+
if (object.status === 'ready') {
|
|
156
|
+
const canonicalRecord = record(object.record);
|
|
157
|
+
const objective = record(object.objective);
|
|
158
|
+
const canonicalSource = source(object.source);
|
|
159
|
+
if (object.reason !== null || !canonicalRecord || !objective || !canonicalSource) return null;
|
|
160
|
+
if (!matchesExpected(canonicalRecord, expected)) return null;
|
|
161
|
+
if (!sameIdentity(canonicalRecord, objective)
|
|
162
|
+
|| canonicalSource.source_id !== canonicalRecord.definition.id
|
|
163
|
+
|| canonicalSource.source_version !== canonicalRecord.definition.revision
|
|
164
|
+
|| canonicalSource.digest !== canonicalRecord.digest
|
|
165
|
+
|| JSON.stringify(canonicalSource.provenance) !== JSON.stringify(canonicalRecord.definition.provenance)) return null;
|
|
166
|
+
return object;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return object.source === null
|
|
170
|
+
&& object.record === null
|
|
171
|
+
&& object.objective === null
|
|
172
|
+
&& reason(object.reason)
|
|
173
|
+
? object
|
|
174
|
+
: null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default normalizeObjectiveReadback;
|
package/ui/outcome-knowledge.css
CHANGED
|
@@ -379,11 +379,14 @@
|
|
|
379
379
|
.knowledge-outcome .knowledge-notice { display: flex; flex-wrap: wrap; gap: .4rem .65rem; align-items: center; margin-top: .65rem; padding: .65rem .7rem; border-left: 3px solid var(--knowledge-green); background: var(--knowledge-green-soft); font-size: .78rem; }
|
|
380
380
|
.knowledge-outcome .knowledge-notice.warning { border-left-color: var(--knowledge-amber); background: var(--knowledge-amber-soft); }
|
|
381
381
|
.knowledge-outcome .knowledge-notice.error { border-left-color: var(--knowledge-red); background: var(--knowledge-red-soft); }
|
|
382
|
+
.knowledge-outcome .knowledge-conflict-detail { flex-basis: 100%; color: var(--knowledge-muted); }
|
|
383
|
+
.knowledge-outcome .knowledge-conflict-comparison { flex-basis: 100%; align-items: stretch; }
|
|
384
|
+
.knowledge-outcome .knowledge-conflict-comparison .knowledge-long-text { min-height: 3rem; max-height: 18rem; overflow: auto; }
|
|
382
385
|
.knowledge-outcome .knowledge-inline-status { color: var(--knowledge-muted); font-size: .78rem; }
|
|
383
386
|
.knowledge-outcome .knowledge-warning-copy { color: var(--knowledge-amber); font-size: .78rem; }
|
|
384
387
|
.knowledge-outcome .knowledge-success-copy { color: var(--knowledge-green); font-size: .78rem; }
|
|
385
388
|
.knowledge-outcome .knowledge-permission-copy { color: var(--knowledge-muted); font-size: .78rem; }
|
|
386
|
-
.knowledge-outcome .knowledge-comparison { display: grid; grid-template-columns:
|
|
389
|
+
.knowledge-outcome .knowledge-comparison { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; margin-top: .55rem; }
|
|
387
390
|
.knowledge-outcome .knowledge-source-column,
|
|
388
391
|
.knowledge-outcome .knowledge-proposal-column { min-width: 0; border: 1px solid var(--knowledge-line); border-radius: .45rem; padding: .75rem; }
|
|
389
392
|
.knowledge-outcome .knowledge-source-column { background: var(--knowledge-canvas); }
|
|
@@ -408,6 +411,20 @@
|
|
|
408
411
|
.knowledge-outcome .knowledge-evidence-adopted { color: var(--knowledge-green); }
|
|
409
412
|
.knowledge-outcome .knowledge-evidence-excluded { color: var(--knowledge-muted); }
|
|
410
413
|
|
|
414
|
+
/* Registration is an authoring task. Give the source and proposal practical
|
|
415
|
+
width on desktop, then keep the inspector below the work instead of
|
|
416
|
+
leaving a narrow authoring column beside unused rail space. */
|
|
417
|
+
@media (min-width: 881px) {
|
|
418
|
+
.knowledge-outcome .knowledge-outcome-body[data-view="capture"] { grid-template-columns: minmax(0, 1fr); }
|
|
419
|
+
.knowledge-outcome .knowledge-outcome-body[data-view="capture"] .knowledge-outcome-primary { padding-right: 0; }
|
|
420
|
+
.knowledge-outcome .knowledge-outcome-body[data-view="capture"] .knowledge-outcome-detail {
|
|
421
|
+
margin-top: 1.4rem;
|
|
422
|
+
padding-left: 0;
|
|
423
|
+
border-top: 1px solid var(--knowledge-line);
|
|
424
|
+
border-left: 0;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
411
428
|
@media (max-width: 880px) {
|
|
412
429
|
.knowledge-outcome .knowledge-outcome-body { grid-template-columns: minmax(0, 1fr); }
|
|
413
430
|
.knowledge-outcome .knowledge-outcome-primary { padding-right: 0; }
|