@unson/brainbase-mcp 0.7.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.
- package/contracts/foundation/graph-history.sql +634 -0
- package/contracts/judgment-value-proof/schema.json +43 -1
- package/dist/canonical-task-service.js +16 -5
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +107 -0
- package/dist/company-os-objectives.d.ts +11 -0
- package/dist/company-os-objectives.js +84 -47
- package/dist/foundation-authenticated-tools.d.ts +56 -0
- package/dist/foundation-authenticated-tools.js +347 -0
- package/dist/foundation-graph-http.d.ts +43 -0
- package/dist/foundation-graph-http.js +243 -0
- package/dist/foundation-graph-write.d.ts +90 -0
- package/dist/foundation-graph-write.js +447 -0
- package/dist/foundation-public-provider.d.ts +140 -0
- package/dist/foundation-public-provider.js +192 -0
- package/dist/graph-corrections.d.ts +157 -0
- package/dist/graph-corrections.js +620 -0
- package/dist/graph-foundation-reader.d.ts +60 -0
- package/dist/graph-foundation-reader.js +494 -0
- package/dist/graph-retrieval.d.ts +6 -1
- package/dist/graph-revision-reader.d.ts +80 -0
- package/dist/graph-revision-reader.js +246 -0
- package/dist/graph-web-http.d.ts +26 -0
- package/dist/graph-web-http.js +215 -0
- package/dist/graph-web.d.ts +364 -0
- package/dist/graph-web.js +639 -0
- package/dist/judgment-problem-snapshot.d.ts +30 -2
- package/dist/judgment-problem-snapshot.js +302 -6
- package/dist/judgment-value-proof-review.d.ts +166 -0
- package/dist/judgment-value-proof-review.js +437 -0
- package/dist/judgment-value-proof.d.ts +28 -1
- package/dist/judgment-value-proof.js +53 -8
- package/dist/knowledge-continuation.d.ts +127 -0
- package/dist/knowledge-continuation.js +446 -0
- package/dist/knowledge-lookup.d.ts +178 -0
- package/dist/knowledge-lookup.js +758 -0
- package/dist/local-web-fetch-bridge.d.ts +5 -0
- package/dist/local-web-fetch-bridge.js +37 -0
- package/dist/local-web-host.d.ts +109 -0
- package/dist/local-web-host.js +688 -0
- package/dist/local-web-security.d.ts +25 -0
- package/dist/local-web-security.js +95 -0
- package/dist/organization-graph.d.ts +15 -0
- package/dist/organization-graph.js +24 -4
- package/dist/personal-knowledge.d.ts +12 -0
- package/dist/personal-knowledge.js +29 -0
- package/dist/personal-memory-handover.d.ts +108 -0
- package/dist/personal-memory-handover.js +369 -0
- package/dist/philosophy-revision-reader.d.ts +85 -0
- package/dist/philosophy-revision-reader.js +241 -0
- package/dist/portable-graph.d.ts +24 -0
- package/dist/portable-graph.js +77 -0
- package/dist/server.d.ts +7 -2
- package/dist/server.js +9 -6
- package/dist/value-proof-review-http.d.ts +25 -0
- package/dist/value-proof-review-http.js +185 -0
- package/dist/world-model.d.ts +21 -3
- package/dist/world-model.js +94 -12
- package/package.json +94 -3
- package/ui/brainbase-tokens.css +56 -0
- package/ui/graph-own-share.js +367 -0
- package/ui/graph-projects-view.css +23 -0
- package/ui/graph-projects-view.js +730 -0
- package/ui/graph-registry-view.css +101 -0
- package/ui/graph-registry-view.js +804 -0
- package/ui/graph-view-shared.css +522 -0
- package/ui/graph-view-shared.js +1227 -0
- package/ui/judgment-view.css +119 -45
- package/ui/local-web-shell.css +396 -0
- package/ui/local-web-shell.js +378 -0
- package/ui/objective-editor-http-port.js +154 -0
- package/ui/objective-editor.css +180 -74
- package/ui/objective-editor.js +490 -43
- package/ui/outcome-knowledge.css +50 -14
- package/ui/outcome-knowledge.js +15 -1
- package/ui/outcome-mana.css +91 -14
- package/ui/outcome-mana.js +958 -61
- package/ui/value-proof-review.css +504 -0
- package/ui/value-proof-review.js +1104 -0
- package/ui/workspace-kit.css +485 -0
- package/ui/workspace-kit.js +229 -0
- package/ui/world-model-view.css +188 -0
- package/ui/world-model-view.js +486 -0
|
@@ -53,6 +53,35 @@
|
|
|
53
53
|
{ "type": "boolean" },
|
|
54
54
|
{ "const": "unconfirmed" }
|
|
55
55
|
]
|
|
56
|
+
},
|
|
57
|
+
"judgment_kind": {
|
|
58
|
+
"oneOf": [
|
|
59
|
+
{ "type": "null" },
|
|
60
|
+
{
|
|
61
|
+
"type": "object",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"required": ["key", "label"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"key": { "type": "string", "pattern": "^[a-z0-9_]{1,64}$" },
|
|
66
|
+
"label": { "type": "string", "minLength": 1 }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"inheritance": {
|
|
72
|
+
"oneOf": [
|
|
73
|
+
{ "type": "null" },
|
|
74
|
+
{
|
|
75
|
+
"type": "object",
|
|
76
|
+
"additionalProperties": false,
|
|
77
|
+
"required": ["sources", "same_conditions", "rechecked_conditions"],
|
|
78
|
+
"properties": {
|
|
79
|
+
"sources": { "type": "array", "items": { "$ref": "#/$defs/inheritance_source" } },
|
|
80
|
+
"same_conditions": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
81
|
+
"rechecked_conditions": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
56
85
|
}
|
|
57
86
|
}
|
|
58
87
|
},
|
|
@@ -123,7 +152,20 @@
|
|
|
123
152
|
"required": ["entity_id", "application"],
|
|
124
153
|
"properties": {
|
|
125
154
|
"entity_id": { "type": "string", "minLength": 1 },
|
|
126
|
-
"application": { "type": "string", "minLength": 1 }
|
|
155
|
+
"application": { "type": "string", "minLength": 1 },
|
|
156
|
+
"layer": { "enum": ["philosophy", "objective", "world_model", "method", "constraint", "other"] },
|
|
157
|
+
"version": { "type": ["string", "null"] }
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"inheritance_source": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"additionalProperties": false,
|
|
163
|
+
"required": ["kind", "ref", "version", "label"],
|
|
164
|
+
"properties": {
|
|
165
|
+
"kind": { "enum": ["judgment", "method", "decision", "other"] },
|
|
166
|
+
"ref": { "type": "string", "minLength": 1 },
|
|
167
|
+
"version": { "type": ["string", "null"] },
|
|
168
|
+
"label": { "type": "string", "minLength": 1 }
|
|
127
169
|
}
|
|
128
170
|
},
|
|
129
171
|
"artifact_ref": {
|
|
@@ -55,13 +55,16 @@ function failValidation(fieldErrors) {
|
|
|
55
55
|
throw new CanonicalTaskError('validation_failed', 'Canonical Task request is invalid', 422, { fieldErrors }, { fieldErrors });
|
|
56
56
|
}
|
|
57
57
|
function normalizeString(value, field, options = {}) {
|
|
58
|
-
const
|
|
58
|
+
const text = value == null ? '' : String(value).normalize('NFKC');
|
|
59
|
+
// Multi-line fields keep line breaks and tabs; CRLF/CR are stored as LF.
|
|
60
|
+
const normalized = (options.multiline ? text.replace(/\r\n?/gu, '\n') : text).trim();
|
|
59
61
|
if (!normalized) {
|
|
60
62
|
if (options.required)
|
|
61
63
|
fail('validation_error', `${field} is required`, 400, { field });
|
|
62
64
|
return null;
|
|
63
65
|
}
|
|
64
|
-
|
|
66
|
+
const controlCharacters = options.multiline ? /[\u0000-\u0008\u000b-\u001f\u007f]/u : /[\u0000-\u001f\u007f]/u;
|
|
67
|
+
if (controlCharacters.test(normalized)) {
|
|
65
68
|
fail('validation_error', `${field} contains control characters`, 400, { field });
|
|
66
69
|
}
|
|
67
70
|
if (options.max && normalized.length > options.max) {
|
|
@@ -163,6 +166,14 @@ function projectTaskResult(result) {
|
|
|
163
166
|
}
|
|
164
167
|
return result;
|
|
165
168
|
}
|
|
169
|
+
// Repositories report warnings as `{ code, message }` objects or legacy strings.
|
|
170
|
+
// The page contract is string[], so keep the code rather than "[object Object]".
|
|
171
|
+
function warningCode(warning) {
|
|
172
|
+
if (typeof warning === 'string')
|
|
173
|
+
return warning;
|
|
174
|
+
const code = warning?.code;
|
|
175
|
+
return typeof code === 'string' && code ? code : JSON.stringify(warning);
|
|
176
|
+
}
|
|
166
177
|
function normalizePage(page, normalize) {
|
|
167
178
|
const items = Array.isArray(page.items) ? page.items.map(normalize) : [];
|
|
168
179
|
// An explicit null means that the backend did not request or cannot provide
|
|
@@ -185,7 +196,7 @@ function normalizePage(page, normalize) {
|
|
|
185
196
|
count_status: countStatus,
|
|
186
197
|
next_cursor: nextCursor,
|
|
187
198
|
read_status: readStatus,
|
|
188
|
-
warnings: items.flatMap((task) => Array.isArray(task.normalization_warnings) ? task.normalization_warnings.map(
|
|
199
|
+
warnings: items.flatMap((task) => Array.isArray(task.normalization_warnings) ? task.normalization_warnings.map(warningCode) : []),
|
|
189
200
|
};
|
|
190
201
|
}
|
|
191
202
|
export class CanonicalTaskService {
|
|
@@ -598,7 +609,7 @@ export class CanonicalTaskService {
|
|
|
598
609
|
fail('validation_error', 'priority is invalid', 400, { field: 'priority' });
|
|
599
610
|
return {
|
|
600
611
|
title,
|
|
601
|
-
description: normalizeString(input.description, 'description', { max: 10000 }),
|
|
612
|
+
description: normalizeString(input.description, 'description', { max: 10000, multiline: true }),
|
|
602
613
|
priority,
|
|
603
614
|
assignee_person_id: normalizeString(input.assignee_person_id, 'assignee_person_id', { max: 200 }),
|
|
604
615
|
due_at: normalizeIsoDate(input.due_at, 'due_at'),
|
|
@@ -618,7 +629,7 @@ export class CanonicalTaskService {
|
|
|
618
629
|
if ('title' in input)
|
|
619
630
|
patch.title = normalizeString(input.title, 'title', { required: true, max: MAX_TITLE_LENGTH });
|
|
620
631
|
if ('description' in input)
|
|
621
|
-
patch.description = normalizeString(input.description, 'description', { max: 10000 });
|
|
632
|
+
patch.description = normalizeString(input.description, 'description', { max: 10000, multiline: true });
|
|
622
633
|
if ('priority' in input) {
|
|
623
634
|
const priority = input.priority == null || input.priority === '' ? null : String(input.priority);
|
|
624
635
|
if (priority != null && !isCanonicalTaskPriority(priority))
|
package/dist/cli.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ interface CliIo {
|
|
|
7
7
|
stderr?: {
|
|
8
8
|
write(chunk: string): unknown;
|
|
9
9
|
};
|
|
10
|
+
/** Stops a long-running command such as web:serve (besides SIGINT/SIGTERM). */
|
|
11
|
+
signal?: AbortSignal;
|
|
10
12
|
}
|
|
11
13
|
export declare function runCli(argv?: string[], io?: CliIo): Promise<number>;
|
|
12
14
|
export declare function isCliEntrypoint(moduleUrl: string, invokedPath: string | undefined): boolean;
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createPortableGraph, portableGraphDigest } from './portable-graph.js';
|
|
3
3
|
import { createOrganizationGraphConfig, createOrganizationGraphClient } from './organization-graph.js';
|
|
4
|
+
import { listLocalMemories, memoryListJson, registerSelectedMemories, registrationPreviewJson, renderMemoryList, renderRegistrationPreview, renderRegistrationResult, selectLocalMemories } from './personal-memory-handover.js';
|
|
4
5
|
import { constants, realpathSync } from 'node:fs';
|
|
5
6
|
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
6
7
|
import { delimiter, dirname, isAbsolute, join } from 'node:path';
|
|
@@ -19,6 +20,8 @@ import { buildProjectRegistrationPlan, parseProjectSource, parseProjectStakehold
|
|
|
19
20
|
import { renderGuidedFirstRun } from './guided-onboarding.js';
|
|
20
21
|
import { blockedJudgmentOutput, processJudgmentHook } from './judgment-host.js';
|
|
21
22
|
import { applyCanonicalWrites, buildCanonicalEdge } from './canonical-edge-builder.js';
|
|
23
|
+
import { defaultJudgmentJournalRoot } from './judgment-value-proof-review.js';
|
|
24
|
+
import { createLocalWebHost, LOCAL_WEB_DEFAULT_PORT } from './local-web-host.js';
|
|
22
25
|
export async function runCli(argv = process.argv.slice(2), io = process) {
|
|
23
26
|
const parsed = parseArgs(argv);
|
|
24
27
|
try {
|
|
@@ -41,6 +44,27 @@ export async function runCli(argv = process.argv.slice(2), io = process) {
|
|
|
41
44
|
sourceFilesUnchanged: true, graphId: config.graphId }) + '\n');
|
|
42
45
|
return 0;
|
|
43
46
|
}
|
|
47
|
+
case 'graph:bundles': {
|
|
48
|
+
const config = createOrganizationGraphConfig();
|
|
49
|
+
if (!config)
|
|
50
|
+
throw new Error('graph:bundles requires all BRAINBASE_ORGANIZATION_* settings');
|
|
51
|
+
const format = first(parsed, 'format') ?? 'text';
|
|
52
|
+
if (format !== 'text' && format !== 'json')
|
|
53
|
+
throw new Error('graph:bundles --format must be text or json');
|
|
54
|
+
const list = await createOrganizationGraphClient(config).listPortableGraphs();
|
|
55
|
+
if (format === 'json')
|
|
56
|
+
write(io, `${JSON.stringify(list, null, 2)}\n`);
|
|
57
|
+
else if (list.bundles.length === 0)
|
|
58
|
+
write(io, 'No Graph bundles have been uploaded to this organization yet.\n');
|
|
59
|
+
else
|
|
60
|
+
write(io, list.bundles.map((entry) => `${entry.createdAt} ${entry.projectCode} ${entry.graphId} ${entry.digest}\n`).join('')
|
|
61
|
+
+ (list.truncated ? `Only the newest ${list.bundles.length} bundle(s) are shown.\n` : ''));
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
case 'memory:list':
|
|
65
|
+
return await memoryList(parsed, io);
|
|
66
|
+
case 'memory:register':
|
|
67
|
+
return await memoryRegister(parsed, io);
|
|
44
68
|
case 'onboard:init':
|
|
45
69
|
return await onboardInit(parsed, io);
|
|
46
70
|
case 'onboard:seed':
|
|
@@ -86,6 +110,10 @@ export async function runCli(argv = process.argv.slice(2), io = process) {
|
|
|
86
110
|
return await judgmentInstall(parsed, io);
|
|
87
111
|
case 'doctor':
|
|
88
112
|
return await doctor(parsed, io);
|
|
113
|
+
case 'web:serve':
|
|
114
|
+
case 'review:serve':
|
|
115
|
+
// review:serve is kept as an alias of the same local Web host.
|
|
116
|
+
return await webServe(parsed, io, parsed.command);
|
|
89
117
|
case 'mcp':
|
|
90
118
|
await import('./index.js');
|
|
91
119
|
return 0;
|
|
@@ -175,6 +203,36 @@ async function readHookStdin(input) {
|
|
|
175
203
|
text += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8');
|
|
176
204
|
return text;
|
|
177
205
|
}
|
|
206
|
+
function textOrJsonFormat(parsed, command) {
|
|
207
|
+
const format = first(parsed, 'format') ?? 'text';
|
|
208
|
+
if (format !== 'text' && format !== 'json')
|
|
209
|
+
throw new Error(`${command} --format must be text or json`);
|
|
210
|
+
return format;
|
|
211
|
+
}
|
|
212
|
+
async function memoryList(parsed, io) {
|
|
213
|
+
const format = textOrJsonFormat(parsed, 'memory:list');
|
|
214
|
+
const memories = await listLocalMemories(first(parsed, 'dir'));
|
|
215
|
+
write(io, format === 'json' ? `${JSON.stringify(memoryListJson(memories), null, 2)}\n` : renderMemoryList(memories));
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
async function memoryRegister(parsed, io) {
|
|
219
|
+
const format = textOrJsonFormat(parsed, 'memory:register');
|
|
220
|
+
const ids = parsed.values.get('id') ?? [];
|
|
221
|
+
if (ids.length === 0)
|
|
222
|
+
throw new Error('memory:register requires at least one --id <id> (see brainbase memory:list)');
|
|
223
|
+
const dataDir = first(parsed, 'dir');
|
|
224
|
+
if (!parsed.flags.has('write')) {
|
|
225
|
+
const selected = selectLocalMemories(await listLocalMemories(dataDir), ids);
|
|
226
|
+
write(io, format === 'json' ? `${JSON.stringify(registrationPreviewJson(selected), null, 2)}\n` : renderRegistrationPreview(selected));
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
const config = createOrganizationGraphConfig();
|
|
230
|
+
if (!config)
|
|
231
|
+
throw new Error('memory:register --write requires all BRAINBASE_ORGANIZATION_* settings; nothing was sent');
|
|
232
|
+
const result = await registerSelectedMemories({ dataDir, ids, organization: { url: config.url, token: config.token } });
|
|
233
|
+
write(io, format === 'json' ? `${JSON.stringify({ status: 'registered', ...result }, null, 2)}\n` : renderRegistrationResult(result));
|
|
234
|
+
return 0;
|
|
235
|
+
}
|
|
178
236
|
async function onboardInit(parsed, io) {
|
|
179
237
|
const dataDir = resolveDataDir(first(parsed, 'dir'));
|
|
180
238
|
await initializePersonalOs(dataDir);
|
|
@@ -976,6 +1034,50 @@ async function doctor(parsed, io) {
|
|
|
976
1034
|
function graphDiagnosisExitCode(status) {
|
|
977
1035
|
return status === 'invalid' || status === 'unavailable' || status === 'migration_required' ? 1 : 0;
|
|
978
1036
|
}
|
|
1037
|
+
async function webServe(parsed, io, command) {
|
|
1038
|
+
const dataDir = resolveDataDir(first(parsed, 'dir'));
|
|
1039
|
+
const journalRoot = first(parsed, 'journal')
|
|
1040
|
+
?? process.env.BRAINBASE_JUDGMENT_JOURNAL_DIR
|
|
1041
|
+
?? defaultJudgmentJournalRoot(dataDir);
|
|
1042
|
+
const port = Number(first(parsed, 'port') ?? String(LOCAL_WEB_DEFAULT_PORT));
|
|
1043
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
1044
|
+
throw new Error(`${command} requires --port to be an integer between 0 and 65535`);
|
|
1045
|
+
}
|
|
1046
|
+
const { server } = createLocalWebHost({ dataDir, journalRoot });
|
|
1047
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
1048
|
+
server.once('error', rejectListen);
|
|
1049
|
+
server.listen(port, '127.0.0.1', () => resolveListen());
|
|
1050
|
+
});
|
|
1051
|
+
const address = server.address();
|
|
1052
|
+
const actualPort = typeof address === 'object' && address ? address.port : port;
|
|
1053
|
+
const origin = `http://127.0.0.1:${actualPort}`;
|
|
1054
|
+
write(io, [
|
|
1055
|
+
`Brainbase: ${origin}/`,
|
|
1056
|
+
`- 今日(判断の見返し): ${origin}/#today`,
|
|
1057
|
+
`- 目的と現状: ${origin}/#objectives`,
|
|
1058
|
+
`- プロジェクトと関係者: ${origin}/#projects`,
|
|
1059
|
+
`- 情報と関係: ${origin}/#graph`,
|
|
1060
|
+
`データ: ${dataDir}`,
|
|
1061
|
+
`判断journal: ${journalRoot}`,
|
|
1062
|
+
'終了: Ctrl+C',
|
|
1063
|
+
''
|
|
1064
|
+
].join('\n'));
|
|
1065
|
+
await new Promise((resolveClose) => {
|
|
1066
|
+
const stop = () => {
|
|
1067
|
+
process.off('SIGINT', stop);
|
|
1068
|
+
process.off('SIGTERM', stop);
|
|
1069
|
+
io.signal?.removeEventListener('abort', stop);
|
|
1070
|
+
server.close(() => resolveClose());
|
|
1071
|
+
};
|
|
1072
|
+
process.once('SIGINT', stop);
|
|
1073
|
+
process.once('SIGTERM', stop);
|
|
1074
|
+
if (io.signal?.aborted)
|
|
1075
|
+
stop();
|
|
1076
|
+
else
|
|
1077
|
+
io.signal?.addEventListener('abort', stop, { once: true });
|
|
1078
|
+
});
|
|
1079
|
+
return 0;
|
|
1080
|
+
}
|
|
979
1081
|
function parseArgs(argv) {
|
|
980
1082
|
const [firstToken, ...remaining] = argv;
|
|
981
1083
|
const command = firstToken?.startsWith('--') ? undefined : firstToken;
|
|
@@ -1104,10 +1206,15 @@ function usage() {
|
|
|
1104
1206
|
brainbase ontology:show
|
|
1105
1207
|
brainbase ontology:audit [--dir path] [--ontology-version 0.0.0|1.0.0|2.0.0]
|
|
1106
1208
|
brainbase graph:upgrade [--dir path]
|
|
1209
|
+
brainbase graph:bundles [--format text|json]
|
|
1210
|
+
brainbase memory:list [--dir path] [--format text|json]
|
|
1211
|
+
brainbase memory:register --id id [--id id...] [--dir path] [--write] [--format text|json]
|
|
1107
1212
|
brainbase ontology:migrate [--dir path] [--write --expected-input-digest digest]
|
|
1108
1213
|
brainbase judgment:install --target codex [--autonomy-mode off|canary|on] [--autonomy-project code] [--dry-run] [--output path]
|
|
1109
1214
|
brainbase judgment:hook [--autonomy-mode off|canary|on] [--autonomy-project code]
|
|
1110
1215
|
brainbase doctor [--dir path] [--judgment-hooks path]
|
|
1216
|
+
brainbase web:serve [--dir path] [--journal path] [--port n]
|
|
1217
|
+
brainbase review:serve [--dir path] [--journal path] [--port n] (web:serveの別名)
|
|
1111
1218
|
`;
|
|
1112
1219
|
}
|
|
1113
1220
|
function shellArg(value) {
|
|
@@ -13,6 +13,11 @@ export interface ObjectiveReadiness {
|
|
|
13
13
|
/** Definition and referenced Variable issues; observations are not inferred. */
|
|
14
14
|
issues: readonly ObjectiveReadinessIssue[];
|
|
15
15
|
}
|
|
16
|
+
/** The canonical read surface needed to check an Objective and its criteria. */
|
|
17
|
+
export interface ObjectiveReadinessReader {
|
|
18
|
+
read(reference: FoundationRevision, context: FoundationStoreContext): Promise<FoundationCatalogRecord | null>;
|
|
19
|
+
readLatest?(type: 'objective' | 'variable', id: string, context: FoundationStoreContext): Promise<FoundationCatalogRecord | null>;
|
|
20
|
+
}
|
|
16
21
|
export type StoryObjectiveLinkKind = 'contribution' | 'execution_dependency' | 'time_condition';
|
|
17
22
|
export interface StoryObjectiveLinkInput {
|
|
18
23
|
storyId: string;
|
|
@@ -49,4 +54,10 @@ export declare class CompanyOsObjectives {
|
|
|
49
54
|
linkObjectiveStory(input: StoryObjectiveLinkInput, context: FoundationStoreContext): Promise<void>;
|
|
50
55
|
private readTyped;
|
|
51
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Check an Objective and every Variable revision named by its criteria.
|
|
59
|
+
* Both the Objective service and judgment snapshot providers call this
|
|
60
|
+
* function so readiness cannot vary by caller.
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkObjectiveReadiness(reader: ObjectiveReadinessReader, id: string, context: FoundationStoreContext, revision?: string): Promise<ObjectiveReadiness>;
|
|
52
63
|
export declare function createCompanyOsObjectives(store: FoundationRevisionStore): CompanyOsObjectives;
|
|
@@ -27,53 +27,7 @@ export class CompanyOsObjectives {
|
|
|
27
27
|
}, context);
|
|
28
28
|
}
|
|
29
29
|
async checkObjectiveReadiness(id, context, revision) {
|
|
30
|
-
|
|
31
|
-
if (!record) {
|
|
32
|
-
return {
|
|
33
|
-
ready: false,
|
|
34
|
-
issues: [{ code: 'NOT_FOUND', path: 'id', message: `Objective ${id} was not found.` }]
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
const objective = record.definition;
|
|
38
|
-
const issues = validationIssues(objective, 'judgment');
|
|
39
|
-
const criteria = Array.isArray(objective.criteria) ? objective.criteria : [];
|
|
40
|
-
for (const [index, criterion] of criteria.entries()) {
|
|
41
|
-
if (!criterion || typeof criterion !== 'object' || !criterion.variableRef)
|
|
42
|
-
continue;
|
|
43
|
-
if (!isVariableRevisionReference(criterion.variableRef)) {
|
|
44
|
-
issues.push({
|
|
45
|
-
code: 'INVALID_VARIABLE_REFERENCE',
|
|
46
|
-
path: `criteria[${index}].variableRef`,
|
|
47
|
-
message: 'Criterion must reference a Variable with a positive revision.'
|
|
48
|
-
});
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
const variable = await this.store.read(criterion.variableRef, context);
|
|
52
|
-
if (!variable) {
|
|
53
|
-
issues.push({
|
|
54
|
-
code: 'MISSING_VARIABLE_REVISION',
|
|
55
|
-
path: `criteria[${index}].variableRef`,
|
|
56
|
-
message: `Variable ${criterion.variableRef.id}@${criterion.variableRef.revision} was not found.`
|
|
57
|
-
});
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (variable.definition.type !== 'variable') {
|
|
61
|
-
issues.push({
|
|
62
|
-
code: 'INVALID_VARIABLE_REFERENCE',
|
|
63
|
-
path: `criteria[${index}].variableRef`,
|
|
64
|
-
message: `Criterion ${criterion.variableRef.id}@${criterion.variableRef.revision} is not a Variable.`
|
|
65
|
-
});
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
for (const issue of validationIssues(variable.definition, 'judgment')) {
|
|
69
|
-
issues.push({
|
|
70
|
-
...issue,
|
|
71
|
-
path: `criteria[${index}].variableRef.${issue.path}`
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
issues.push(...criterionCompatibilityIssues(criterion, variable.definition, index));
|
|
75
|
-
}
|
|
76
|
-
return { ready: issues.length === 0, issues };
|
|
30
|
+
return checkObjectiveReadiness(this.store, id, context, revision);
|
|
77
31
|
}
|
|
78
32
|
createVariable(definition, context) {
|
|
79
33
|
assertType(definition, 'variable');
|
|
@@ -130,6 +84,69 @@ export class CompanyOsObjectives {
|
|
|
130
84
|
return this.store.readLatest(type, id, context);
|
|
131
85
|
}
|
|
132
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Check an Objective and every Variable revision named by its criteria.
|
|
89
|
+
* Both the Objective service and judgment snapshot providers call this
|
|
90
|
+
* function so readiness cannot vary by caller.
|
|
91
|
+
*/
|
|
92
|
+
export async function checkObjectiveReadiness(reader, id, context, revision) {
|
|
93
|
+
const record = revision === undefined
|
|
94
|
+
? reader.readLatest === undefined ? null : await reader.readLatest('objective', id, context)
|
|
95
|
+
: await reader.read({ id, type: 'objective', revision }, context);
|
|
96
|
+
if (!record) {
|
|
97
|
+
return {
|
|
98
|
+
ready: false,
|
|
99
|
+
issues: [{ code: 'NOT_FOUND', path: 'id', message: `Objective ${id} was not found.` }]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (record.definition.type !== 'objective') {
|
|
103
|
+
return {
|
|
104
|
+
ready: false,
|
|
105
|
+
issues: [{ code: 'INVALID_OBJECTIVE_REFERENCE', path: 'type', message: `Foundation ${id} is not an Objective.` }]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const objective = record.definition;
|
|
109
|
+
const issues = validationIssues(objective, 'judgment');
|
|
110
|
+
const criteria = Array.isArray(objective.criteria) ? objective.criteria : [];
|
|
111
|
+
for (const [index, criterion] of criteria.entries()) {
|
|
112
|
+
if (!criterion || typeof criterion !== 'object' || !criterion.variableRef)
|
|
113
|
+
continue;
|
|
114
|
+
if (!isVariableRevisionReference(criterion.variableRef)) {
|
|
115
|
+
issues.push({
|
|
116
|
+
code: 'INVALID_VARIABLE_REFERENCE',
|
|
117
|
+
path: `criteria[${index}].variableRef`,
|
|
118
|
+
message: 'Criterion must reference a Variable with a positive revision.'
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const variable = await reader.read(criterion.variableRef, context);
|
|
123
|
+
if (!variable) {
|
|
124
|
+
issues.push({
|
|
125
|
+
code: 'MISSING_VARIABLE_REVISION',
|
|
126
|
+
path: `criteria[${index}].variableRef`,
|
|
127
|
+
message: `Variable ${criterion.variableRef.id}@${criterion.variableRef.revision} was not found.`
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (variable.definition.type !== 'variable') {
|
|
132
|
+
issues.push({
|
|
133
|
+
code: 'INVALID_VARIABLE_REFERENCE',
|
|
134
|
+
path: `criteria[${index}].variableRef`,
|
|
135
|
+
message: `Criterion ${criterion.variableRef.id}@${criterion.variableRef.revision} is not a Variable.`
|
|
136
|
+
});
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
for (const issue of validationIssues(variable.definition, 'judgment')) {
|
|
140
|
+
issues.push({
|
|
141
|
+
...issue,
|
|
142
|
+
path: `criteria[${index}].variableRef.${issue.path}`
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
issues.push(...criterionCompatibilityIssues(criterion, variable.definition, index));
|
|
146
|
+
issues.push(...criterionPeriodIssues(objective, variable.definition, index));
|
|
147
|
+
}
|
|
148
|
+
return { ready: issues.length === 0, issues };
|
|
149
|
+
}
|
|
133
150
|
export function createCompanyOsObjectives(store) {
|
|
134
151
|
return new CompanyOsObjectives(store);
|
|
135
152
|
}
|
|
@@ -195,3 +212,23 @@ function criterionCompatibilityIssues(criterion, variable, index) {
|
|
|
195
212
|
}
|
|
196
213
|
return [];
|
|
197
214
|
}
|
|
215
|
+
function criterionPeriodIssues(objective, variable, index) {
|
|
216
|
+
const objectiveFrom = Date.parse(objective.evaluationPeriod?.from ?? '');
|
|
217
|
+
const objectiveUntil = Date.parse(objective.evaluationPeriod?.until ?? '');
|
|
218
|
+
const variableFrom = Date.parse(variable.scope?.validFrom ?? '');
|
|
219
|
+
const variableUntil = variable.scope?.validUntil === undefined
|
|
220
|
+
? Number.POSITIVE_INFINITY
|
|
221
|
+
: Date.parse(variable.scope.validUntil);
|
|
222
|
+
if (!Number.isFinite(objectiveFrom) || !Number.isFinite(objectiveUntil)
|
|
223
|
+
|| !Number.isFinite(variableFrom)
|
|
224
|
+
|| (variable.scope?.validUntil !== undefined && !Number.isFinite(variableUntil))) {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
if (variableFrom <= objectiveFrom && variableUntil >= objectiveUntil)
|
|
228
|
+
return [];
|
|
229
|
+
return [{
|
|
230
|
+
code: 'CRITERION_PERIOD_MISMATCH',
|
|
231
|
+
path: `criteria[${index}].variableRef`,
|
|
232
|
+
message: `Variable ${variable.id}@${variable.revision} does not cover the Objective evaluation period.`
|
|
233
|
+
}];
|
|
234
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
export type FoundationAuthenticatedProjectContext = {
|
|
3
|
+
token: string;
|
|
4
|
+
scope: string[];
|
|
5
|
+
};
|
|
6
|
+
export type FoundationAuthenticatedToolResult = {
|
|
7
|
+
status: 'ok' | 'error' | 'unavailable';
|
|
8
|
+
scope: {
|
|
9
|
+
project_codes: string[];
|
|
10
|
+
};
|
|
11
|
+
data?: unknown;
|
|
12
|
+
error?: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
http_status?: number;
|
|
16
|
+
details?: unknown;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export type FoundationAuthenticatedRequest = {
|
|
20
|
+
path: string;
|
|
21
|
+
method: string;
|
|
22
|
+
body?: unknown;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
export type FoundationAuthenticatedJsonResponse = {
|
|
26
|
+
ok: true;
|
|
27
|
+
response: Response;
|
|
28
|
+
payload: unknown;
|
|
29
|
+
payloadParsed: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
ok: false;
|
|
32
|
+
result: FoundationAuthenticatedToolResult;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Narrow host port for authentication and authenticated Graph requests.
|
|
36
|
+
* The OSS adapter owns request validation, CSRF/session binding, and response
|
|
37
|
+
* validation; a connected Host supplies its canonical auth implementation.
|
|
38
|
+
*/
|
|
39
|
+
export type FoundationAuthenticatedToolPort = {
|
|
40
|
+
authenticateProject(args: Record<string, unknown>, options?: {
|
|
41
|
+
requireProject?: boolean;
|
|
42
|
+
}): Promise<FoundationAuthenticatedProjectContext | FoundationAuthenticatedToolResult>;
|
|
43
|
+
fetchAuthenticatedJson(context: FoundationAuthenticatedProjectContext, request: FoundationAuthenticatedRequest): Promise<FoundationAuthenticatedJsonResponse>;
|
|
44
|
+
toolError(status: 'error' | 'unavailable', code: string, message: string, scope: string[], httpStatus?: number, details?: unknown): FoundationAuthenticatedToolResult;
|
|
45
|
+
};
|
|
46
|
+
export type FoundationAuthenticatedToolDependencies = {
|
|
47
|
+
apiUrl: string;
|
|
48
|
+
fetch?: typeof globalThis.fetch;
|
|
49
|
+
auth: FoundationAuthenticatedToolPort;
|
|
50
|
+
};
|
|
51
|
+
type FoundationPublicToolName = 'foundation_describe' | 'foundation_read' | 'foundation_validate_reference' | 'foundation_validate_problem';
|
|
52
|
+
export type FoundationPublicToolDependencies = FoundationAuthenticatedToolDependencies;
|
|
53
|
+
export declare const foundationPublicTools: Tool[];
|
|
54
|
+
export declare function isFoundationPublicToolName(name: string): name is FoundationPublicToolName;
|
|
55
|
+
export declare function handleFoundationPublicToolCall(name: string, args: Record<string, unknown>, dependencies: FoundationPublicToolDependencies): Promise<FoundationAuthenticatedToolResult | null>;
|
|
56
|
+
export {};
|