@shawnowen/comet-mcp 2.3.0 → 2.4.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.
- package/README.md +86 -19
- package/dist/alert-dispatcher.d.ts +23 -0
- package/dist/alert-dispatcher.js +101 -0
- package/dist/bound-session.d.ts +23 -0
- package/dist/bound-session.js +119 -0
- package/dist/bridge-config.d.ts +6 -0
- package/dist/bridge-config.js +78 -0
- package/dist/cdp-client.d.ts +40 -4
- package/dist/cdp-client.js +502 -155
- package/dist/comet-ai.d.ts +15 -0
- package/dist/comet-ai.js +114 -38
- package/dist/delegate-binding.d.ts +19 -0
- package/dist/delegate-binding.js +73 -0
- package/dist/discovery/capability-entry.d.ts +215 -0
- package/dist/discovery/capability-entry.js +13 -0
- package/dist/discovery/description-template.d.ts +40 -0
- package/dist/discovery/description-template.js +61 -0
- package/dist/discovery/golden-queries.fixture.d.ts +22 -0
- package/dist/discovery/golden-queries.fixture.js +137 -0
- package/dist/discovery/mcp-source.d.ts +38 -0
- package/dist/discovery/mcp-source.js +70 -0
- package/dist/discovery/metadata-completeness.d.ts +48 -0
- package/dist/discovery/metadata-completeness.js +83 -0
- package/dist/discovery/registry.d.ts +35 -0
- package/dist/discovery/registry.js +35 -0
- package/dist/discovery/safety.d.ts +44 -0
- package/dist/discovery/safety.js +59 -0
- package/dist/discovery/schema-validator.d.ts +36 -0
- package/dist/discovery/schema-validator.js +257 -0
- package/dist/discovery/source-error.d.ts +47 -0
- package/dist/discovery/source-error.js +95 -0
- package/dist/discovery/tool-meta.d.ts +41 -0
- package/dist/discovery/tool-meta.js +229 -0
- package/dist/discovery/virtual-tools.d.ts +20 -0
- package/dist/discovery/virtual-tools.js +69 -0
- package/dist/http-server.js +2067 -47
- package/dist/index.js +3163 -710
- package/dist/observer.d.ts +47 -0
- package/dist/observer.js +516 -0
- package/dist/session-registry.d.ts +57 -0
- package/dist/session-registry.js +500 -0
- package/dist/sidecar-artifacts.d.ts +49 -0
- package/dist/sidecar-artifacts.js +146 -0
- package/dist/snapshot-capture.d.ts +3 -0
- package/dist/snapshot-capture.js +91 -0
- package/dist/tab-group-archive.js +3 -1
- package/dist/tab-groups.d.ts +7 -0
- package/dist/tab-groups.js +21 -3
- package/dist/task-thread-aggregator.d.ts +34 -0
- package/dist/task-thread-aggregator.js +480 -0
- package/dist/task-thread-canonical.d.ts +142 -0
- package/dist/task-thread-canonical.js +116 -0
- package/dist/types.d.ts +237 -0
- package/dist/window-bindings.d.ts +112 -0
- package/dist/window-bindings.js +476 -0
- package/extension/background.js +1556 -300
- package/extension/icons/icon.svg +9 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +19 -4
- package/extension/session-logic.js +2383 -0
- package/extension/session-manager.html +299 -0
- package/extension/sidepanel.css +5323 -528
- package/extension/sidepanel.html +282 -2
- package/extension/sidepanel.js +10075 -951
- package/extension/window-policy.js +162 -0
- package/package.json +10 -7
- package/vendor/lifecycle-mcp-adapter.mjs +103 -0
- package/vendor/lifecycle-metadata.mjs +252 -0
- package/vendor/readiness-report.mjs +742 -0
- package/dist/cdp-client.d.ts.map +0 -1
- package/dist/cdp-client.js.map +0 -1
- package/dist/comet-ai.d.ts.map +0 -1
- package/dist/comet-ai.js.map +0 -1
- package/dist/http-server.d.ts.map +0 -1
- package/dist/http-server.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/tab-group-archive.d.ts.map +0 -1
- package/dist/tab-group-archive.js.map +0 -1
- package/dist/tab-groups.d.ts.map +0 -1
- package/dist/tab-groups.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* safety.ts
|
|
3
|
+
* Safety taxonomy loader for the Comet Discovery Layer (Spec 042, T007).
|
|
4
|
+
*
|
|
5
|
+
* Reads the three valid safety classes (SAFE | SESSION | CAUTION) from
|
|
6
|
+
* docs/TOOL-SAFETY-REFERENCE.md as the canonical authority. Exports a
|
|
7
|
+
* validator that rejects any value outside the taxonomy at runtime.
|
|
8
|
+
*
|
|
9
|
+
* This module deliberately does NOT import the taxonomy from TOOL-SAFETY-REFERENCE.md
|
|
10
|
+
* at file-read time (the doc is prose, not machine-readable JSON). Instead we
|
|
11
|
+
* hard-code the three classes here and note the canonical source in JSDoc so
|
|
12
|
+
* T061 can add a pointer from the doc back to this file.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The three valid safety classes.
|
|
16
|
+
* Canonical authority: docs/TOOL-SAFETY-REFERENCE.md
|
|
17
|
+
*
|
|
18
|
+
* SAFE — Read-only, zero side effects, no active session required.
|
|
19
|
+
* Examples: comet_observe, comet_peek, comet_tab_groups(action=list).
|
|
20
|
+
* SESSION — Requires an active Comet CDP session (comet_connect first).
|
|
21
|
+
* Scoped to the calling agent's own tab group.
|
|
22
|
+
* Examples: comet_screenshot, comet_read_page, comet_navigate.
|
|
23
|
+
* CAUTION — Cross-session side effects possible; can modify shared browser state.
|
|
24
|
+
* Must carry an explicit multi-agent rule reminder in its description.
|
|
25
|
+
* Examples: comet_stop, comet_automate (when interacting across groups).
|
|
26
|
+
*/
|
|
27
|
+
export const VALID_SAFETY_CLASSES = new Set([
|
|
28
|
+
"SAFE",
|
|
29
|
+
"SESSION",
|
|
30
|
+
"CAUTION",
|
|
31
|
+
]);
|
|
32
|
+
/**
|
|
33
|
+
* Assert that `value` is a valid SafetyClass.
|
|
34
|
+
* Throws a descriptive TypeError if not.
|
|
35
|
+
* Used by source readers to reject entries missing or misconfigured safety.
|
|
36
|
+
*
|
|
37
|
+
* @param value - The candidate safety string from a source definition.
|
|
38
|
+
* @param context - Optional label (tool name, file path) for error messages.
|
|
39
|
+
* @returns The validated SafetyClass.
|
|
40
|
+
*/
|
|
41
|
+
export function assertSafety(value, context) {
|
|
42
|
+
if (VALID_SAFETY_CLASSES.has(value)) {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
const ctxStr = context ? ` (context: ${context})` : "";
|
|
46
|
+
throw new TypeError(`Invalid safety class "${value}"${ctxStr}. ` +
|
|
47
|
+
`Valid classes are: ${[...VALID_SAFETY_CLASSES].join(" | ")}. ` +
|
|
48
|
+
`See docs/TOOL-SAFETY-REFERENCE.md for definitions.`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Safe version of assertSafety — returns null instead of throwing.
|
|
52
|
+
* Useful when the caller wants to route errors through SourceError rather than throw.
|
|
53
|
+
*/
|
|
54
|
+
export function parseSafety(value) {
|
|
55
|
+
if (typeof value !== "string")
|
|
56
|
+
return null;
|
|
57
|
+
return VALID_SAFETY_CLASSES.has(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* schema-validator.ts
|
|
3
|
+
* Lightweight JSON / YAML Schema validator for the Comet Discovery Layer (Spec 042, T009).
|
|
4
|
+
*
|
|
5
|
+
* Loads schema files from ./contracts/ and validates arbitrary values against them.
|
|
6
|
+
* Uses a minimal built-in validator so the discovery module has no heavy runtime
|
|
7
|
+
* dependency (ajv is not in the dependency list). Only the subset of JSON Schema
|
|
8
|
+
* draft 2020-12 actually used by the four contracts is implemented.
|
|
9
|
+
*
|
|
10
|
+
* Supported keywords:
|
|
11
|
+
* type, enum, const, pattern, minLength, maxLength, minimum, minItems,
|
|
12
|
+
* required, properties, additionalProperties, oneOf, allOf, if/then/else,
|
|
13
|
+
* $ref (intra-document only), items, format (date, date-time — format:true check only)
|
|
14
|
+
*/
|
|
15
|
+
export interface ValidationResult {
|
|
16
|
+
valid: boolean;
|
|
17
|
+
errors: ValidationError[];
|
|
18
|
+
}
|
|
19
|
+
export interface ValidationError {
|
|
20
|
+
path: string;
|
|
21
|
+
message: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate `value` against the named contract file in ./contracts/.
|
|
25
|
+
* Filename must be one of the four contract files committed at T003.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const result = validateContract("capability-entry.schema.json", entry);
|
|
29
|
+
* if (!result.valid) console.error(result.errors);
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateContract(contractFile: string, value: unknown): ValidationResult;
|
|
32
|
+
/**
|
|
33
|
+
* Throw if validation fails. Formats errors as readable path-messages.
|
|
34
|
+
*/
|
|
35
|
+
export declare function assertValid(contractFile: string, value: unknown, context?: string): void;
|
|
36
|
+
//# sourceMappingURL=schema-validator.d.ts.map
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* schema-validator.ts
|
|
3
|
+
* Lightweight JSON / YAML Schema validator for the Comet Discovery Layer (Spec 042, T009).
|
|
4
|
+
*
|
|
5
|
+
* Loads schema files from ./contracts/ and validates arbitrary values against them.
|
|
6
|
+
* Uses a minimal built-in validator so the discovery module has no heavy runtime
|
|
7
|
+
* dependency (ajv is not in the dependency list). Only the subset of JSON Schema
|
|
8
|
+
* draft 2020-12 actually used by the four contracts is implemented.
|
|
9
|
+
*
|
|
10
|
+
* Supported keywords:
|
|
11
|
+
* type, enum, const, pattern, minLength, maxLength, minimum, minItems,
|
|
12
|
+
* required, properties, additionalProperties, oneOf, allOf, if/then/else,
|
|
13
|
+
* $ref (intra-document only), items, format (date, date-time — format:true check only)
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from "fs";
|
|
16
|
+
import { join, dirname } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { parse as parseYaml } from "yaml";
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
const CONTRACTS_DIR = join(__dirname, "contracts");
|
|
22
|
+
const schemaCache = new Map();
|
|
23
|
+
function loadSchema(filename) {
|
|
24
|
+
if (schemaCache.has(filename))
|
|
25
|
+
return schemaCache.get(filename);
|
|
26
|
+
const filePath = join(CONTRACTS_DIR, filename);
|
|
27
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
28
|
+
const schema = filename.endsWith(".yaml") || filename.endsWith(".yml")
|
|
29
|
+
? parseYaml(raw)
|
|
30
|
+
: JSON.parse(raw);
|
|
31
|
+
schemaCache.set(filename, schema);
|
|
32
|
+
return schema;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate `value` against the named contract file in ./contracts/.
|
|
36
|
+
* Filename must be one of the four contract files committed at T003.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const result = validateContract("capability-entry.schema.json", entry);
|
|
40
|
+
* if (!result.valid) console.error(result.errors);
|
|
41
|
+
*/
|
|
42
|
+
export function validateContract(contractFile, value) {
|
|
43
|
+
const schema = loadSchema(contractFile);
|
|
44
|
+
const errors = [];
|
|
45
|
+
validateNode(schema, value, "$", errors, schema);
|
|
46
|
+
return { valid: errors.length === 0, errors };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Throw if validation fails. Formats errors as readable path-messages.
|
|
50
|
+
*/
|
|
51
|
+
export function assertValid(contractFile, value, context) {
|
|
52
|
+
const result = validateContract(contractFile, value);
|
|
53
|
+
if (!result.valid) {
|
|
54
|
+
const prefix = context ? `${context}: ` : "";
|
|
55
|
+
const lines = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
|
|
56
|
+
throw new Error(`${prefix}Schema validation failed against ${contractFile}:\n${lines}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Internal validator
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
function validateNode(schema, value, path, errors, rootSchema) {
|
|
63
|
+
if (typeof schema !== "object" || schema === null)
|
|
64
|
+
return;
|
|
65
|
+
const s = schema;
|
|
66
|
+
// $ref — intra-document only
|
|
67
|
+
if ("$ref" in s) {
|
|
68
|
+
const ref = s["$ref"];
|
|
69
|
+
const resolved = resolveRef(ref, rootSchema);
|
|
70
|
+
if (resolved)
|
|
71
|
+
validateNode(resolved, value, path, errors, rootSchema);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// type
|
|
75
|
+
if ("type" in s) {
|
|
76
|
+
const t = s["type"];
|
|
77
|
+
if (!checkType(t, value)) {
|
|
78
|
+
errors.push({ path, message: `expected type ${JSON.stringify(t)}, got ${getType(value)}` });
|
|
79
|
+
return; // further checks are meaningless
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// enum
|
|
83
|
+
if ("enum" in s) {
|
|
84
|
+
const vals = s["enum"];
|
|
85
|
+
if (!vals.some((v) => deepEqual(v, value))) {
|
|
86
|
+
errors.push({ path, message: `must be one of ${JSON.stringify(vals)}` });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// const
|
|
90
|
+
if ("const" in s) {
|
|
91
|
+
if (!deepEqual(s["const"], value)) {
|
|
92
|
+
errors.push({ path, message: `must equal ${JSON.stringify(s["const"])}` });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// pattern (string)
|
|
96
|
+
if ("pattern" in s && typeof value === "string") {
|
|
97
|
+
const re = new RegExp(s["pattern"]);
|
|
98
|
+
if (!re.test(value)) {
|
|
99
|
+
errors.push({ path, message: `must match pattern ${s["pattern"]}` });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// minLength / maxLength (string)
|
|
103
|
+
if ("minLength" in s && typeof value === "string") {
|
|
104
|
+
if (value.length < s["minLength"]) {
|
|
105
|
+
errors.push({ path, message: `must be at least ${s["minLength"]} characters` });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if ("maxLength" in s && typeof value === "string") {
|
|
109
|
+
if (value.length > s["maxLength"]) {
|
|
110
|
+
errors.push({ path, message: `must be at most ${s["maxLength"]} characters` });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// minimum (number)
|
|
114
|
+
if ("minimum" in s && typeof value === "number") {
|
|
115
|
+
if (value < s["minimum"]) {
|
|
116
|
+
errors.push({ path, message: `must be >= ${s["minimum"]}` });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// minItems (array)
|
|
120
|
+
if ("minItems" in s && Array.isArray(value)) {
|
|
121
|
+
if (value.length < s["minItems"]) {
|
|
122
|
+
errors.push({ path, message: `must have at least ${s["minItems"]} items` });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// required (object)
|
|
126
|
+
if ("required" in s && typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
127
|
+
const obj = value;
|
|
128
|
+
for (const key of s["required"]) {
|
|
129
|
+
if (!(key in obj)) {
|
|
130
|
+
errors.push({ path: `${path}.${key}`, message: `is required` });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// properties (object)
|
|
135
|
+
if ("properties" in s && typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
136
|
+
const obj = value;
|
|
137
|
+
const props = s["properties"];
|
|
138
|
+
for (const [key, propSchema] of Object.entries(props)) {
|
|
139
|
+
if (key in obj) {
|
|
140
|
+
validateNode(propSchema, obj[key], `${path}.${key}`, errors, rootSchema);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// additionalProperties: false
|
|
145
|
+
if (s["additionalProperties"] === false && typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
146
|
+
const obj = value;
|
|
147
|
+
const allowed = new Set([
|
|
148
|
+
...Object.keys((s["properties"] ?? {})),
|
|
149
|
+
...Object.keys((s["$defs"] ?? {})),
|
|
150
|
+
]);
|
|
151
|
+
for (const key of Object.keys(obj)) {
|
|
152
|
+
if (!allowed.has(key)) {
|
|
153
|
+
errors.push({ path: `${path}.${key}`, message: `additional property not allowed` });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// items (array)
|
|
158
|
+
if ("items" in s && Array.isArray(value)) {
|
|
159
|
+
const itemSchema = s["items"];
|
|
160
|
+
for (let i = 0; i < value.length; i++) {
|
|
161
|
+
validateNode(itemSchema, value[i], `${path}[${i}]`, errors, rootSchema);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// oneOf
|
|
165
|
+
if ("oneOf" in s) {
|
|
166
|
+
const schemas = s["oneOf"];
|
|
167
|
+
let matched = 0;
|
|
168
|
+
for (const sub of schemas) {
|
|
169
|
+
const subErrors = [];
|
|
170
|
+
validateNode(sub, value, path, subErrors, rootSchema);
|
|
171
|
+
if (subErrors.length === 0)
|
|
172
|
+
matched++;
|
|
173
|
+
}
|
|
174
|
+
if (matched !== 1) {
|
|
175
|
+
errors.push({ path, message: `must match exactly one of the oneOf schemas (matched ${matched})` });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// allOf
|
|
179
|
+
if ("allOf" in s) {
|
|
180
|
+
for (const sub of s["allOf"]) {
|
|
181
|
+
validateNode(sub, value, path, errors, rootSchema);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// if / then / else
|
|
185
|
+
if ("if" in s) {
|
|
186
|
+
const ifErrors = [];
|
|
187
|
+
validateNode(s["if"], value, path, ifErrors, rootSchema);
|
|
188
|
+
if (ifErrors.length === 0 && "then" in s) {
|
|
189
|
+
validateNode(s["then"], value, path, errors, rootSchema);
|
|
190
|
+
}
|
|
191
|
+
else if (ifErrors.length > 0 && "else" in s) {
|
|
192
|
+
validateNode(s["else"], value, path, errors, rootSchema);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// format — date and date-time (basic regex checks)
|
|
196
|
+
if ("format" in s && typeof value === "string") {
|
|
197
|
+
const fmt = s["format"];
|
|
198
|
+
if (fmt === "date" && !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
199
|
+
errors.push({ path, message: `must be an ISO 8601 date (YYYY-MM-DD)` });
|
|
200
|
+
}
|
|
201
|
+
if (fmt === "date-time" && !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
|
|
202
|
+
errors.push({ path, message: `must be an ISO 8601 date-time` });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Helpers
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
function resolveRef(ref, root) {
|
|
210
|
+
if (!ref.startsWith("#/"))
|
|
211
|
+
return null;
|
|
212
|
+
const parts = ref.slice(2).split("/");
|
|
213
|
+
let node = root;
|
|
214
|
+
for (const part of parts) {
|
|
215
|
+
if (typeof node !== "object" || node === null)
|
|
216
|
+
return null;
|
|
217
|
+
node = node[part];
|
|
218
|
+
}
|
|
219
|
+
return node;
|
|
220
|
+
}
|
|
221
|
+
function checkType(type, value) {
|
|
222
|
+
const types = Array.isArray(type) ? type : [type];
|
|
223
|
+
return types.some((t) => {
|
|
224
|
+
switch (t) {
|
|
225
|
+
case "string": return typeof value === "string";
|
|
226
|
+
case "number": return typeof value === "number";
|
|
227
|
+
case "integer": return typeof value === "number" && Number.isInteger(value);
|
|
228
|
+
case "boolean": return typeof value === "boolean";
|
|
229
|
+
case "null": return value === null;
|
|
230
|
+
case "array": return Array.isArray(value);
|
|
231
|
+
case "object": return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
232
|
+
default: return false;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function getType(value) {
|
|
237
|
+
if (value === null)
|
|
238
|
+
return "null";
|
|
239
|
+
if (Array.isArray(value))
|
|
240
|
+
return "array";
|
|
241
|
+
return typeof value;
|
|
242
|
+
}
|
|
243
|
+
function deepEqual(a, b) {
|
|
244
|
+
if (a === b)
|
|
245
|
+
return true;
|
|
246
|
+
if (typeof a !== typeof b)
|
|
247
|
+
return false;
|
|
248
|
+
if (typeof a === "object" && a !== null && b !== null) {
|
|
249
|
+
const ka = Object.keys(a).sort();
|
|
250
|
+
const kb = Object.keys(b).sort();
|
|
251
|
+
if (ka.join() !== kb.join())
|
|
252
|
+
return false;
|
|
253
|
+
return ka.every((k) => deepEqual(a[k], b[k]));
|
|
254
|
+
}
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=schema-validator.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* source-error.ts
|
|
3
|
+
* SourceError aggregator + stable-format warning emitter (Spec 042, T008).
|
|
4
|
+
*
|
|
5
|
+
* Implements the FR-008a warn-and-continue contract:
|
|
6
|
+
* - Source readers call `aggregateError()` when a source is unavailable.
|
|
7
|
+
* - The registry composer calls `emitSourceWarnings()` once after all sources run.
|
|
8
|
+
* - Output is one-line per missing source, emitted to stderr (R7).
|
|
9
|
+
* - MCP startup never throws because of a missing source.
|
|
10
|
+
*/
|
|
11
|
+
import type { SourceError, SourceLayer } from "./capability-entry.js";
|
|
12
|
+
/**
|
|
13
|
+
* Build a SourceError record from a SourceLayer + reason + optional raw error.
|
|
14
|
+
*/
|
|
15
|
+
export declare function makeSourceError(source: SourceLayer, reason: string, originalError?: unknown): SourceError;
|
|
16
|
+
/**
|
|
17
|
+
* Build a SourceError from a caught exception, normalizing the message.
|
|
18
|
+
*/
|
|
19
|
+
export declare function errorFromException(source: SourceLayer, err: unknown): SourceError;
|
|
20
|
+
/**
|
|
21
|
+
* In-memory accumulator used by the registry composer during one startup cycle.
|
|
22
|
+
* Not a singleton — callers construct and pass this around so tests can inspect it.
|
|
23
|
+
*/
|
|
24
|
+
export declare class SourceErrorAggregator {
|
|
25
|
+
private readonly _errors;
|
|
26
|
+
add(err: SourceError): void;
|
|
27
|
+
get errors(): readonly SourceError[];
|
|
28
|
+
get hasErrors(): boolean;
|
|
29
|
+
unavailableSources(): SourceLayer[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Emit one warning line per SourceError to stderr.
|
|
33
|
+
* Format: [comet-discovery] WARN source=<layer> reason=<reason>
|
|
34
|
+
*
|
|
35
|
+
* This format is stable across versions so external log parsers can key on it.
|
|
36
|
+
* Full JSON details are emitted only when COMET_DISCOVERY_VERBOSE=1.
|
|
37
|
+
*/
|
|
38
|
+
export declare function emitSourceWarnings(errors: readonly SourceError[]): void;
|
|
39
|
+
/**
|
|
40
|
+
* Emit a session-start warning when the registry is incomplete.
|
|
41
|
+
* Called by index.ts after composeRegistry(); format matches DriftReport.status.
|
|
42
|
+
*/
|
|
43
|
+
export declare function emitSessionStartWarning(unavailable: Array<{
|
|
44
|
+
source: SourceLayer;
|
|
45
|
+
reason: string;
|
|
46
|
+
}>): void;
|
|
47
|
+
//# sourceMappingURL=source-error.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* source-error.ts
|
|
3
|
+
* SourceError aggregator + stable-format warning emitter (Spec 042, T008).
|
|
4
|
+
*
|
|
5
|
+
* Implements the FR-008a warn-and-continue contract:
|
|
6
|
+
* - Source readers call `aggregateError()` when a source is unavailable.
|
|
7
|
+
* - The registry composer calls `emitSourceWarnings()` once after all sources run.
|
|
8
|
+
* - Output is one-line per missing source, emitted to stderr (R7).
|
|
9
|
+
* - MCP startup never throws because of a missing source.
|
|
10
|
+
*/
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Builder helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/**
|
|
15
|
+
* Build a SourceError record from a SourceLayer + reason + optional raw error.
|
|
16
|
+
*/
|
|
17
|
+
export function makeSourceError(source, reason, originalError) {
|
|
18
|
+
return { source, reason, originalError };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a SourceError from a caught exception, normalizing the message.
|
|
22
|
+
*/
|
|
23
|
+
export function errorFromException(source, err) {
|
|
24
|
+
const reason = err instanceof Error ? err.message : typeof err === "string" ? err : String(err);
|
|
25
|
+
return makeSourceError(source, reason, err);
|
|
26
|
+
}
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Aggregator
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/**
|
|
31
|
+
* In-memory accumulator used by the registry composer during one startup cycle.
|
|
32
|
+
* Not a singleton — callers construct and pass this around so tests can inspect it.
|
|
33
|
+
*/
|
|
34
|
+
export class SourceErrorAggregator {
|
|
35
|
+
_errors = [];
|
|
36
|
+
add(err) {
|
|
37
|
+
this._errors.push(err);
|
|
38
|
+
}
|
|
39
|
+
get errors() {
|
|
40
|
+
return this._errors;
|
|
41
|
+
}
|
|
42
|
+
get hasErrors() {
|
|
43
|
+
return this._errors.length > 0;
|
|
44
|
+
}
|
|
45
|
+
unavailableSources() {
|
|
46
|
+
return [...new Set(this._errors.map((e) => e.source))];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Stable-format warning emitter (R7)
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
/**
|
|
53
|
+
* Emit one warning line per SourceError to stderr.
|
|
54
|
+
* Format: [comet-discovery] WARN source=<layer> reason=<reason>
|
|
55
|
+
*
|
|
56
|
+
* This format is stable across versions so external log parsers can key on it.
|
|
57
|
+
* Full JSON details are emitted only when COMET_DISCOVERY_VERBOSE=1.
|
|
58
|
+
*/
|
|
59
|
+
export function emitSourceWarnings(errors) {
|
|
60
|
+
if (errors.length === 0)
|
|
61
|
+
return;
|
|
62
|
+
for (const err of errors) {
|
|
63
|
+
const line = `[comet-discovery] WARN source=${err.source} reason=${sanitize(err.reason)}`;
|
|
64
|
+
process.stderr.write(line + "\n");
|
|
65
|
+
if (process.env.COMET_DISCOVERY_VERBOSE === "1") {
|
|
66
|
+
const detail = {
|
|
67
|
+
source: err.source,
|
|
68
|
+
reason: err.reason,
|
|
69
|
+
error: err.originalError instanceof Error
|
|
70
|
+
? { message: err.originalError.message, stack: err.originalError.stack }
|
|
71
|
+
: err.originalError,
|
|
72
|
+
};
|
|
73
|
+
process.stderr.write("[comet-discovery] VERBOSE " + JSON.stringify(detail) + "\n");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Emit a session-start warning when the registry is incomplete.
|
|
79
|
+
* Called by index.ts after composeRegistry(); format matches DriftReport.status.
|
|
80
|
+
*/
|
|
81
|
+
export function emitSessionStartWarning(unavailable) {
|
|
82
|
+
if (unavailable.length === 0)
|
|
83
|
+
return;
|
|
84
|
+
const sources = unavailable.map((u) => u.source).join(", ");
|
|
85
|
+
process.stderr.write(`[comet-discovery] WARN session-start: incomplete registry — unavailable sources: ${sources}. ` +
|
|
86
|
+
`Run COMET_DISCOVERY_VERBOSE=1 for details.\n`);
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Helpers
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
/** Strip newlines and truncate long reason strings for single-line stderr. */
|
|
92
|
+
function sanitize(s) {
|
|
93
|
+
return s.replace(/[\r\n]+/g, " ").slice(0, 200);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=source-error.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tool-meta.ts
|
|
3
|
+
* Discovery metadata catalog for the 25 native Comet MCP tools (Spec 042, T024).
|
|
4
|
+
*
|
|
5
|
+
* Each entry provides the discovery-layer fields that are NOT present in the
|
|
6
|
+
* existing TOOLS array in index.ts: canonicalId, safety, preconditions,
|
|
7
|
+
* intentKeywords, and a description following the R10 template.
|
|
8
|
+
*
|
|
9
|
+
* AUTHORITATIVE SOURCES:
|
|
10
|
+
* - Safety classes: docs/TOOL-SAFETY-REFERENCE.md
|
|
11
|
+
* - Preconditions: same doc, safety class descriptions
|
|
12
|
+
*
|
|
13
|
+
* This file is the single source of truth for native-tool discovery metadata.
|
|
14
|
+
* When a new tool is added to TOOLS in index.ts, add a matching entry here.
|
|
15
|
+
*/
|
|
16
|
+
import type { SafetyClass, Precondition } from "./capability-entry.js";
|
|
17
|
+
export interface ToolMeta {
|
|
18
|
+
/** Must match the `name` field in the TOOLS array in index.ts */
|
|
19
|
+
toolName: string;
|
|
20
|
+
/** Shared canonicalId used for dedup grouping across layers */
|
|
21
|
+
canonicalId: string;
|
|
22
|
+
/** Safety class — from docs/TOOL-SAFETY-REFERENCE.md */
|
|
23
|
+
safety: SafetyClass;
|
|
24
|
+
/** Prerequisites before calling this tool */
|
|
25
|
+
preconditions: Precondition[];
|
|
26
|
+
/**
|
|
27
|
+
* At least 3 lowercase keywords for ToolSearch ranking.
|
|
28
|
+
* Think: what would an agent type to find this tool?
|
|
29
|
+
*/
|
|
30
|
+
intentKeywords: string[];
|
|
31
|
+
/** R10 description template: "<verb> <object> [<modifier>]. Source: mcp. Safety: <class>." */
|
|
32
|
+
description: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Discovery metadata for the 25 native MCP tools.
|
|
36
|
+
* Order matches the TOOLS array in index.ts for readability.
|
|
37
|
+
*/
|
|
38
|
+
export declare const TOOL_META: ToolMeta[];
|
|
39
|
+
/** Lookup map: toolName → ToolMeta */
|
|
40
|
+
export declare const TOOL_META_MAP: ReadonlyMap<string, ToolMeta>;
|
|
41
|
+
//# sourceMappingURL=tool-meta.d.ts.map
|