@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
|
-
|
|
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.
|
|
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": "
|
|
410
|
+
"gitHead": "da34b87b5890ae3bc4c89bffe6faa54a302e3276"
|
|
411
411
|
}
|