@unson/brainbase-mcp 0.23.0 → 0.23.1

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.
@@ -97,6 +97,8 @@ export interface KnowledgeLookupState {
97
97
  revision: number;
98
98
  status: KnowledgeStatus;
99
99
  started_at: number;
100
+ /** Set when the first validated retrieval action is reserved. Old states may omit it. */
101
+ retrieval_started_at?: number | null;
100
102
  limits: KnowledgeLookupLimits;
101
103
  attempts: KnowledgeAttempt[];
102
104
  required_fields: string[] | null;
@@ -20,6 +20,7 @@ export function createKnowledgeLookup({ lookupId, question, now = Date.now(), li
20
20
  revision: 0,
21
21
  status: 'resolving',
22
22
  started_at: now,
23
+ retrieval_started_at: null,
23
24
  limits: {
24
25
  attempts: bounded(limits.attempts, 8, 16),
25
26
  replans: bounded(limits.replans, 4, 8),
@@ -42,8 +43,51 @@ function terminate(state, reason, status = 'unresolved') {
42
43
  state.pending = null;
43
44
  return state;
44
45
  }
46
+ const hasPendingRetrieval = (state) => state.pending !== null && state.pending.kind !== 'finish';
47
+ const retrievalStartedAt = (state) => {
48
+ if (typeof state.retrieval_started_at === 'number' && Number.isFinite(state.retrieval_started_at)) {
49
+ return state.retrieval_started_at;
50
+ }
51
+ // States written before retrieval_started_at was introduced can still be
52
+ // bounded safely once they have evidence of a retrieval reservation.
53
+ if (state.attempts.length > 0 || hasPendingRetrieval(state))
54
+ return state.started_at;
55
+ return null;
56
+ };
57
+ const retrievalPhase = (state) => {
58
+ if (hasPendingRetrieval(state))
59
+ return 'in_flight';
60
+ if (state.attempts.length > 0)
61
+ return 'attempted';
62
+ return 'not_started';
63
+ };
64
+ function recordTimedOutPendingRetrieval(state) {
65
+ const pending = state.pending;
66
+ if (!pending)
67
+ return;
68
+ state.pending = null;
69
+ if (pending.kind === 'finish')
70
+ return;
71
+ if (state.attempts.some((attempt) => attempt.attempt_id === pending.tool_use_id))
72
+ return;
73
+ // The host cannot tell whether a timed-out reservation reached the tool.
74
+ // Keep the reservation as an unknown transport attempt rather than treating
75
+ // a late or missing result as a successful read.
76
+ state.attempts.push({
77
+ attempt_id: pending.tool_use_id,
78
+ kind: pending.kind,
79
+ fingerprint: pending.fingerprint,
80
+ outcome: 'transport_error',
81
+ references: [],
82
+ searched_scope: {},
83
+ missing_fields: state.required_fields ?? []
84
+ });
85
+ state.revision += 1;
86
+ }
45
87
  function expire(state, now) {
46
- if (!isKnowledgeLookupTerminal(state) && now - state.started_at >= state.limits.milliseconds) {
88
+ const startedAt = retrievalStartedAt(state);
89
+ if (startedAt !== null && !isKnowledgeLookupTerminal(state) && now - startedAt >= state.limits.milliseconds) {
90
+ recordTimedOutPendingRetrieval(state);
47
91
  terminate(state, 'time_budget_exhausted');
48
92
  }
49
93
  return state;
@@ -196,8 +240,10 @@ function validFinish(state, action) {
196
240
  }
197
241
  export function prepareKnowledgeAction(previous, input, toolUseId, now = Date.now()) {
198
242
  const state = expire(structuredClone(previous), now);
199
- if (isKnowledgeLookupTerminal(state))
243
+ if (isKnowledgeLookupTerminal(state)) {
244
+ recordTimedOutPendingRetrieval(state);
200
245
  return { state, allowed: false, reason: state.termination_reason ?? state.status };
246
+ }
201
247
  if (state.pending)
202
248
  return { state, allowed: false, reason: 'attempt_in_flight' };
203
249
  if (!record(input))
@@ -312,6 +358,9 @@ export function prepareKnowledgeAction(previous, input, toolUseId, now = Date.no
312
358
  }
313
359
  state.replans += 1;
314
360
  }
361
+ if (state.retrieval_started_at == null && state.attempts.length === 0 && !hasPendingRetrieval(state)) {
362
+ state.retrieval_started_at = now;
363
+ }
315
364
  state.pending = {
316
365
  tool_use_id: toolUseId,
317
366
  kind: action.kind,
@@ -323,6 +372,14 @@ export function prepareKnowledgeAction(previous, input, toolUseId, now = Date.no
323
372
  }
324
373
  export function recordKnowledgeResult(previous, input, result, toolUseId, now = Date.now()) {
325
374
  const state = structuredClone(previous);
375
+ if (isKnowledgeLookupTerminal(state)) {
376
+ // A late PostToolUse must never reopen a finished lookup. Preserve a stale
377
+ // retrieval reservation as unknown while keeping the terminal state.
378
+ recordTimedOutPendingRetrieval(state);
379
+ return state;
380
+ }
381
+ if (isKnowledgeLookupTerminal(expire(state, now)))
382
+ return state;
326
383
  if (typeof toolUseId === 'string' && state.finished_calls.includes(toolUseId))
327
384
  return state;
328
385
  if (!state.pending ||
@@ -411,8 +468,10 @@ export function recordKnowledgeResult(previous, input, result, toolUseId, now =
411
468
  }
412
469
  export function stopKnowledgeLookup(previous, now = Date.now()) {
413
470
  const state = expire(structuredClone(previous), now);
414
- if (isKnowledgeLookupTerminal(state))
471
+ if (isKnowledgeLookupTerminal(state)) {
472
+ recordTimedOutPendingRetrieval(state);
415
473
  return { state, block: false, reason: knowledgeLookupContext(state) };
474
+ }
416
475
  state.stop_requests += 1;
417
476
  if (state.stop_requests > state.limits.replans) {
418
477
  terminate(state, 'model_did_not_produce_executable_plan');
@@ -444,6 +503,7 @@ export function knowledgeLookupContext(state) {
444
503
  revision: state.revision,
445
504
  question: state.question,
446
505
  status: state.status,
506
+ retrieval_phase: retrievalPhase(state),
447
507
  required_fields: state.required_fields,
448
508
  attempts: state.attempts,
449
509
  remaining_attempts: Math.max(0, state.limits.attempts - state.attempts.length),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
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",
@@ -407,5 +407,5 @@
407
407
  "engines": {
408
408
  "node": ">=20"
409
409
  },
410
- "gitHead": "25fb5aa910ba704d704768d3df5f271b085df5a2"
410
+ "gitHead": "da34b87b5890ae3bc4c89bffe6faa54a302e3276"
411
411
  }