@unson/brainbase-mcp 0.6.0 → 0.8.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.
Files changed (148) hide show
  1. package/contracts/foundation/graph-history.sql +634 -0
  2. package/contracts/judgment-value-proof/schema.json +43 -1
  3. package/dist/canonical-task-contract.d.ts +9 -0
  4. package/dist/canonical-task-contract.js +39 -0
  5. package/dist/canonical-task-principal.d.ts +16 -0
  6. package/dist/canonical-task-principal.js +36 -0
  7. package/dist/canonical-task-service.d.ts +251 -0
  8. package/dist/canonical-task-service.js +820 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +107 -0
  11. package/dist/company-os-evaluation.d.ts +193 -0
  12. package/dist/company-os-evaluation.js +1150 -0
  13. package/dist/company-os-impact-review.d.ts +248 -0
  14. package/dist/company-os-impact-review.js +663 -0
  15. package/dist/company-os-learning-adoption.d.ts +288 -0
  16. package/dist/company-os-learning-adoption.js +944 -0
  17. package/dist/company-os-objectives.d.ts +63 -0
  18. package/dist/company-os-objectives.js +234 -0
  19. package/dist/company-os-receipt-adapter.d.ts +109 -0
  20. package/dist/company-os-receipt-adapter.js +636 -0
  21. package/dist/constraint-exception-store.d.ts +23 -0
  22. package/dist/constraint-exception-store.js +108 -0
  23. package/dist/constraint-resolution.d.ts +175 -0
  24. package/dist/constraint-resolution.js +922 -0
  25. package/dist/decision-adapter.d.ts +145 -0
  26. package/dist/decision-adapter.js +586 -0
  27. package/dist/durable-wait-http.d.ts +51 -0
  28. package/dist/durable-wait-http.js +497 -0
  29. package/dist/durable-waits.d.ts +234 -0
  30. package/dist/durable-waits.js +944 -0
  31. package/dist/execution-authority-http.d.ts +28 -0
  32. package/dist/execution-authority-http.js +359 -0
  33. package/dist/execution-authority.d.ts +201 -0
  34. package/dist/execution-authority.js +634 -0
  35. package/dist/foundation-authenticated-tools.d.ts +56 -0
  36. package/dist/foundation-authenticated-tools.js +347 -0
  37. package/dist/foundation-catalog.d.ts +26 -0
  38. package/dist/foundation-catalog.js +206 -0
  39. package/dist/foundation-constraint-store.d.ts +50 -0
  40. package/dist/foundation-constraint-store.js +215 -0
  41. package/dist/foundation-graph-http.d.ts +43 -0
  42. package/dist/foundation-graph-http.js +243 -0
  43. package/dist/foundation-graph-write.d.ts +90 -0
  44. package/dist/foundation-graph-write.js +447 -0
  45. package/dist/foundation-http.d.ts +90 -0
  46. package/dist/foundation-http.js +439 -0
  47. package/dist/foundation-public-provider.d.ts +140 -0
  48. package/dist/foundation-public-provider.js +192 -0
  49. package/dist/foundation-store.d.ts +132 -0
  50. package/dist/foundation-store.js +654 -0
  51. package/dist/graph-corrections.d.ts +157 -0
  52. package/dist/graph-corrections.js +620 -0
  53. package/dist/graph-foundation-reader.d.ts +60 -0
  54. package/dist/graph-foundation-reader.js +494 -0
  55. package/dist/graph-retrieval.d.ts +6 -1
  56. package/dist/graph-revision-reader.d.ts +80 -0
  57. package/dist/graph-revision-reader.js +246 -0
  58. package/dist/graph-web-http.d.ts +26 -0
  59. package/dist/graph-web-http.js +215 -0
  60. package/dist/graph-web.d.ts +364 -0
  61. package/dist/graph-web.js +639 -0
  62. package/dist/judgment-dag-composition.d.ts +224 -0
  63. package/dist/judgment-dag-composition.js +731 -0
  64. package/dist/judgment-dag.d.ts +2 -0
  65. package/dist/judgment-dag.js +1 -0
  66. package/dist/judgment-problem-snapshot.d.ts +213 -0
  67. package/dist/judgment-problem-snapshot.js +1073 -0
  68. package/dist/judgment-value-proof-review.d.ts +166 -0
  69. package/dist/judgment-value-proof-review.js +437 -0
  70. package/dist/judgment-value-proof.d.ts +28 -1
  71. package/dist/judgment-value-proof.js +53 -8
  72. package/dist/judgment-view-http.d.ts +22 -0
  73. package/dist/judgment-view-http.js +128 -0
  74. package/dist/judgment-view.d.ts +222 -0
  75. package/dist/judgment-view.js +627 -0
  76. package/dist/knowledge-adapter.d.ts +112 -0
  77. package/dist/knowledge-adapter.js +691 -0
  78. package/dist/knowledge-continuation.d.ts +127 -0
  79. package/dist/knowledge-continuation.js +446 -0
  80. package/dist/knowledge-lookup.d.ts +178 -0
  81. package/dist/knowledge-lookup.js +758 -0
  82. package/dist/local-web-fetch-bridge.d.ts +5 -0
  83. package/dist/local-web-fetch-bridge.js +37 -0
  84. package/dist/local-web-host.d.ts +109 -0
  85. package/dist/local-web-host.js +688 -0
  86. package/dist/local-web-security.d.ts +25 -0
  87. package/dist/local-web-security.js +95 -0
  88. package/dist/ontology-foundation.d.ts +209 -0
  89. package/dist/ontology-foundation.js +1000 -0
  90. package/dist/ontology.d.ts +13 -0
  91. package/dist/ontology.js +14 -0
  92. package/dist/organization-connection.d.ts +164 -0
  93. package/dist/organization-connection.js +428 -0
  94. package/dist/organization-graph.d.ts +15 -0
  95. package/dist/organization-graph.js +24 -4
  96. package/dist/personal-knowledge.d.ts +132 -0
  97. package/dist/personal-knowledge.js +779 -0
  98. package/dist/personal-memory-handover.d.ts +108 -0
  99. package/dist/personal-memory-handover.js +369 -0
  100. package/dist/philosophy-revision-reader.d.ts +85 -0
  101. package/dist/philosophy-revision-reader.js +241 -0
  102. package/dist/portable-graph.d.ts +24 -0
  103. package/dist/portable-graph.js +77 -0
  104. package/dist/problem-candidates.d.ts +149 -0
  105. package/dist/problem-candidates.js +898 -0
  106. package/dist/problem-selection.d.ts +238 -0
  107. package/dist/problem-selection.js +995 -0
  108. package/dist/resource-reservation-http.d.ts +31 -0
  109. package/dist/resource-reservation-http.js +371 -0
  110. package/dist/resource-reservations.d.ts +221 -0
  111. package/dist/resource-reservations.js +577 -0
  112. package/dist/server.d.ts +104 -2
  113. package/dist/server.js +186 -4
  114. package/dist/ssot.d.ts +6 -1
  115. package/dist/ssot.js +63 -5
  116. package/dist/streamable-http.d.ts +42 -0
  117. package/dist/streamable-http.js +288 -0
  118. package/dist/types.d.ts +20 -0
  119. package/dist/value-proof-review-http.d.ts +25 -0
  120. package/dist/value-proof-review-http.js +185 -0
  121. package/dist/world-model.d.ts +199 -0
  122. package/dist/world-model.js +955 -0
  123. package/package.json +230 -3
  124. package/ui/brainbase-tokens.css +56 -0
  125. package/ui/graph-own-share.js +367 -0
  126. package/ui/graph-projects-view.css +23 -0
  127. package/ui/graph-projects-view.js +730 -0
  128. package/ui/graph-registry-view.css +101 -0
  129. package/ui/graph-registry-view.js +804 -0
  130. package/ui/graph-view-shared.css +522 -0
  131. package/ui/graph-view-shared.js +1227 -0
  132. package/ui/judgment-view.css +312 -0
  133. package/ui/judgment-view.js +508 -0
  134. package/ui/local-web-shell.css +396 -0
  135. package/ui/local-web-shell.js +378 -0
  136. package/ui/objective-editor-http-port.js +154 -0
  137. package/ui/objective-editor.css +286 -0
  138. package/ui/objective-editor.js +1586 -0
  139. package/ui/outcome-knowledge.css +50 -14
  140. package/ui/outcome-knowledge.js +15 -1
  141. package/ui/outcome-mana.css +91 -14
  142. package/ui/outcome-mana.js +958 -61
  143. package/ui/value-proof-review.css +504 -0
  144. package/ui/value-proof-review.js +1104 -0
  145. package/ui/workspace-kit.css +485 -0
  146. package/ui/workspace-kit.js +229 -0
  147. package/ui/world-model-view.css +188 -0
  148. package/ui/world-model-view.js +486 -0
