@unson/brainbase-mcp 0.23.1 → 0.23.3

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.
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { KNOWLEDGE_LOOKUP_ALLOWED_FIELD_ROOTS, validateKnowledgeLookupFields, validateKnowledgeLookupFinish, } from './knowledge-lookup.js';
3
3
  const TERMINAL = new Set(['satisfied', 'unresolved', 'needs_user_input', 'cancelled']);
4
4
  const ACTION_KINDS = new Set(['search', 'read', 'follow_relation', 'finish']);
5
- const KNOWLEDGE_LOOKUP_FIELD_GUIDANCE = `required_fieldsは自然文ではなく公開field pathを指定してください(例: content, body, statement, markdown, summary, name, environments.production.endpoint)。許可root: ${[...KNOWLEDGE_LOOKUP_ALLOWED_FIELD_ROOTS].join(', ')}`;
5
+ const KNOWLEDGE_LOOKUP_FIELD_GUIDANCE = `required_fieldsは自然文ではなく公開field pathを指定してください。Graph本文はcontentを使い、bodyはソース投影が明示的に公開する場合だけ指定してください(例: content, body, statement, markdown, summary, name, environments.production.endpoint)。許可root: ${[...KNOWLEDGE_LOOKUP_ALLOWED_FIELD_ROOTS].join(', ')}`;
6
6
  const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
7
7
  const canonical = (value) => JSON.stringify(value, (_key, item) => record(item)
8
8
  ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b)))
