@unson/brainbase-mcp 0.23.6 → 0.23.7

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.
@@ -12,6 +12,14 @@ export interface CanonicalTaskContext extends JsonRecord {
12
12
  /** Opaque host-policy scope, not authority or task input. */
13
13
  storageScope?: string;
14
14
  }
15
+ /** Conditions to review; this metadata is not evidence of completion. */
16
+ export interface CanonicalTaskCompletionContract {
17
+ goal: string;
18
+ criteria: Array<{
19
+ id: string;
20
+ condition: string;
21
+ }>;
22
+ }
15
23
  export interface CanonicalTaskRecord extends JsonRecord {
16
24
  id: string;
17
25
  version: number;
@@ -29,11 +37,13 @@ export interface CanonicalTaskRecord extends JsonRecord {
29
37
  project_codes?: string[];
30
38
  /** What the work is for, in a few words (one per task); null when unlabelled. */
31
39
  purpose_label?: string | null;
40
+ completion_contract?: CanonicalTaskCompletionContract | null;
32
41
  created_at?: string | null;
33
42
  updated_at?: string | null;
34
43
  web_url?: string | null;
35
44
  }
36
45
  export interface CanonicalTaskCreateInput extends JsonRecord {
46
+ completion_contract?: unknown;
37
47
  title: string;
38
48
  description?: string | null;
39
49
  priority?: CanonicalTaskPriority | string | null;
@@ -46,6 +56,7 @@ export interface CanonicalTaskCreateInput extends JsonRecord {
46
56
  purpose_label?: string | null;
47
57
  }
48
58
  export interface CanonicalTaskUpdateInput extends JsonRecord {
59
+ completion_contract?: unknown;
49
60
  title?: string;
50
61
  description?: string | null;
51
62
  priority?: CanonicalTaskPriority | string | null;
@@ -26,6 +26,7 @@ const DEFAULT_LIMIT = 20;
26
26
  const MAX_LIMIT = 100;
27
27
  const RESERVED_IDEMPOTENCY_PREFIXES = ['api:', 'workflow:'];
28
28
  const CREATE_FIELDS = new Set([
29
+ 'completion_contract',
29
30
  'title',
30
31
  'description',
31
32
  'priority',
@@ -38,6 +39,7 @@ const CREATE_FIELDS = new Set([
38
39
  'purpose_label',
39
40
  ]);
40
41
  const MUTABLE_FIELDS = new Set([
42
+ 'completion_contract',
41
43
  'expected_version',
42
44
  'title',
43
45
  'description',
@@ -79,6 +81,39 @@ const MAX_PURPOSE_LABEL_LENGTH = 30;
79
81
  function normalizePurposeLabel(value) {
80
82
  return normalizeString(value, 'purpose_label', { max: MAX_PURPOSE_LABEL_LENGTH });
81
83
  }
84
+ function normalizeCompletionContract(value) {
85
+ if (value == null)
86
+ return null;
87
+ const object = (candidate, keys) => {
88
+ if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)
89
+ || ![Object.prototype, null].includes(Object.getPrototypeOf(candidate))
90
+ || Object.keys(candidate).some((key) => !keys.includes(key))) {
91
+ fail('validation_error', 'completion_contract contains an invalid object');
92
+ }
93
+ return candidate;
94
+ };
95
+ const text = (candidate, field) => {
96
+ if (typeof candidate !== 'string')
97
+ fail('validation_error', `${field} must be a string`);
98
+ return normalizeString(candidate, field, { required: true, max: 2000, multiline: true });
99
+ };
100
+ const contract = object(value, ['goal', 'criteria']);
101
+ const goal = text(contract.goal, 'completion_contract.goal');
102
+ if (!Array.isArray(contract.criteria) || contract.criteria.length < 1 || contract.criteria.length > 50) {
103
+ fail('validation_error', 'completion_contract.criteria must contain 1 to 50 conditions');
104
+ }
105
+ const ids = new Set();
106
+ const criteria = Array.from(contract.criteria, (candidate) => {
107
+ const criterion = object(candidate, ['id', 'condition']);
108
+ if (typeof criterion.id !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(criterion.id)
109
+ || ids.has(criterion.id)) {
110
+ fail('validation_error', 'completion_contract criterion id is invalid or duplicated');
111
+ }
112
+ ids.add(criterion.id);
113
+ return { id: criterion.id, condition: text(criterion.condition, 'completion_contract.criteria.condition') };
114
+ });
115
+ return { goal, criteria };
116
+ }
82
117
  function normalizeIsoDate(value, field) {
83
118
  const text = normalizeString(value, field);
84
119
  if (text == null)
@@ -635,7 +670,9 @@ export class CanonicalTaskService {
635
670
  if (!isCanonicalTaskPriority(priority))
636
671
  fail('validation_error', 'priority is invalid', 400, { field: 'priority' });
637
672
  const purposeLabel = normalizePurposeLabel(input.purpose_label);
673
+ const completionContract = normalizeCompletionContract(input.completion_contract);
638
674
  return {
675
+ ...(completionContract ? { completion_contract: completionContract } : {}),
639
676
  title,
640
677
  description: normalizeString(input.description, 'description', { max: 10000, multiline: true }),
641
678
  priority,
@@ -656,6 +693,8 @@ export class CanonicalTaskService {
656
693
  if (unknownFields.length > 0)
657
694
  fail('validation_error', 'Task update contains unsupported fields', 400, { fields: unknownFields });
658
695
  const patch = {};
696
+ if ('completion_contract' in input)
697
+ patch.completion_contract = normalizeCompletionContract(input.completion_contract);
659
698
  if ('title' in input)
660
699
  patch.title = normalizeString(input.title, 'title', { required: true, max: MAX_TITLE_LENGTH });
661
700
  if ('description' in input)
@@ -857,6 +896,12 @@ export class CanonicalTaskService {
857
896
  response.project_codes = [];
858
897
  if (response.purpose_label === undefined)
859
898
  response.purpose_label = null;
899
+ try {
900
+ response.completion_contract = normalizeCompletionContract(response.completion_contract);
901
+ }
902
+ catch {
903
+ fail('task_store_invalid', 'Stored Task completion contract is invalid', 503);
904
+ }
860
905
  return response;
861
906
  }
862
907
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.23.6",
3
+ "version": "0.23.7",
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",
@@ -423,5 +423,5 @@
423
423
  "engines": {
424
424
  "node": ">=20"
425
425
  },
426
- "gitHead": "d475d6282eda01482255c0a234999e194560d3c8"
426
+ "gitHead": "5dd3fb22d8a4b559ccb0e0ee14a6e9cdac6a4b4a"
427
427
  }