@@ -0,0 +1,166 @@
1
+ import { type JudgmentValueProof } from './judgment-value-proof.js';
2
+ export declare const JUDGMENT_VALUE_PROOF_FILE_SUFFIX = ".value-proof.json";
3
+ export declare const JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA = "brainbase-judgment-value-proof-feedback-v1";
4
+ export declare const JUDGMENT_VALUE_PROOF_FEEDBACK_FILE = "judgment-value-proof-feedback.jsonl";
5
+ export type JudgmentValueProofReviewSection = 'needs_human' | 'blocked' | 'continued' | 'other';
6
+ export type JudgmentValueProofFeedbackStatus = 'accepted' | 'corrected' | 'next_time_ask' | 'reverted';
7
+ /** What the owner's feedback asks to change. `delegation` is the ask/continue boundary. */
8
+ export type JudgmentValueProofFeedbackLayer = 'delegation' | 'method' | 'objective' | 'world_model' | 'philosophy' | 'other';
9
+ export declare const JUDGMENT_VALUE_PROOF_FEEDBACK_LAYERS: readonly JudgmentValueProofFeedbackLayer[];
10
+ export interface JudgmentValueProofJournalEntry {
11
+ readonly file: string;
12
+ readonly proof: JudgmentValueProof;
13
+ }
14
+ export interface JudgmentValueProofJournalRejection {
15
+ readonly file: string;
16
+ readonly reason: string;
17
+ }
18
+ export type JudgmentValueProofJournalRead = {
19
+ readonly status: 'unavailable';
20
+ readonly root: string;
21
+ readonly reason: string;
22
+ } | {
23
+ readonly status: 'available';
24
+ readonly root: string;
25
+ readonly entries: readonly JudgmentValueProofJournalEntry[];
26
+ readonly rejected: readonly JudgmentValueProofJournalRejection[];
27
+ readonly latest_recorded_at: string | null;
28
+ };
29
+ export interface JudgmentValueProofFeedbackRecord {
30
+ readonly schema_version: typeof JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA;
31
+ readonly feedback_id: string;
32
+ readonly intent_id: string;
33
+ readonly decision_attempt_id: string;
34
+ readonly status: JudgmentValueProofFeedbackStatus;
35
+ readonly summary: string | null;
36
+ /** Absent on records written before the layer was introduced. */
37
+ readonly target_layer?: JudgmentValueProofFeedbackLayer | null;
38
+ readonly recorded_at: string;
39
+ }
40
+ export interface JudgmentValueProofFeedbackInput {
41
+ readonly dataDir?: string;
42
+ readonly intent_id: string;
43
+ readonly decision_attempt_id: string;
44
+ readonly status: JudgmentValueProofFeedbackStatus;
45
+ readonly summary?: string | null;
46
+ readonly target_layer?: JudgmentValueProofFeedbackLayer | null;
47
+ readonly now?: Date;
48
+ }
49
+ export interface JudgmentValueProofReviewItem {
50
+ readonly file: string;
51
+ readonly section: JudgmentValueProofReviewSection;
52
+ readonly proof: JudgmentValueProof;
53
+ readonly feedback_history: readonly JudgmentValueProofFeedbackRecord[];
54
+ }
55
+ export type JudgmentValueProofReviewHome = {
56
+ readonly status: 'unavailable';
57
+ readonly root: string;
58
+ readonly reason: string;
59
+ } | {
60
+ readonly status: 'available';
61
+ readonly root: string;
62
+ readonly coverage: {
63
+ readonly saved: number;
64
+ readonly rejected: number;
65
+ readonly latest_recorded_at: string | null;
66
+ readonly possibly_stalled: boolean;
67
+ };
68
+ readonly sections: Readonly<Record<JudgmentValueProofReviewSection, readonly JudgmentValueProofReviewItem[]>>;
69
+ readonly delegation_map: JudgmentDelegationMap;
70
+ readonly rejected: readonly JudgmentValueProofJournalRejection[];
71
+ };
72
+ /** Whether Brainbase currently proceeds on a kind of judgment, is being checked, or asks the owner. */
73
+ export type JudgmentDelegationState = 'delegated' | 'verifying' | 'returned';
74
+ /** Rows are kinds when recorded; otherwise the reason code, which is not a kind and is shown apart. */
75
+ export type JudgmentDelegationRowSource = 'judgment_kind' | 'reason_code' | 'unrecorded';
76
+ export type JudgmentDelegationStateBasis = {
77
+ readonly reason: 'latest_judgment_returned';
78
+ readonly at: string;
79
+ } | {
80
+ readonly reason: 'latest_feedback';
81
+ readonly status: JudgmentValueProofFeedbackStatus;
82
+ readonly target_layer: JudgmentValueProofFeedbackLayer | null;
83
+ readonly at: string;
84
+ } | {
85
+ readonly reason: 'no_feedback';
86
+ };
87
+ export interface JudgmentDelegationItemRef {
88
+ readonly intent_id: string;
89
+ readonly decision_attempt_id: string;
90
+ readonly row_key: string;
91
+ }
92
+ export interface JudgmentDelegationRow {
93
+ readonly key: string;
94
+ readonly source: JudgmentDelegationRowSource;
95
+ /** Kind label or reason code; null for judgments that recorded neither. */
96
+ readonly label: string | null;
97
+ readonly state: JudgmentDelegationState;
98
+ readonly state_basis: JudgmentDelegationStateBasis;
99
+ readonly counts: {
100
+ readonly continued: number;
101
+ readonly returned: number;
102
+ readonly rated: number;
103
+ readonly by_feedback: Readonly<Record<JudgmentValueProofFeedbackStatus, number>>;
104
+ readonly corrected_or_reverted: number;
105
+ readonly inherited: number;
106
+ };
107
+ readonly latest_recorded_at: string;
108
+ /** Newest first. */
109
+ readonly items: readonly JudgmentDelegationItemRef[];
110
+ }
111
+ export interface JudgmentDelegationMap {
112
+ readonly judged: number;
113
+ readonly kind_recorded: number;
114
+ readonly rated: number;
115
+ /** Kind rows first, then reason-code rows, then the unrecorded row. */
116
+ readonly rows: readonly JudgmentDelegationRow[];
117
+ /** Continued without asking, then corrected or reverted by the owner (P14). */
118
+ readonly corrected_after_continue: readonly JudgmentDelegationItemRef[];
119
+ /** Continued without asking after the owner asked to be asked for that row. */
120
+ readonly continued_after_ask: readonly JudgmentDelegationItemRef[];
121
+ }
122
+ export declare function defaultJudgmentJournalRoot(dataDir?: string): string;
123
+ /**
124
+ * Reads saved judgment value proofs from `<root>/*.value-proof.json` and
125
+ * `<root>/<session>/*.value-proof.json`. The journal is read-only here.
126
+ */
127
+ export declare function readJudgmentValueProofJournal(options?: {
128
+ readonly root?: string;
129
+ }): Promise<JudgmentValueProofJournalRead>;
130
+ export declare function classifyJudgmentValueProof(proof: JudgmentValueProof): JudgmentValueProofReviewSection;
131
+ /** Reads all recorded feedback in append order. A missing file means no feedback has been recorded. */
132
+ export declare function readJudgmentValueProofFeedback(options?: {
133
+ readonly dataDir?: string;
134
+ }): Promise<readonly JudgmentValueProofFeedbackRecord[]>;
135
+ /**
136
+ * Appends one owner feedback record without touching the judgment journal.
137
+ * Re-sending the same attempt, status and summary returns the existing record.
138
+ */
139
+ export declare function recordJudgmentValueProofFeedback(input: JudgmentValueProofFeedbackInput): Promise<{
140
+ readonly record: JudgmentValueProofFeedbackRecord;
141
+ readonly created: boolean;
142
+ }>;
143
+ /** Returns the proof with its latest owner feedback applied, plus the feedback history for that attempt. */
144
+ export declare function applyJudgmentValueProofFeedback(proof: JudgmentValueProof, records: readonly JudgmentValueProofFeedbackRecord[]): {
145
+ readonly proof: JudgmentValueProof;
146
+ readonly history: readonly JudgmentValueProofFeedbackRecord[];
147
+ };
148
+ export declare function buildJudgmentValueProofReviewHome(journal: JudgmentValueProofJournalRead, feedback: readonly JudgmentValueProofFeedbackRecord[], options?: {
149
+ readonly now?: Date;
150
+ readonly staleAfterMs?: number;
151
+ }): JudgmentValueProofReviewHome;
152
+ /**
153
+ * Groups reviewable judgments by kind and decides, per kind, whether Brainbase is trusted to
154
+ * proceed, is still being checked, or returns the judgment to the owner. Deterministic: the state
155
+ * follows only the newest judgment and the newest owner feedback in the row.
156
+ */
157
+ export declare function buildJudgmentDelegationMap(items: readonly JudgmentValueProofReviewItem[], feedback: readonly JudgmentValueProofFeedbackRecord[]): JudgmentDelegationMap;
158
+ export interface JudgmentValueProofFeedbackSummary {
159
+ /** Decisions with at least one feedback record; each counts once, by its latest feedback. */
160
+ readonly rated: number;
161
+ readonly by_status: Readonly<Record<JudgmentValueProofFeedbackStatus, number>>;
162
+ /** Latest `corrected` and `next_time_ask` feedback by the layer they ask to change. */
163
+ readonly by_layer: Readonly<Record<JudgmentValueProofFeedbackLayer | 'unrecorded', number>>;
164
+ }
165
+ /** Counts the latest feedback per decision, for deciding where revisions should live. */
166
+ export declare function summarizeJudgmentValueProofFeedback(records: readonly JudgmentValueProofFeedbackRecord[]): JudgmentValueProofFeedbackSummary;
@@ -0,0 +1,437 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { appendFile, mkdir, readdir, readFile } from 'node:fs/promises';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { placeJudgmentValueProof, validateJudgmentValueProof } from './judgment-value-proof.js';
5
+ import { defaultDataDir } from './paths.js';
6
+ export const JUDGMENT_VALUE_PROOF_FILE_SUFFIX = '.value-proof.json';
7
+ export const JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA = 'brainbase-judgment-value-proof-feedback-v1';
8
+ export const JUDGMENT_VALUE_PROOF_FEEDBACK_FILE = 'judgment-value-proof-feedback.jsonl';
9
+ const FEEDBACK_SUMMARY_LIMIT = 500;
10
+ const DEFAULT_STALE_AFTER_MS = 48 * 60 * 60 * 1000;
11
+ const FEEDBACK_STATUSES = [
12
+ 'accepted',
13
+ 'corrected',
14
+ 'next_time_ask',
15
+ 'reverted'
16
+ ];
17
+ const SUMMARY_REQUIRED = ['corrected', 'reverted'];
18
+ export const JUDGMENT_VALUE_PROOF_FEEDBACK_LAYERS = [
19
+ 'delegation',
20
+ 'method',
21
+ 'objective',
22
+ 'world_model',
23
+ 'philosophy',
24
+ 'other'
25
+ ];
26
+ export function defaultJudgmentJournalRoot(dataDir = defaultDataDir()) {
27
+ return join(resolve(dataDir), 'judgment-journal');
28
+ }
29
+ function errorMessage(error) {
30
+ return error instanceof Error ? error.message : String(error);
31
+ }
32
+ function errorCode(error) {
33
+ return typeof error === 'object' && error !== null && 'code' in error
34
+ ? String(error.code)
35
+ : undefined;
36
+ }
37
+ function compareRecordedAtDesc(left, right) {
38
+ return left < right ? 1 : left > right ? -1 : 0;
39
+ }
40
+ async function readProofFile(file) {
41
+ try {
42
+ const proof = validateJudgmentValueProof(JSON.parse(await readFile(file, 'utf8')));
43
+ return { file, proof };
44
+ }
45
+ catch (error) {
46
+ return { file, reason: errorMessage(error) };
47
+ }
48
+ }
49
+ /**
50
+ * Reads saved judgment value proofs from `<root>/*.value-proof.json` and
51
+ * `<root>/<session>/*.value-proof.json`. The journal is read-only here.
52
+ */
53
+ export async function readJudgmentValueProofJournal(options = {}) {
54
+ const root = resolve(options.root ?? defaultJudgmentJournalRoot());
55
+ let topLevel;
56
+ try {
57
+ topLevel = await readdir(root, { withFileTypes: true });
58
+ }
59
+ catch (error) {
60
+ const code = errorCode(error);
61
+ const reason = code === 'ENOENT'
62
+ ? 'judgment_journal_not_found'
63
+ : code === 'ENOTDIR'
64
+ ? 'judgment_journal_not_directory'
65
+ : `judgment_journal_unreadable:${code ?? errorMessage(error)}`;
66
+ return { status: 'unavailable', root, reason };
67
+ }
68
+ const files = [];
69
+ for (const entry of topLevel) {
70
+ const path = join(root, entry.name);
71
+ if (entry.isFile() && entry.name.endsWith(JUDGMENT_VALUE_PROOF_FILE_SUFFIX)) {
72
+ files.push(path);
73
+ }
74
+ else if (entry.isDirectory()) {
75
+ const nested = await readdir(path, { withFileTypes: true });
76
+ for (const child of nested) {
77
+ if (child.isFile() && child.name.endsWith(JUDGMENT_VALUE_PROOF_FILE_SUFFIX)) {
78
+ files.push(join(path, child.name));
79
+ }
80
+ }
81
+ }
82
+ }
83
+ const results = await Promise.all(files.sort().map(readProofFile));
84
+ const entries = results
85
+ .filter((result) => 'proof' in result)
86
+ .sort((left, right) => compareRecordedAtDesc(left.proof.recorded_at, right.proof.recorded_at)
87
+ || left.file.localeCompare(right.file));
88
+ const rejected = results.filter((result) => 'reason' in result);
89
+ return {
90
+ status: 'available',
91
+ root,
92
+ entries,
93
+ rejected,
94
+ latest_recorded_at: entries[0]?.proof.recorded_at ?? null
95
+ };
96
+ }
97
+ export function classifyJudgmentValueProof(proof) {
98
+ if (placeJudgmentValueProof(proof).web_surface === 'none')
99
+ return 'other';
100
+ if (proof.state === 'waiting_human')
101
+ return 'needs_human';
102
+ if (proof.state === 'blocked')
103
+ return 'blocked';
104
+ if (proof.interruption.resolution === 'continued_without_human')
105
+ return 'continued';
106
+ return 'other';
107
+ }
108
+ function feedbackFile(dataDir) {
109
+ return join(resolve(dataDir ?? defaultDataDir()), JUDGMENT_VALUE_PROOF_FEEDBACK_FILE);
110
+ }
111
+ function requiredId(value, field) {
112
+ const normalized = typeof value === 'string' ? value.trim() : '';
113
+ if (!normalized)
114
+ throw new TypeError(`${field} must be a non-empty string`);
115
+ return normalized;
116
+ }
117
+ function normalizeSummary(status, summary) {
118
+ const normalized = typeof summary === 'string' ? summary.trim() : '';
119
+ if (!normalized && SUMMARY_REQUIRED.includes(status)) {
120
+ throw new TypeError(`${status} feedback requires summary`);
121
+ }
122
+ if (Array.from(normalized).length > FEEDBACK_SUMMARY_LIMIT) {
123
+ throw new TypeError(`feedback summary must be at most ${FEEDBACK_SUMMARY_LIMIT} characters`);
124
+ }
125
+ return normalized || null;
126
+ }
127
+ /**
128
+ * `next_time_ask` always targets the delegation boundary; `corrected` must say what it corrects;
129
+ * `accepted` and `reverted` carry no layer.
130
+ */
131
+ function normalizeTargetLayer(status, layer) {
132
+ if (layer !== undefined && layer !== null && !JUDGMENT_VALUE_PROOF_FEEDBACK_LAYERS.includes(layer)) {
133
+ throw new TypeError(`unsupported feedback target_layer: ${String(layer)}`);
134
+ }
135
+ if (status === 'next_time_ask') {
136
+ if (layer && layer !== 'delegation')
137
+ throw new TypeError('next_time_ask feedback targets delegation');
138
+ return 'delegation';
139
+ }
140
+ if (status === 'corrected') {
141
+ if (!layer)
142
+ throw new TypeError('corrected feedback requires target_layer');
143
+ return layer;
144
+ }
145
+ if (layer)
146
+ throw new TypeError(`${status} feedback does not take target_layer`);
147
+ return null;
148
+ }
149
+ function feedbackId(decisionAttemptId, status, layer, summary) {
150
+ return `sha256:${createHash('sha256').update(`${decisionAttemptId}\n${status}\n${layer ?? ''}\n${summary ?? ''}`).digest('hex')}`;
151
+ }
152
+ function parseFeedbackRecord(line, lineNumber) {
153
+ let value;
154
+ try {
155
+ value = JSON.parse(line);
156
+ }
157
+ catch (error) {
158
+ throw new Error(`judgment_value_proof_feedback_corrupt:line_${lineNumber}`, { cause: error });
159
+ }
160
+ if (value.schema_version !== JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA
161
+ || !FEEDBACK_STATUSES.includes(value.status)
162
+ || typeof value.feedback_id !== 'string'
163
+ || typeof value.intent_id !== 'string'
164
+ || typeof value.decision_attempt_id !== 'string'
165
+ || typeof value.recorded_at !== 'string'
166
+ || !(value.summary === null || typeof value.summary === 'string')
167
+ || !(value.target_layer === undefined || value.target_layer === null
168
+ || JUDGMENT_VALUE_PROOF_FEEDBACK_LAYERS.includes(value.target_layer))) {
169
+ throw new Error(`judgment_value_proof_feedback_invalid:line_${lineNumber}`);
170
+ }
171
+ return value;
172
+ }
173
+ /** Reads all recorded feedback in append order. A missing file means no feedback has been recorded. */
174
+ export async function readJudgmentValueProofFeedback(options = {}) {
175
+ let text;
176
+ try {
177
+ text = await readFile(feedbackFile(options.dataDir), 'utf8');
178
+ }
179
+ catch (error) {
180
+ if (errorCode(error) === 'ENOENT')
181
+ return [];
182
+ throw error;
183
+ }
184
+ return text.split('\n')
185
+ .map((line, index) => ({ line, lineNumber: index + 1 }))
186
+ .filter(({ line }) => line.trim())
187
+ .map(({ line, lineNumber }) => parseFeedbackRecord(line, lineNumber));
188
+ }
189
+ /**
190
+ * Appends one owner feedback record without touching the judgment journal.
191
+ * Re-sending the same attempt, status and summary returns the existing record.
192
+ */
193
+ export async function recordJudgmentValueProofFeedback(input) {
194
+ const intentId = requiredId(input.intent_id, 'intent_id');
195
+ const decisionAttemptId = requiredId(input.decision_attempt_id, 'decision_attempt_id');
196
+ if (!FEEDBACK_STATUSES.includes(input.status)) {
197
+ throw new TypeError(`unsupported feedback status: ${String(input.status)}`);
198
+ }
199
+ const summary = normalizeSummary(input.status, input.summary);
200
+ const targetLayer = normalizeTargetLayer(input.status, input.target_layer);
201
+ const id = feedbackId(decisionAttemptId, input.status, targetLayer, summary);
202
+ const existing = (await readJudgmentValueProofFeedback({ dataDir: input.dataDir }))
203
+ .find((record) => record.feedback_id === id);
204
+ if (existing)
205
+ return { record: existing, created: false };
206
+ const record = {
207
+ schema_version: JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA,
208
+ feedback_id: id,
209
+ intent_id: intentId,
210
+ decision_attempt_id: decisionAttemptId,
211
+ status: input.status,
212
+ summary,
213
+ target_layer: targetLayer,
214
+ recorded_at: (input.now ?? new Date()).toISOString()
215
+ };
216
+ const file = feedbackFile(input.dataDir);
217
+ await mkdir(dirname(file), { recursive: true });
218
+ await appendFile(file, `${JSON.stringify(record)}\n`, 'utf8');
219
+ const readBack = (await readJudgmentValueProofFeedback({ dataDir: input.dataDir }))
220
+ .find((entry) => entry.feedback_id === id);
221
+ if (!readBack)
222
+ throw new Error('judgment_value_proof_feedback_readback_missing');
223
+ return { record: readBack, created: true };
224
+ }
225
+ /** Returns the proof with its latest owner feedback applied, plus the feedback history for that attempt. */
226
+ export function applyJudgmentValueProofFeedback(proof, records) {
227
+ const history = records.filter((record) => record.intent_id === proof.intent_id
228
+ && record.decision_attempt_id === proof.decision_attempt_id);
229
+ const latest = history.at(-1);
230
+ if (!latest)
231
+ return { proof, history };
232
+ const applied = {
233
+ ...proof,
234
+ feedback: {
235
+ status: latest.status,
236
+ summary: latest.summary,
237
+ evidence_ref: {
238
+ kind: 'human_feedback',
239
+ ref: `judgment-value-proof-feedback:${latest.feedback_id}`,
240
+ status: 'verified',
241
+ label: '本人の評価'
242
+ }
243
+ }
244
+ };
245
+ return { proof: validateJudgmentValueProof(applied), history };
246
+ }
247
+ export function buildJudgmentValueProofReviewHome(journal, feedback, options = {}) {
248
+ if (journal.status === 'unavailable')
249
+ return journal;
250
+ const sections = {
251
+ needs_human: [],
252
+ blocked: [],
253
+ continued: [],
254
+ other: []
255
+ };
256
+ for (const entry of journal.entries) {
257
+ const { proof, history } = applyJudgmentValueProofFeedback(entry.proof, feedback);
258
+ const section = classifyJudgmentValueProof(proof);
259
+ sections[section].push({ file: entry.file, section, proof, feedback_history: history });
260
+ }
261
+ const delegationMap = buildJudgmentDelegationMap([...sections.needs_human, ...sections.blocked, ...sections.continued], feedback);
262
+ const now = (options.now ?? new Date()).getTime();
263
+ const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
264
+ const latest = journal.latest_recorded_at === null ? null : Date.parse(journal.latest_recorded_at);
265
+ const possiblyStalled = latest === null || Number.isNaN(latest) || now - latest > staleAfterMs;
266
+ return {
267
+ status: 'available',
268
+ root: journal.root,
269
+ coverage: {
270
+ saved: journal.entries.length,
271
+ rejected: journal.rejected.length,
272
+ latest_recorded_at: journal.latest_recorded_at,
273
+ possibly_stalled: possiblyStalled
274
+ },
275
+ sections,
276
+ delegation_map: delegationMap,
277
+ rejected: journal.rejected
278
+ };
279
+ }
280
+ function delegationRowIdentity(proof) {
281
+ const kind = proof.decision.judgment_kind;
282
+ if (kind)
283
+ return { key: `kind:${kind.key}`, source: 'judgment_kind', label: kind.label };
284
+ const reason = proof.interruption.reason_code?.trim();
285
+ if (reason)
286
+ return { key: `reason:${reason}`, source: 'reason_code', label: reason };
287
+ return { key: 'unrecorded', source: 'unrecorded', label: null };
288
+ }
289
+ function laterFeedback(left, right) {
290
+ return !left || right.record.recorded_at > left.record.recorded_at
291
+ || (right.record.recorded_at === left.record.recorded_at && right.order > left.order);
292
+ }
293
+ function returnsDelegation(record) {
294
+ return record.status === 'next_time_ask' || record.status === 'reverted'
295
+ || (record.status === 'corrected' && record.target_layer === 'delegation');
296
+ }
297
+ function newestFirst(entries) {
298
+ return [...entries].sort((left, right) => compareRecordedAtDesc(left.at, right.at)).map((entry) => entry.ref);
299
+ }
300
+ function itemRef(item, rowKey) {
301
+ return { intent_id: item.proof.intent_id, decision_attempt_id: item.proof.decision_attempt_id, row_key: rowKey };
302
+ }
303
+ /**
304
+ * Groups reviewable judgments by kind and decides, per kind, whether Brainbase is trusted to
305
+ * proceed, is still being checked, or returns the judgment to the owner. Deterministic: the state
306
+ * follows only the newest judgment and the newest owner feedback in the row.
307
+ */
308
+ export function buildJudgmentDelegationMap(items, feedback) {
309
+ const order = new Map(feedback.map((record, index) => [record, index]));
310
+ const sorted = [...items].sort((left, right) => compareRecordedAtDesc(left.proof.recorded_at, right.proof.recorded_at)
311
+ || left.file.localeCompare(right.file));
312
+ const groups = new Map();
313
+ for (const item of sorted) {
314
+ const identity = delegationRowIdentity(item.proof);
315
+ const group = groups.get(identity.key) ?? { identity, items: [] };
316
+ group.items.push(item);
317
+ groups.set(identity.key, group);
318
+ }
319
+ const correctedAfterContinue = [];
320
+ const continuedAfterAsk = [];
321
+ const rows = [];
322
+ for (const { identity, items: rowItems } of groups.values()) {
323
+ const byFeedback = { accepted: 0, corrected: 0, next_time_ask: 0, reverted: 0 };
324
+ let latestFeedback = null;
325
+ // The first ask is the cutoff: every later judgment that still proceeded without asking ignored it.
326
+ let firstAsk = null;
327
+ let continued = 0;
328
+ let returned = 0;
329
+ let rated = 0;
330
+ let inherited = 0;
331
+ for (const item of rowItems) {
332
+ const resolution = item.proof.interruption.resolution;
333
+ if (resolution === 'continued_without_human')
334
+ continued += 1;
335
+ if (resolution === 'human_required')
336
+ returned += 1;
337
+ if ((item.proof.decision.inheritance?.sources.length ?? 0) > 0)
338
+ inherited += 1;
339
+ const latest = item.feedback_history.at(-1);
340
+ if (latest) {
341
+ rated += 1;
342
+ byFeedback[latest.status] += 1;
343
+ if (resolution === 'continued_without_human' && (latest.status === 'corrected' || latest.status === 'reverted')) {
344
+ correctedAfterContinue.push({ ref: itemRef(item, identity.key), at: item.proof.recorded_at });
345
+ }
346
+ }
347
+ for (const record of item.feedback_history) {
348
+ const candidate = { record, order: order.get(record) ?? -1 };
349
+ if (laterFeedback(latestFeedback, candidate))
350
+ latestFeedback = candidate;
351
+ if (record.status === 'next_time_ask' && (!firstAsk || record.recorded_at < firstAsk.recorded_at))
352
+ firstAsk = record;
353
+ }
354
+ }
355
+ if (firstAsk) {
356
+ for (const item of rowItems) {
357
+ if (item.proof.interruption.resolution === 'continued_without_human'
358
+ && item.proof.recorded_at > firstAsk.recorded_at
359
+ && item.feedback_history.at(-1)?.status !== 'accepted') {
360
+ continuedAfterAsk.push({ ref: itemRef(item, identity.key), at: item.proof.recorded_at });
361
+ }
362
+ }
363
+ }
364
+ const newest = rowItems[0].proof;
365
+ let state;
366
+ let basis;
367
+ const feedbackBasis = (record) => ({
368
+ reason: 'latest_feedback',
369
+ status: record.status,
370
+ target_layer: record.target_layer ?? (record.status === 'next_time_ask' ? 'delegation' : null),
371
+ at: record.recorded_at
372
+ });
373
+ if (newest.interruption.resolution === 'human_required') {
374
+ state = 'returned';
375
+ basis = { reason: 'latest_judgment_returned', at: newest.recorded_at };
376
+ }
377
+ else if (latestFeedback && returnsDelegation(latestFeedback.record)) {
378
+ state = 'returned';
379
+ basis = feedbackBasis(latestFeedback.record);
380
+ }
381
+ else if (newest.interruption.resolution === 'continued_without_human' && latestFeedback?.record.status === 'accepted') {
382
+ state = 'delegated';
383
+ basis = feedbackBasis(latestFeedback.record);
384
+ }
385
+ else {
386
+ state = 'verifying';
387
+ basis = latestFeedback ? feedbackBasis(latestFeedback.record) : { reason: 'no_feedback' };
388
+ }
389
+ rows.push({
390
+ key: identity.key,
391
+ source: identity.source,
392
+ // The newest judgment names a kind row, so a relabelled kind shows its current label.
393
+ label: identity.source === 'judgment_kind' ? newest.decision.judgment_kind?.label ?? identity.label : identity.label,
394
+ state,
395
+ state_basis: basis,
396
+ counts: {
397
+ continued,
398
+ returned,
399
+ rated,
400
+ by_feedback: byFeedback,
401
+ corrected_or_reverted: byFeedback.corrected + byFeedback.reverted,
402
+ inherited
403
+ },
404
+ latest_recorded_at: newest.recorded_at,
405
+ items: rowItems.map((item) => itemRef(item, identity.key))
406
+ });
407
+ }
408
+ const sourceRank = { judgment_kind: 0, reason_code: 1, unrecorded: 2 };
409
+ rows.sort((left, right) => sourceRank[left.source] - sourceRank[right.source]
410
+ || compareRecordedAtDesc(left.latest_recorded_at, right.latest_recorded_at)
411
+ || left.key.localeCompare(right.key));
412
+ return {
413
+ judged: sorted.length,
414
+ kind_recorded: sorted.filter((item) => item.proof.decision.judgment_kind).length,
415
+ rated: rows.reduce((total, row) => total + row.counts.rated, 0),
416
+ rows,
417
+ corrected_after_continue: newestFirst(correctedAfterContinue),
418
+ continued_after_ask: newestFirst(continuedAfterAsk)
419
+ };
420
+ }
421
+ /** Counts the latest feedback per decision, for deciding where revisions should live. */
422
+ export function summarizeJudgmentValueProofFeedback(records) {
423
+ const latest = new Map();
424
+ for (const record of records)
425
+ latest.set(`${record.intent_id}\u0000${record.decision_attempt_id}`, record);
426
+ const byStatus = { accepted: 0, corrected: 0, next_time_ask: 0, reverted: 0 };
427
+ const byLayer = {
428
+ delegation: 0, method: 0, objective: 0, world_model: 0, philosophy: 0, other: 0, unrecorded: 0
429
+ };
430
+ for (const record of latest.values()) {
431
+ byStatus[record.status] += 1;
432
+ if (record.status === 'corrected' || record.status === 'next_time_ask') {
433
+ byLayer[record.target_layer ?? (record.status === 'next_time_ask' ? 'delegation' : 'unrecorded')] += 1;
434
+ }
435
+ }
436
+ return { rated: latest.size, by_status: byStatus, by_layer: byLayer };
437
+ }
@@ -12,9 +12,33 @@ export interface JudgmentValueProofArtifactRef {
12
12
  ref: string;
13
13
  label?: string;
14
14
  }