@@ -678,7 +678,7 @@ export const knowledgeLookupTools = [{
678
678
  type: 'array',
679
679
  minItems: 1,
680
680
  maxItems: MAX_FIELDS,
681
- description: 'Public field paths only; use roots such as content, body, statement, markdown, summary, name, or environments.production.endpoint.',
681
+ description: 'Public field paths only; for Graph text use content. Use body only when the source projection explicitly exposes a body field. Other roots include statement, markdown, summary, name, or environments.production.endpoint.',
682
682
  items: { type: 'string', minLength: 1, maxLength: MAX_FIELD_LENGTH },
683
683
  },
684
684
  known_entity_id: { type: 'string', minLength: 1, maxLength: 1_000 },
@@ -1,5 +1,6 @@
1
1
  import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
2
2
  import { type ObjectiveDefinitionBuilder } from './foundation-http.js';
3
+ import type { JudgmentHistoryEntrypoint } from './judgment-history.js';
3
4
  import { JudgmentValueProofJournalCache } from './judgment-value-proof-review.js';
4
5
  import { type MeetingMinutesLineageCorrectionHook } from './meeting-minutes-lineage-lifecycle.js';
5
6
  import type { MinutesLineageActor, MinutesLineageStore } from './meeting-minutes-lineage.js';
@@ -25,6 +26,7 @@ export declare const LOCAL_WEB_STATUS_PATH = "/api/local/status";
25
26
  export declare const LOCAL_WEB_WORLD_MODEL_PREFIX = "/api/world-model";
26
27
  export declare const LOCAL_WEB_GRAPH_PREFIX = "/api/graph";
27
28
  export declare const LOCAL_WEB_MEETING_MINUTES_PREFIX = "/api/meeting-minutes";
29
+ export declare const LOCAL_WEB_JUDGMENT_HISTORY_PREFIX: "/api/judgment-history";
28
30
  /**
29
31
  * An Objective is a few KB of text and criteria. 64 KiB leaves room for long
30
32
  * Japanese text (about 20k characters) while keeping a loopback request that
@@ -43,6 +45,8 @@ export interface LocalGraphState {
43
45
  export interface LocalWebModuleContext {
44
46
  readonly dataDir: string;
45
47
  readonly journalRoot: string;
48
+ /** Explicit source label for this host's configured owner journal. */
49
+ readonly judgmentHistoryEntrypoint?: JudgmentHistoryEntrypoint;
46
50
  readonly token: string;
47
51
  readonly now: () => Date;
48
52
  /** The host's one listing cache for the judgment journal, shared by every screen that reads it. */
@@ -98,6 +102,8 @@ export interface LocalWebHostOptions {
98
102
  readonly dataDir?: string;
99
103
  /** Judgment journal root. Defaults to the journal inside the data directory. */
100
104
  readonly journalRoot?: string;
105
+ /** Source label for the owner journal; defaults to the Codex local host. */
106
+ readonly judgmentHistoryEntrypoint?: JudgmentHistoryEntrypoint;
101
107
  readonly token?: string;
102
108
  /** Directory containing the packaged `ui/` files. */
103
109
  readonly uiDir?: string;
@@ -153,6 +159,8 @@ export declare function isTrustedLocalWrite(request: IncomingMessage, token: str
153
159
  /** Reads the Graph format and owner without creating or migrating anything. */
154
160
  export declare function readLocalGraphState(dataDir: string): Promise<LocalGraphState>;
155
161
  export declare function createLocalStatusModule(context: LocalWebModuleContext): LocalWebModule;
162
+ /** Common normal judgment history projection over the configured owner journal. */
163
+ export declare function createJudgmentHistoryModule(context: LocalWebModuleContext): LocalWebModule;
156
164
  export declare function createValueProofModule(context: LocalWebModuleContext): LocalWebModule;
157
165
  /** Native meeting minutes use the host's authenticated local single-owner principal. */
158
166
  export declare function createMeetingMinutesModule(context: LocalWebModuleContext): LocalWebModule;
@@ -8,6 +8,8 @@ import { foundationCatalogOrEmpty, foundationKey } from './foundation-catalog.js
8
8
  import { createFoundationHttpRouter, createObjectiveFoundationRoute, FOUNDATION_HTTP_PREFIX, FOUNDATION_OBJECTIVE_ROUTE_PREFIX } from './foundation-http.js';
9
9
  import { createFoundationRevisionStore, FoundationStoreError } from './foundation-store.js';
10
10
  import { createGraphWebHttpHandler } from './graph-web-http.js';
11
+ import { createJudgmentHistoryHttpHandler, JUDGMENT_HISTORY_HTTP_PREFIX } from './judgment-history-http.js';
12
+ import { createJudgmentHistoryReader, createLocalJudgmentHistoryFeedbackWriter, createLocalJudgmentHistorySource } from './judgment-history.js';
11
13
  import { defaultJudgmentJournalRoot, JudgmentValueProofJournalCache } from './judgment-value-proof-review.js';
12
14
  import { createMeetingMinutesHttpHandler } from './meeting-minutes-http.js';
13
15
  import { createMeetingMinutesLineageHttpHandler } from './meeting-minutes-lineage-http.js';
@@ -41,6 +43,7 @@ export const LOCAL_WEB_STATUS_PATH = '/api/local/status';
41
43
  export const LOCAL_WEB_WORLD_MODEL_PREFIX = '/api/world-model';
42
44
  export const LOCAL_WEB_GRAPH_PREFIX = '/api/graph';
43
45
  export const LOCAL_WEB_MEETING_MINUTES_PREFIX = '/api/meeting-minutes';
46
+ export const LOCAL_WEB_JUDGMENT_HISTORY_PREFIX = JUDGMENT_HISTORY_HTTP_PREFIX;
44
47
  /**
45
48
  * An Objective is a few KB of text and criteria. 64 KiB leaves room for long
46
49
  * Japanese text (about 20k characters) while keeping a loopback request that
@@ -162,6 +165,37 @@ export function createLocalStatusModule(context) {
162
165
  }
163
166
  };
164
167
  }
168
+ /** Common normal judgment history projection over the configured owner journal. */
169
+ export function createJudgmentHistoryModule(context) {
170
+ const source = createLocalJudgmentHistorySource({
171
+ root: context.journalRoot,
172
+ entrypoint: context.judgmentHistoryEntrypoint ?? 'codex'
173
+ });
174
+ const reader = createJudgmentHistoryReader({
175
+ source,
176
+ now: context.now
177
+ });
178
+ const writeFeedback = createLocalJudgmentHistoryFeedbackWriter({
179
+ root: context.journalRoot,
180
+ reader,
181
+ now: context.now
182
+ });
183
+ return {
184
+ id: 'judgment-history',
185
+ uiFiles: [],
186
+ handle: createJudgmentHistoryHttpHandler({
187
+ reader,
188
+ basePath: LOCAL_WEB_JUDGMENT_HISTORY_PREFIX,
189
+ writeFeedback,
190
+ assertWriteAllowed(request) {
191
+ if (isTrustedLocalWrite(request, context.token))
192
+ return;
193
+ throw rejectUntrustedWrite(request, context.token)
194
+ ?? new LocalWebHttpError(403, 'web_token_required', 'A valid launch token is required');
195
+ }
196
+ })
197
+ };
198
+ }
165
199
  export function createValueProofModule(context) {
166
200
  const handler = createValueProofReviewHttpHandler({
167
201
  journalRoot: context.journalRoot,
@@ -857,6 +891,7 @@ export function createGraphWebModule(context) {
857
891
  export function defaultLocalWebModules(context) {
858
892
  return [
859
893
  createLocalStatusModule(context),
894
+ createJudgmentHistoryModule(context),
860
895
  createValueProofModule(context),
861
896
  createMeetingMinutesModule(context),
862
897
  createObjectiveFoundationModule(context),
@@ -934,6 +969,7 @@ export function createLocalWebHost(options = {}) {
934
969
  const context = {
935
970
  dataDir,
936
971
  journalRoot,
972
+ judgmentHistoryEntrypoint: options.judgmentHistoryEntrypoint ?? 'codex',
937
973
  token,
938
974
  now,
939
975
  journalCache: new JudgmentValueProofJournalCache(),
@@ -10,7 +10,8 @@
10
10
  */
11
11
  import type { FoundationAcl, FoundationRevision, FoundationScope } from './ontology-foundation.js';
12
12
  import type { ProblemCandidateRecord, ProblemCandidateStore, ProblemCandidateStoreContext } from './problem-candidates.js';
13
- import { type JudgmentDAGCompositionDefinition, type JudgmentDAGCompositionRunRequest, type JudgmentDAGProblemSnapshotReference } from './judgment-dag-composition.js';
13
+ import { type JudgmentDAGCompositionDefinition, type JudgmentDAGCompositionErrorCode, type JudgmentDAGCompositionStatus, type JudgmentDAGCompositionRunRecord, type JudgmentDAGCompositionRunRequest, type JudgmentDAGProblemSnapshotReference } from './judgment-dag-composition.js';
14
+ import type { JudgmentDAGJSONValue } from './judgment-dag-runner.js';
14
15
  export declare const PROBLEM_SELECTION_CONTRACT_VERSION: "problem-selection.v1";
15
16
  export declare const PROBLEM_SELECTION_RECORD_VERSION: "problem-selection-record.v1";
16
17
  export declare const PROBLEM_SELECTION_DIRECTORY: "problem-selections";
@@ -98,6 +99,87 @@ export interface ProblemSelectionEvaluationResult {
98
99
  readonly unknowns?: readonly ProblemSelectionUnknown[];
99
100
  readonly objectiveConflicts?: readonly ProblemSelectionObjectiveConflict[];
100
101
  }
102
+ /**
103
+ * The immutable identity that the Company OS producer binds to a run.
104
+ *
105
+ * This is an input from a trusted Host/Company Authority context, not an
106
+ * authentication assertion. OSS intentionally does not infer or authenticate
107
+ * a caller from these fields. The producer and Organization store must verify
108
+ * the binding and the current ACL before accepting an event.
109
+ */
110
+ export interface JudgmentDAGCompositionRunHistoryBinding {
111
+ readonly organization_id: string;
112
+ readonly tenant_id: string;
113
+ readonly owner_person_id: string;
114
+ readonly project_code: string | null;
115
+ readonly turn_ref: string;
116
+ }
117
+ export type JudgmentDAGCompositionRunHistoryProducerName = 'company_os' | 'codex' | 'claude_code' | 'mana';
118
+ export interface JudgmentDAGCompositionRunHistorySource {
119
+ readonly producer: JudgmentDAGCompositionRunHistoryProducerName;
120
+ readonly producer_revision: string;
121
+ }
122
+ /** References must be supplied by the trusted producer from actual reads. */
123
+ export interface JudgmentDAGCompositionRunHistoryReferences {
124
+ readonly problem_snapshot: JudgmentDAGProblemSnapshotReference;
125
+ readonly foundation: readonly JudgmentDAGJSONValue[];
126
+ readonly evidence: readonly JudgmentDAGJSONValue[];
127
+ }
128
+ export type JudgmentDAGCompositionRunHistoryState = 'started' | JudgmentDAGCompositionStatus | 'unknown';
129
+ export type JudgmentDAGCompositionRunHistoryFailureCode = JudgmentDAGCompositionErrorCode | 'unknown';
130
+ export interface JudgmentDAGCompositionRunHistoryFailure {
131
+ readonly code: JudgmentDAGCompositionRunHistoryFailureCode;
132
+ /** This message is fixed; raw executor/store exceptions are never persisted. */
133
+ readonly message: string;
134
+ }
135
+ export interface JudgmentDAGCompositionRunHistoryLifecycle {
136
+ readonly lifecycle_event_id: string;
137
+ readonly state: JudgmentDAGCompositionRunHistoryState;
138
+ readonly recorded_at: string;
139
+ readonly failure: JudgmentDAGCompositionRunHistoryFailure | null;
140
+ }
141
+ /**
142
+ * Port event understood by a trusted Host adapter and the Organization native
143
+ * store. The adapter may map this event to its envelope/store API, but it
144
+ * must preserve the run body and binding fields exactly.
145
+ */
146
+ export interface JudgmentDAGCompositionRunHistoryEvent {
147
+ readonly run_id: string;
148
+ readonly binding: JudgmentDAGCompositionRunHistoryBinding;
149
+ readonly source: JudgmentDAGCompositionRunHistorySource;
150
+ readonly semantic_scope: JudgmentDAGCompositionDefinition['scope'];
151
+ readonly lifecycle: JudgmentDAGCompositionRunHistoryLifecycle;
152
+ readonly composition_run: JudgmentDAGCompositionRunRecord | null;
153
+ readonly references: JudgmentDAGCompositionRunHistoryReferences;
154
+ }
155
+ /**
156
+ * Company OS native storage boundary. Implementations own trusted binding,
157
+ * current ACL, idempotency, and conflict checks. A caller that merely passes
158
+ * a body to this port is not thereby trusted.
159
+ *
160
+ * Retry contract: the caller supplies stable lifecycle_event_id and
161
+ * recorded_at values, including timestamps. A producer may retry its own
162
+ * transport with the identical event; this wrapper never retries the
163
+ * executor and never creates a new event identity or time.
164
+ */
165
+ export interface JudgmentDAGCompositionRunHistoryProducer {
166
+ readonly append: (event: JudgmentDAGCompositionRunHistoryEvent) => void | Promise<void>;
167
+ }
168
+ /** Short alias for callers that name the boundary as a CompositionRun port. */
169
+ export type JudgmentDAGCompositionRunProducer = JudgmentDAGCompositionRunHistoryProducer;
170
+ export interface JudgmentDAGCompositionRunHistoryLifecycleInput {
171
+ readonly lifecycle_event_id: string;
172
+ readonly recorded_at: string;
173
+ }
174
+ export interface JudgmentDAGCompositionRunHistoryOptions {
175
+ readonly producer: JudgmentDAGCompositionRunHistoryProducer;
176
+ /** Trusted binding supplied by Host/Company Authority; OSS does not derive it. */
177
+ readonly binding: JudgmentDAGCompositionRunHistoryBinding;
178
+ readonly source: JudgmentDAGCompositionRunHistorySource;
179
+ readonly references: JudgmentDAGCompositionRunHistoryReferences;
180
+ readonly started: JudgmentDAGCompositionRunHistoryLifecycleInput;
181
+ readonly terminal: JudgmentDAGCompositionRunHistoryLifecycleInput;
182
+ }
101
183
  export interface ProblemSelectionEvaluationRequest {
102
184
  readonly selectionId: string;
103
185
  readonly selectionProblem: JudgmentDAGProblemSnapshotReference;
@@ -224,6 +306,7 @@ export declare const selectProblem: typeof createProblemSelection;
224
306
  export declare function evaluateProblemSelectionWithComposition(input: {
225
307
  readonly compositionRequest: JudgmentDAGCompositionRunRequest;
226
308
  readonly resultInvocationId?: string;
309
+ readonly compositionRunHistory?: JudgmentDAGCompositionRunHistoryOptions;
227
310
  }): Promise<ProblemSelectionEvaluationResult>;
228
311
  export declare function computeProblemSelectionRecordId(record: ProblemSelectionRecord): ProblemSelectionRecordId;
229
312
  export declare function saveProblemSelectionRecord(request: SaveProblemSelectionRecordRequest): Promise<ProblemSelectionRecordReceipt>;
@@ -11,7 +11,7 @@
11
11
  import { createHash, randomUUID } from 'node:crypto';
12
12
  import { link, lstat, mkdir, open, readFile, unlink } from 'node:fs/promises';
13
13
  import path from 'node:path';
14
- import { createJudgmentDAGCompositionDefinition, executeJudgmentDAGComposition } from './judgment-dag-composition.js';
14
+ import { createJudgmentDAGCompositionDefinition, JudgmentDAGCompositionError, executeJudgmentDAGComposition } from './judgment-dag-composition.js';
15
15
  export const PROBLEM_SELECTION_CONTRACT_VERSION = 'problem-selection.v1';
16
16
  export const PROBLEM_SELECTION_RECORD_VERSION = 'problem-selection-record.v1';
17
17
  export const PROBLEM_SELECTION_DIRECTORY = 'problem-selections';
@@ -695,6 +695,100 @@ export async function createProblemSelection(request) {
695
695
  export const runProblemSelection = createProblemSelection;
696
696
  /** Alias for callers that name the operation as a selection. */
697
697
  export const selectProblem = createProblemSelection;
698
+ const SAFE_COMPOSITION_FAILURE_MESSAGE = 'DAG composition failed before a run record was returned';
699
+ const SAFE_COMPOSITION_HISTORY_FAILURE_MESSAGE = 'DAG composition history could not be saved';
700
+ const SAFE_COMPOSITION_RESULT_FAILURE_MESSAGE = 'selection composition result could not be read';
701
+ function snapshotCompositionHistoryMetadata(request, options) {
702
+ // Producer adapters receive a frozen JSON-only snapshot. This keeps a
703
+ // producer from changing the binding, references, run id, or scope between
704
+ // the started event and the terminal event while it performs persistence.
705
+ // Only metadata is cloned here: the request also contains executable
706
+ // snapshot/DAG/contract/port adapters and is intentionally never cloned.
707
+ return deepFreeze({
708
+ run_id: request.run_id,
709
+ binding: cloneJson(options.binding, 'compositionRunHistory.binding'),
710
+ source: cloneJson(options.source, 'compositionRunHistory.source'),
711
+ semantic_scope: cloneJson(request.delegation.scope, 'compositionRunHistory.semantic_scope'),
712
+ references: cloneJson(options.references, 'compositionRunHistory.references'),
713
+ started: cloneJson(options.started, 'compositionRunHistory.started'),
714
+ terminal: cloneJson(options.terminal, 'compositionRunHistory.terminal')
715
+ });
716
+ }
717
+ function compositionHistoryReview(reason) {
718
+ return {
719
+ status: 'human_review_required',
720
+ reason,
721
+ unknowns: [{ status: 'unknown', reason }],
722
+ objectiveConflicts: []
723
+ };
724
+ }
725
+ function compositionHistorySaveFailure() {
726
+ const reason = `selection composition history unavailable: ${SAFE_COMPOSITION_HISTORY_FAILURE_MESSAGE}`;
727
+ return compositionHistoryReview(reason);
728
+ }
729
+ function compositionFailureCode(cause) {
730
+ return cause instanceof JudgmentDAGCompositionError ? cause.code : 'unknown';
731
+ }
732
+ function compositionHistoryEvent(metadata, lifecycle, state, compositionRun, failure) {
733
+ return deepFreeze({
734
+ run_id: metadata.run_id,
735
+ binding: metadata.binding,
736
+ source: metadata.source,
737
+ semantic_scope: metadata.semantic_scope,
738
+ lifecycle: {
739
+ lifecycle_event_id: lifecycle.lifecycle_event_id,
740
+ state,
741
+ recorded_at: lifecycle.recorded_at,
742
+ failure
743
+ },
744
+ composition_run: compositionRun,
745
+ references: metadata.references
746
+ });
747
+ }
748
+ async function appendCompositionHistoryEvent(options, event) {
749
+ try {
750
+ await options.producer.append(event);
751
+ return true;
752
+ }
753
+ catch {
754
+ // The persistence implementation may retry its own transport. The
755
+ // wrapper does not retry or expose the store's raw error.
756
+ return false;
757
+ }
758
+ }
759
+ function isCompositionRunStatus(value) {
760
+ return value === 'completed' || value === 'failed' || value === 'held';
761
+ }
762
+ function sameCompositionHistoryJson(left, right) {
763
+ try {
764
+ return canonicalJson(left) === canonicalJson(right);
765
+ }
766
+ catch {
767
+ return false;
768
+ }
769
+ }
770
+ function isCompositionRunBoundToHistoryMetadata(run, metadata) {
771
+ return (run.run_id === metadata.run_id &&
772
+ isCompositionRunStatus(run.status) &&
773
+ sameCompositionHistoryJson(run.delegation.scope, metadata.semantic_scope) &&
774
+ sameCompositionHistoryJson(run.problem_snapshot, metadata.references.problem_snapshot));
775
+ }
776
+ function compositionResultFromRun(run, resultInvocationId) {
777
+ if (run.status !== 'completed') {
778
+ const reason = `selection composition status is ${run.status}`;
779
+ return compositionHistoryReview(reason);
780
+ }
781
+ const child = resultInvocationId === undefined
782
+ ? run.children[run.children.length - 1]
783
+ : run.children.find((entry) => entry.invocation_id === resultInvocationId);
784
+ if (child === undefined)
785
+ return compositionHistoryReview('selection composition returned no designated result child');
786
+ if (child.result.status !== 'completed' || !isPlainRecord(child.result.conclusion)) {
787
+ const reason = child.result.reason ?? `selection child status is ${child.result.status}`;
788
+ return compositionHistoryReview(reason);
789
+ }
790
+ return child.result.conclusion;
791
+ }
698
792
  /**
699
793
  * Adapter helper for a composition whose designated child returns the
700
794
  * selection contract in its conclusion. The meta Problem remains the exact
@@ -702,28 +796,60 @@ export const selectProblem = createProblemSelection;
702
796
  * requires a later Problem creation request.
703
797
  */
704
798
  export async function evaluateProblemSelectionWithComposition(input) {
799
+ const history = input.compositionRunHistory;
800
+ let historyMetadata;
801
+ if (history !== undefined) {
802
+ try {
803
+ historyMetadata = snapshotCompositionHistoryMetadata(input.compositionRequest, history);
804
+ }
805
+ catch {
806
+ return compositionHistoryReview(`selection composition unavailable: ${SAFE_COMPOSITION_FAILURE_MESSAGE}`);
807
+ }
808
+ }
809
+ if (history !== undefined) {
810
+ const startedEvent = compositionHistoryEvent(historyMetadata, historyMetadata.started, 'started', null, null);
811
+ if (!await appendCompositionHistoryEvent(history, startedEvent))
812
+ return compositionHistorySaveFailure();
813
+ }
705
814
  let run;
706
815
  try {
707
816
  run = await executeJudgmentDAGComposition(input.compositionRequest);
708
817
  }
709
818
  catch (cause) {
710
- const reason = cause instanceof Error ? cause.message : 'DAG composition failed';
711
- return { status: 'human_review_required', reason: `selection composition unavailable: ${reason}`, unknowns: [{ status: 'unknown', reason }], objectiveConflicts: [] };
819
+ const failureCode = compositionFailureCode(cause);
820
+ if (history !== undefined) {
821
+ const failureEvent = compositionHistoryEvent(historyMetadata, historyMetadata.terminal, failureCode === 'unknown' ? 'unknown' : 'failed', null, { code: failureCode, message: SAFE_COMPOSITION_FAILURE_MESSAGE });
822
+ if (!await appendCompositionHistoryEvent(history, failureEvent))
823
+ return compositionHistorySaveFailure();
824
+ }
825
+ return compositionHistoryReview(`selection composition unavailable: ${SAFE_COMPOSITION_FAILURE_MESSAGE}`);
712
826
  }
713
- if (run.status !== 'completed') {
714
- const reason = `selection composition status is ${run.status}`;
715
- return { status: 'human_review_required', reason, unknowns: [{ status: 'unknown', reason }], objectiveConflicts: [] };
827
+ if (history !== undefined) {
828
+ let terminalEvent;
829
+ try {
830
+ if (!isCompositionRunBoundToHistoryMetadata(run, historyMetadata)) {
831
+ terminalEvent = compositionHistoryEvent(historyMetadata, historyMetadata.terminal, 'unknown', null, { code: 'unknown', message: SAFE_COMPOSITION_FAILURE_MESSAGE });
832
+ }
833
+ else {
834
+ terminalEvent = compositionHistoryEvent(historyMetadata, historyMetadata.terminal, run.status, cloneJson(run, 'compositionRunHistory.composition_run'), null);
835
+ }
836
+ }
837
+ catch {
838
+ terminalEvent = compositionHistoryEvent(historyMetadata, historyMetadata.terminal, 'unknown', null, { code: 'unknown', message: SAFE_COMPOSITION_FAILURE_MESSAGE });
839
+ }
840
+ if (!await appendCompositionHistoryEvent(history, terminalEvent))
841
+ return compositionHistorySaveFailure();
842
+ if (terminalEvent.composition_run === null)
843
+ return compositionHistoryReview(SAFE_COMPOSITION_RESULT_FAILURE_MESSAGE);
716
844
  }
717
- const child = input.resultInvocationId === undefined
718
- ? run.children[run.children.length - 1]
719
- : run.children.find((entry) => entry.invocation_id === input.resultInvocationId);
720
- if (child === undefined)
721
- return { status: 'human_review_required', reason: 'selection composition returned no designated result child', unknowns: [{ status: 'unknown', reason: 'missing selection result child' }], objectiveConflicts: [] };
722
- if (child.result.status !== 'completed' || !isPlainRecord(child.result.conclusion)) {
723
- const reason = child.result.reason ?? `selection child status is ${child.result.status}`;
724
- return { status: 'human_review_required', reason, unknowns: [{ status: 'unknown', reason }], objectiveConflicts: [] };
845
+ try {
846
+ return compositionResultFromRun(run, input.resultInvocationId);
847
+ }
848
+ catch {
849
+ // The terminal event was appended before this extraction. Keep the
850
+ // persisted run while requiring a human review of the derived selection.
851
+ return compositionHistoryReview(SAFE_COMPOSITION_RESULT_FAILURE_MESSAGE);
725
852
  }
726
- return child.result.conclusion;
727
853
  }
728
854
  function selectionRecordPayload(record) {
729
855
  return record;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
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",
@@ -31,6 +31,14 @@
31
31
  "types": "./dist/judgment-frame.d.ts",
32
32
  "import": "./dist/judgment-frame.js"
33
33
  },
34
+ "./judgment-history": {
35
+ "types": "./dist/judgment-history.d.ts",
36
+ "import": "./dist/judgment-history.js"
37
+ },
38
+ "./judgment-history-http": {
39
+ "types": "./dist/judgment-history-http.d.ts",
40
+ "import": "./dist/judgment-history-http.js"
41
+ },
34
42
  "./judgment-value-proof": {
35
43
  "types": "./dist/judgment-value-proof.d.ts",
36
44
  "import": "./dist/judgment-value-proof.js"
@@ -115,6 +123,10 @@
115
123
  "types": "./dist/company-os-learning-adoption.d.ts",
116
124
  "import": "./dist/company-os-learning-adoption.js"
117
125
  },
126
+ "./company-os-judgment-history": {
127
+ "types": "./dist/company-os-judgment-history.d.ts",
128
+ "import": "./dist/company-os-judgment-history.js"
129
+ },
118
130
  "./meeting-minutes-lineage": {
119
131
  "types": "./dist/meeting-minutes-lineage.d.ts",
120
132
  "import": "./dist/meeting-minutes-lineage.js"
@@ -407,5 +419,5 @@
407
419
  "engines": {
408
420
  "node": ">=20"
409
421
  },
410
- "gitHead": "da34b87b5890ae3bc4c89bffe6faa54a302e3276"
422
+ "gitHead": "51eca010f2f8df4d24b44bd705467bee1161719d"
411
423
  }
@@ -26,6 +26,35 @@
26
26
  gap: 6px;
27
27
  }
28
28
 
29
+ .bb-jh-normal-filters {
30
+ display: flex;
31
+ flex: 1 1 280px;
32
+ flex-wrap: wrap;
33
+ gap: 8px;
34
+ }
35
+
36
+ .bb-jh-filter {
37
+ display: grid;
38
+ min-width: 130px;
39
+ gap: 4px;
40
+ color: var(--bb-color-muted);
41
+ font-size: 11px;
42
+ }
43
+
44
+ .bb-jh-filter span {
45
+ line-height: 1.2;
46
+ }
47
+
48
+ .bb-jh-filter select {
49
+ min-height: 38px;
50
+ border: 1px solid var(--bb-color-border-strong);
51
+ border-radius: var(--bb-radius-sm);
52
+ padding: 7px 28px 7px 10px;
53
+ background: var(--bb-color-panel);
54
+ color: var(--bb-color-ink);
55
+ font: inherit;
56
+ }
57
+
29
58
  .bb-jh-search {
30
59
  display: flex;
31
60
  flex: 1 1 230px;
@@ -167,17 +196,161 @@
167
196
  line-height: 1.5;
168
197
  }
169
198
 
199
+ .bb-jh-reference-availability,
200
+ .bb-jh-missing-fields {
201
+ color: var(--bb-color-warning-strong, var(--bb-color-muted)) !important;
202
+ }
203
+
204
+ .bb-jh-alternative-list {
205
+ display: grid;
206
+ gap: 10px;
207
+ margin: 0;
208
+ padding: 0 0 0 18px;
209
+ }
210
+
211
+ .bb-jh-alternative-list li {
212
+ display: grid;
213
+ gap: 2px;
214
+ line-height: 1.45;
215
+ }
216
+
217
+ .bb-jh-alternative-list li span {
218
+ color: var(--bb-color-muted);
219
+ font-size: 12px;
220
+ }
221
+
222
+ .bb-jh-pagination {
223
+ display: flex;
224
+ align-items: center;
225
+ justify-content: space-between;
226
+ gap: 12px;
227
+ margin: 14px 0 0;
228
+ color: var(--bb-color-muted);
229
+ font-size: 12px;
230
+ }
231
+
232
+ .bb-jh-pagination-count {
233
+ font-variant-numeric: tabular-nums;
234
+ }
235
+
170
236
  .bb-jh .bb-ws-definition dd > span {
171
237
  display: block;
172
238
  line-height: 1.5;
173
239
  }
174
240
 
241
+ .bb-jh-feedback-events,
242
+ .bb-jh-feedback-composer {
243
+ display: grid;
244
+ gap: 10px;
245
+ }
246
+
247
+ .bb-jh-feedback-event {
248
+ display: grid;
249
+ gap: 5px;
250
+ padding: 10px 0;
251
+ border-bottom: 1px solid var(--bb-color-border);
252
+ }
253
+
254
+ .bb-jh-feedback-event:last-child {
255
+ border-bottom: 0;
256
+ }
257
+
258
+ .bb-jh-feedback-event strong {
259
+ color: var(--bb-color-ink);
260
+ }
261
+
262
+ .bb-jh-feedback-event code,
263
+ .bb-jh-reference-export > code,
264
+ .bb-jh-feedback-composer > code {
265
+ color: var(--bb-color-quiet);
266
+ font-family: var(--bb-font-mono);
267
+ font-size: 10px;
268
+ overflow-wrap: anywhere;
269
+ }
270
+
271
+ .bb-jh-feedback-event p,
272
+ .bb-jh-reference-export p {
273
+ margin: 0;
274
+ color: var(--bb-color-muted);
275
+ font-size: 12px;
276
+ line-height: 1.5;
277
+ overflow-wrap: anywhere;
278
+ }
279
+
280
+ .bb-jh-reference-export {
281
+ display: grid;
282
+ gap: 7px;
283
+ margin-top: 7px;
284
+ padding: 9px;
285
+ border: 1px solid var(--bb-color-border);
286
+ border-radius: var(--bb-radius-sm);
287
+ background: var(--bb-color-panel-muted, var(--bb-color-panel));
288
+ }
289
+
290
+ .bb-jh-reference-export label,
291
+ .bb-jh-feedback-composer label {
292
+ color: var(--bb-color-muted);
293
+ font-size: 11px;
294
+ }
295
+
296
+ .bb-jh-reference-export textarea,
297
+ .bb-jh-feedback-composer textarea,
298
+ .bb-jh-feedback-composer select {
299
+ width: 100%;
300
+ min-height: 36px;
301
+ border: 1px solid var(--bb-color-border-strong);
302
+ border-radius: var(--bb-radius-sm);
303
+ padding: 8px 10px;
304
+ background: var(--bb-color-panel);
305
+ color: var(--bb-color-ink);
306
+ font: inherit;
307
+ resize: vertical;
308
+ }
309
+
310
+ .bb-jh-reference-payload {
311
+ margin: 0;
312
+ padding: 8px;
313
+ border-radius: var(--bb-radius-sm);
314
+ background: var(--bb-color-code, var(--bb-color-panel));
315
+ color: var(--bb-color-quiet);
316
+ font-family: var(--bb-font-mono);
317
+ font-size: 10px;
318
+ line-height: 1.45;
319
+ white-space: pre-wrap;
320
+ overflow-wrap: anywhere;
321
+ }
322
+
323
+ .bb-jh-copy-status,
324
+ .bb-jh-feedback-status {
325
+ color: var(--bb-color-muted);
326
+ font-size: 11px;
327
+ line-height: 1.45;
328
+ }
329
+
330
+ .bb-jh-feedback-status.is-saved {
331
+ color: var(--bb-color-success-strong, var(--bb-color-accent-strong));
332
+ }
333
+
334
+ .bb-jh-feedback-status.is-unavailable,
335
+ .bb-jh-feedback-status.is-unconfirmed,
336
+ .bb-jh-feedback-status.is-error {
337
+ color: var(--bb-color-warning-strong, var(--bb-color-muted));
338
+ }
339
+
175
340
  @media (max-width: 760px) {
176
341
  .bb-jh-controls {
177
342
  align-items: stretch;
178
343
  flex-direction: column;
179
344
  }
180
345
 
346
+ .bb-jh-normal-filters {
347
+ flex-direction: column;
348
+ }
349
+
350
+ .bb-jh-filter select {
351
+ width: 100%;
352
+ }
353
+
181
354
  .bb-jh-search {
182
355
  max-width: none;
183
356
  }