15
+ /** Which part of the organization's judgment a basis came from. Absent means not recorded. */
16
+ export type JudgmentValueProofBasisLayer = 'philosophy' | 'objective' | 'world_model' | 'method' | 'constraint' | 'other';
15
17
  export interface JudgmentValueProofBasis {
16
18
  entity_id: string;
17
19
  application: string;
20
+ layer?: JudgmentValueProofBasisLayer;
21
+ /** Version of the referenced entity used for this judgment. */
22
+ version?: string | null;
23
+ }
24
+ export interface JudgmentValueProofInheritanceSource {
25
+ kind: 'judgment' | 'method' | 'decision' | 'other';
26
+ ref: string;
27
+ version: string | null;
28
+ label: string;
29
+ }
30
+ /** Earlier experience carried into this judgment, recorded at judgment time. */
31
+ export interface JudgmentValueProofInheritance {
32
+ sources: JudgmentValueProofInheritanceSource[];
33
+ /** Conditions judged to be the same as the source, so the source could be reused. */
34
+ same_conditions: string[];
35
+ /** Conditions that differed and were checked again for this judgment. */
36
+ rechecked_conditions: string[];
37
+ }
38
+ /** Stable grouping for delegation scope, e.g. `production_release`. */
39
+ export interface JudgmentValueProofKind {
40
+ key: string;
41
+ label: string;
18
42
  }
19
43
  export interface JudgmentValueProofHumanOption {
20
44
  id: string;
@@ -39,6 +63,8 @@ export interface JudgmentValueProof {
39
63
  work_impact: string | null;
40
64
  basis: JudgmentValueProofBasis[];
41
65
  prior_learning_reused: boolean | 'unconfirmed';
66
+ judgment_kind?: JudgmentValueProofKind | null;
67
+ inheritance?: JudgmentValueProofInheritance | null;
42
68
  };
43
69
  execution: {
44
70
  status: 'not_started' | 'executing' | 'completed' | 'blocked';
@@ -65,7 +91,8 @@ export interface JudgmentValueProofPlacement {
65
91
  agent_progress: 'silent' | 'show';
66
92
  agent_completion: 'silent' | 'show';
67
93
  companion_attention: 'none' | 'human_decision' | 'blocked' | 'outcome_unconfirmed' | 'feedback_requested';
68
- web_surface: 'none';
94
+ /** `review`: listed on the local owner review surface (same scope as the weekly digest). */
95
+ web_surface: 'none' | 'review';
69
96
  weekly_digest: 'exclude' | 'include';
70
97
  }
71
98
  export interface JudgmentValueProofCompanionItem {