oh-my-opencode-kikokikok 2.14.2 → 2.15.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/dist/cli/index.js +11 -4
- package/dist/config/schema.d.ts +50 -0
- package/dist/features/knowledge-provider/index.d.ts +1 -0
- package/dist/features/knowledge-provider/mcp-manager.d.ts +67 -0
- package/dist/features/knowledge-provider/mcp-manager.test.d.ts +1 -0
- package/dist/features/knowledge-provider/types.d.ts +22 -2
- package/dist/index.js +3302 -606
- package/dist/plugin-config.test.d.ts +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/knowledge/index.d.ts +1 -0
- package/dist/tools/knowledge/provider-tools.d.ts +3 -0
- package/dist/tools/knowledge/provider-tools.test.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2902,37 +2902,37 @@ var require_dataType = __commonJS((exports) => {
|
|
|
2902
2902
|
DataType2[DataType2["Wrong"] = 1] = "Wrong";
|
|
2903
2903
|
})(DataType || (exports.DataType = DataType = {}));
|
|
2904
2904
|
function getSchemaTypes(schema2) {
|
|
2905
|
-
const
|
|
2906
|
-
const hasNull =
|
|
2905
|
+
const types18 = getJSONTypes(schema2.type);
|
|
2906
|
+
const hasNull = types18.includes("null");
|
|
2907
2907
|
if (hasNull) {
|
|
2908
2908
|
if (schema2.nullable === false)
|
|
2909
2909
|
throw new Error("type: null contradicts nullable: false");
|
|
2910
2910
|
} else {
|
|
2911
|
-
if (!
|
|
2911
|
+
if (!types18.length && schema2.nullable !== undefined) {
|
|
2912
2912
|
throw new Error('"nullable" cannot be used without "type"');
|
|
2913
2913
|
}
|
|
2914
2914
|
if (schema2.nullable === true)
|
|
2915
|
-
|
|
2915
|
+
types18.push("null");
|
|
2916
2916
|
}
|
|
2917
|
-
return
|
|
2917
|
+
return types18;
|
|
2918
2918
|
}
|
|
2919
2919
|
exports.getSchemaTypes = getSchemaTypes;
|
|
2920
2920
|
function getJSONTypes(ts) {
|
|
2921
|
-
const
|
|
2922
|
-
if (
|
|
2923
|
-
return
|
|
2924
|
-
throw new Error("type must be JSONType or JSONType[]: " +
|
|
2921
|
+
const types18 = Array.isArray(ts) ? ts : ts ? [ts] : [];
|
|
2922
|
+
if (types18.every(rules_1.isJSONType))
|
|
2923
|
+
return types18;
|
|
2924
|
+
throw new Error("type must be JSONType or JSONType[]: " + types18.join(","));
|
|
2925
2925
|
}
|
|
2926
2926
|
exports.getJSONTypes = getJSONTypes;
|
|
2927
|
-
function coerceAndCheckDataType(it,
|
|
2927
|
+
function coerceAndCheckDataType(it, types18) {
|
|
2928
2928
|
const { gen, data, opts } = it;
|
|
2929
|
-
const coerceTo = coerceToTypes(
|
|
2930
|
-
const checkTypes =
|
|
2929
|
+
const coerceTo = coerceToTypes(types18, opts.coerceTypes);
|
|
2930
|
+
const checkTypes = types18.length > 0 && !(coerceTo.length === 0 && types18.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types18[0]));
|
|
2931
2931
|
if (checkTypes) {
|
|
2932
|
-
const wrongType = checkDataTypes(
|
|
2932
|
+
const wrongType = checkDataTypes(types18, data, opts.strictNumbers, DataType.Wrong);
|
|
2933
2933
|
gen.if(wrongType, () => {
|
|
2934
2934
|
if (coerceTo.length)
|
|
2935
|
-
coerceData(it,
|
|
2935
|
+
coerceData(it, types18, coerceTo);
|
|
2936
2936
|
else
|
|
2937
2937
|
reportTypeError(it);
|
|
2938
2938
|
});
|
|
@@ -2941,15 +2941,15 @@ var require_dataType = __commonJS((exports) => {
|
|
|
2941
2941
|
}
|
|
2942
2942
|
exports.coerceAndCheckDataType = coerceAndCheckDataType;
|
|
2943
2943
|
var COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
|
|
2944
|
-
function coerceToTypes(
|
|
2945
|
-
return coerceTypes ?
|
|
2944
|
+
function coerceToTypes(types18, coerceTypes) {
|
|
2945
|
+
return coerceTypes ? types18.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
|
|
2946
2946
|
}
|
|
2947
|
-
function coerceData(it,
|
|
2947
|
+
function coerceData(it, types18, coerceTo) {
|
|
2948
2948
|
const { gen, data, opts } = it;
|
|
2949
2949
|
const dataType = gen.let("dataType", (0, codegen_1._)`typeof ${data}`);
|
|
2950
2950
|
const coerced = gen.let("coerced", (0, codegen_1._)`undefined`);
|
|
2951
2951
|
if (opts.coerceTypes === "array") {
|
|
2952
|
-
gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(
|
|
2952
|
+
gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types18, data, opts.strictNumbers), () => gen.assign(coerced, data)));
|
|
2953
2953
|
}
|
|
2954
2954
|
gen.if((0, codegen_1._)`${coerced} !== undefined`);
|
|
2955
2955
|
for (const t of coerceTo) {
|
|
@@ -3025,19 +3025,19 @@ var require_dataType = __commonJS((exports) => {
|
|
|
3025
3025
|
return checkDataType(dataTypes[0], data, strictNums, correct);
|
|
3026
3026
|
}
|
|
3027
3027
|
let cond;
|
|
3028
|
-
const
|
|
3029
|
-
if (
|
|
3028
|
+
const types18 = (0, util_1.toHash)(dataTypes);
|
|
3029
|
+
if (types18.array && types18.object) {
|
|
3030
3030
|
const notObj = (0, codegen_1._)`typeof ${data} != "object"`;
|
|
3031
|
-
cond =
|
|
3032
|
-
delete
|
|
3033
|
-
delete
|
|
3034
|
-
delete
|
|
3031
|
+
cond = types18.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`;
|
|
3032
|
+
delete types18.null;
|
|
3033
|
+
delete types18.array;
|
|
3034
|
+
delete types18.object;
|
|
3035
3035
|
} else {
|
|
3036
3036
|
cond = codegen_1.nil;
|
|
3037
3037
|
}
|
|
3038
|
-
if (
|
|
3039
|
-
delete
|
|
3040
|
-
for (const t in
|
|
3038
|
+
if (types18.number)
|
|
3039
|
+
delete types18.integer;
|
|
3040
|
+
for (const t in types18)
|
|
3041
3041
|
cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct));
|
|
3042
3042
|
return cond;
|
|
3043
3043
|
}
|
|
@@ -3825,9 +3825,9 @@ var require_validate = __commonJS((exports) => {
|
|
|
3825
3825
|
function typeAndKeywords(it, errsCount) {
|
|
3826
3826
|
if (it.opts.jtd)
|
|
3827
3827
|
return schemaKeywords(it, [], false, errsCount);
|
|
3828
|
-
const
|
|
3829
|
-
const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it,
|
|
3830
|
-
schemaKeywords(it,
|
|
3828
|
+
const types18 = (0, dataType_1.getSchemaTypes)(it.schema);
|
|
3829
|
+
const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it, types18);
|
|
3830
|
+
schemaKeywords(it, types18, !checkedTypes, errsCount);
|
|
3831
3831
|
}
|
|
3832
3832
|
function checkRefsAndKeywords(it) {
|
|
3833
3833
|
const { schema: schema2, errSchemaPath, opts, self } = it;
|
|
@@ -3877,7 +3877,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
3877
3877
|
if (items instanceof codegen_1.Name)
|
|
3878
3878
|
gen.assign((0, codegen_1._)`${evaluated}.items`, items);
|
|
3879
3879
|
}
|
|
3880
|
-
function schemaKeywords(it,
|
|
3880
|
+
function schemaKeywords(it, types18, typeErrors, errsCount) {
|
|
3881
3881
|
const { gen, schema: schema2, data, allErrors, opts, self } = it;
|
|
3882
3882
|
const { RULES } = self;
|
|
3883
3883
|
if (schema2.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema2, RULES))) {
|
|
@@ -3885,7 +3885,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
3885
3885
|
return;
|
|
3886
3886
|
}
|
|
3887
3887
|
if (!opts.jtd)
|
|
3888
|
-
checkStrictTypes(it,
|
|
3888
|
+
checkStrictTypes(it, types18);
|
|
3889
3889
|
gen.block(() => {
|
|
3890
3890
|
for (const group of RULES.rules)
|
|
3891
3891
|
groupKeywords(group);
|
|
@@ -3897,7 +3897,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
3897
3897
|
if (group.type) {
|
|
3898
3898
|
gen.if((0, dataType_2.checkDataType)(group.type, data, opts.strictNumbers));
|
|
3899
3899
|
iterateKeywords(it, group);
|
|
3900
|
-
if (
|
|
3900
|
+
if (types18.length === 1 && types18[0] === group.type && typeErrors) {
|
|
3901
3901
|
gen.else();
|
|
3902
3902
|
(0, dataType_2.reportTypeError)(it);
|
|
3903
3903
|
}
|
|
@@ -3921,27 +3921,27 @@ var require_validate = __commonJS((exports) => {
|
|
|
3921
3921
|
}
|
|
3922
3922
|
});
|
|
3923
3923
|
}
|
|
3924
|
-
function checkStrictTypes(it,
|
|
3924
|
+
function checkStrictTypes(it, types18) {
|
|
3925
3925
|
if (it.schemaEnv.meta || !it.opts.strictTypes)
|
|
3926
3926
|
return;
|
|
3927
|
-
checkContextTypes(it,
|
|
3927
|
+
checkContextTypes(it, types18);
|
|
3928
3928
|
if (!it.opts.allowUnionTypes)
|
|
3929
|
-
checkMultipleTypes(it,
|
|
3929
|
+
checkMultipleTypes(it, types18);
|
|
3930
3930
|
checkKeywordTypes(it, it.dataTypes);
|
|
3931
3931
|
}
|
|
3932
|
-
function checkContextTypes(it,
|
|
3933
|
-
if (!
|
|
3932
|
+
function checkContextTypes(it, types18) {
|
|
3933
|
+
if (!types18.length)
|
|
3934
3934
|
return;
|
|
3935
3935
|
if (!it.dataTypes.length) {
|
|
3936
|
-
it.dataTypes =
|
|
3936
|
+
it.dataTypes = types18;
|
|
3937
3937
|
return;
|
|
3938
3938
|
}
|
|
3939
|
-
|
|
3939
|
+
types18.forEach((t) => {
|
|
3940
3940
|
if (!includesType(it.dataTypes, t)) {
|
|
3941
3941
|
strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`);
|
|
3942
3942
|
}
|
|
3943
3943
|
});
|
|
3944
|
-
narrowSchemaTypes(it,
|
|
3944
|
+
narrowSchemaTypes(it, types18);
|
|
3945
3945
|
}
|
|
3946
3946
|
function checkMultipleTypes(it, ts) {
|
|
3947
3947
|
if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) {
|
|
@@ -42134,6 +42134,203 @@ var knowledge_propose = tool({
|
|
|
42134
42134
|
}
|
|
42135
42135
|
}
|
|
42136
42136
|
});
|
|
42137
|
+
|
|
42138
|
+
// src/tools/knowledge/provider-tools.ts
|
|
42139
|
+
function createKnowledgeProviderTools(registry2) {
|
|
42140
|
+
const knowledge_query2 = tool({
|
|
42141
|
+
description: "Search and query the knowledge repository. " + "Find ADRs, policies, patterns, and specs by keywords, type, layer, or full-text search. " + "Returns summaries to conserve context - use knowledge_show for full details.",
|
|
42142
|
+
args: {
|
|
42143
|
+
query: tool.schema.string().optional().describe("Full-text search query across knowledge summaries"),
|
|
42144
|
+
type: tool.schema.enum(["adr", "policy", "pattern", "spec"]).optional().describe("Filter by knowledge type"),
|
|
42145
|
+
layer: tool.schema.enum(["company", "org", "project"]).optional().describe("Filter by organizational layer"),
|
|
42146
|
+
severity: tool.schema.enum(["info", "warn", "block"]).optional().describe("Filter by severity level"),
|
|
42147
|
+
tags: tool.schema.array(tool.schema.string()).optional().describe("Filter by tags"),
|
|
42148
|
+
limit: tool.schema.number().optional().describe(`Maximum results to return (default: ${DEFAULT_QUERY_LIMIT})`)
|
|
42149
|
+
},
|
|
42150
|
+
execute: async (args) => {
|
|
42151
|
+
try {
|
|
42152
|
+
const types14 = args.type ? [args.type] : undefined;
|
|
42153
|
+
const results = await registry2.search({
|
|
42154
|
+
text: args.query ?? "",
|
|
42155
|
+
types: types14,
|
|
42156
|
+
layers: args.layer ? [args.layer] : undefined,
|
|
42157
|
+
limit: args.limit ?? DEFAULT_QUERY_LIMIT,
|
|
42158
|
+
includeContent: false
|
|
42159
|
+
});
|
|
42160
|
+
if (results.length === 0) {
|
|
42161
|
+
return "No knowledge items found matching the query.";
|
|
42162
|
+
}
|
|
42163
|
+
const lines = [`Found ${results.length} knowledge items:`, ""];
|
|
42164
|
+
for (const { item, score } of results) {
|
|
42165
|
+
const severity = item.metadata.severity ?? "info";
|
|
42166
|
+
lines.push(`**${item.id}** [${item.type}/${item.layer ?? "unknown"}] ${severity.toUpperCase()} (score: ${score.toFixed(2)})`);
|
|
42167
|
+
lines.push(` ${item.title}`);
|
|
42168
|
+
lines.push(` ${item.summary}`);
|
|
42169
|
+
if (item.metadata.tags && item.metadata.tags.length > 0) {
|
|
42170
|
+
lines.push(` Tags: ${item.metadata.tags.join(", ")}`);
|
|
42171
|
+
}
|
|
42172
|
+
lines.push(` Provider: ${item.provider}`);
|
|
42173
|
+
lines.push("");
|
|
42174
|
+
}
|
|
42175
|
+
return lines.join(`
|
|
42176
|
+
`);
|
|
42177
|
+
} catch (e) {
|
|
42178
|
+
return `Error querying knowledge: ${e instanceof Error ? e.message : String(e)}`;
|
|
42179
|
+
}
|
|
42180
|
+
}
|
|
42181
|
+
});
|
|
42182
|
+
const knowledge_list2 = tool({
|
|
42183
|
+
description: "List all knowledge items in the repository with statistics. " + "Provides an overview of ADRs, policies, patterns, and specs organized by layer and type.",
|
|
42184
|
+
args: {
|
|
42185
|
+
layer: tool.schema.enum(["company", "org", "project"]).optional().describe("Filter to specific layer"),
|
|
42186
|
+
type: tool.schema.enum(["adr", "policy", "pattern", "spec"]).optional().describe("Filter to specific type"),
|
|
42187
|
+
verbose: tool.schema.boolean().optional().describe("Include item summaries (default: false)")
|
|
42188
|
+
},
|
|
42189
|
+
execute: async (args) => {
|
|
42190
|
+
try {
|
|
42191
|
+
const health = await registry2.healthCheck();
|
|
42192
|
+
const providers = registry2.getProviders();
|
|
42193
|
+
const types14 = args.type ? [args.type] : undefined;
|
|
42194
|
+
const results = await registry2.search({
|
|
42195
|
+
text: "",
|
|
42196
|
+
types: types14,
|
|
42197
|
+
layers: args.layer ? [args.layer] : undefined,
|
|
42198
|
+
limit: 200,
|
|
42199
|
+
includeContent: false
|
|
42200
|
+
});
|
|
42201
|
+
const lines = [
|
|
42202
|
+
"# Knowledge Provider Statistics",
|
|
42203
|
+
"",
|
|
42204
|
+
`Total providers: ${providers.length}`,
|
|
42205
|
+
"",
|
|
42206
|
+
"## Providers"
|
|
42207
|
+
];
|
|
42208
|
+
for (const provider of providers) {
|
|
42209
|
+
const status = health.get(provider.name);
|
|
42210
|
+
const statusStr = status?.status ?? "unknown";
|
|
42211
|
+
lines.push(`- ${provider.name} (${provider.type}): ${statusStr}`);
|
|
42212
|
+
}
|
|
42213
|
+
lines.push("");
|
|
42214
|
+
lines.push("## Items Found");
|
|
42215
|
+
lines.push(`Total: ${results.length}`);
|
|
42216
|
+
lines.push("");
|
|
42217
|
+
const byType = {};
|
|
42218
|
+
const byLayer = {};
|
|
42219
|
+
const byProvider = {};
|
|
42220
|
+
for (const { item } of results) {
|
|
42221
|
+
byType[item.type] = (byType[item.type] ?? 0) + 1;
|
|
42222
|
+
byLayer[item.layer ?? "unknown"] = (byLayer[item.layer ?? "unknown"] ?? 0) + 1;
|
|
42223
|
+
byProvider[item.provider] = (byProvider[item.provider] ?? 0) + 1;
|
|
42224
|
+
}
|
|
42225
|
+
lines.push("### By Type");
|
|
42226
|
+
for (const [type2, count] of Object.entries(byType)) {
|
|
42227
|
+
lines.push(`- ${type2}: ${count}`);
|
|
42228
|
+
}
|
|
42229
|
+
lines.push("");
|
|
42230
|
+
lines.push("### By Layer");
|
|
42231
|
+
for (const [layer, count] of Object.entries(byLayer)) {
|
|
42232
|
+
lines.push(`- ${layer}: ${count}`);
|
|
42233
|
+
}
|
|
42234
|
+
lines.push("");
|
|
42235
|
+
lines.push("### By Provider");
|
|
42236
|
+
for (const [provider, count] of Object.entries(byProvider)) {
|
|
42237
|
+
lines.push(`- ${provider}: ${count}`);
|
|
42238
|
+
}
|
|
42239
|
+
if (args.verbose && results.length > 0) {
|
|
42240
|
+
lines.push("");
|
|
42241
|
+
lines.push("## Items");
|
|
42242
|
+
for (const { item } of results) {
|
|
42243
|
+
lines.push(`- **${item.id}** [${item.type}/${item.layer ?? "unknown"}] ${item.title}`);
|
|
42244
|
+
}
|
|
42245
|
+
}
|
|
42246
|
+
return lines.join(`
|
|
42247
|
+
`);
|
|
42248
|
+
} catch (e) {
|
|
42249
|
+
return `Error listing knowledge: ${e instanceof Error ? e.message : String(e)}`;
|
|
42250
|
+
}
|
|
42251
|
+
}
|
|
42252
|
+
});
|
|
42253
|
+
const knowledge_show2 = tool({
|
|
42254
|
+
description: "Show full details of a specific knowledge item by ID. " + "Includes complete content, constraints, metadata, and optionally version history.",
|
|
42255
|
+
args: {
|
|
42256
|
+
id: tool.schema.string().describe("The knowledge item ID to retrieve"),
|
|
42257
|
+
includeConstraints: tool.schema.boolean().optional().describe("Include constraint details (default: true)"),
|
|
42258
|
+
includeHistory: tool.schema.boolean().optional().describe("Include version history (default: false)")
|
|
42259
|
+
},
|
|
42260
|
+
execute: async (args) => {
|
|
42261
|
+
try {
|
|
42262
|
+
const item = await registry2.getById(args.id);
|
|
42263
|
+
if (!item) {
|
|
42264
|
+
return `Knowledge item not found: ${args.id}`;
|
|
42265
|
+
}
|
|
42266
|
+
const includeConstraints = args.includeConstraints ?? true;
|
|
42267
|
+
const lines = [
|
|
42268
|
+
`# ${item.title}`,
|
|
42269
|
+
"",
|
|
42270
|
+
`**ID:** ${item.id}`,
|
|
42271
|
+
`**Type:** ${item.type}`,
|
|
42272
|
+
`**Layer:** ${item.layer ?? "unknown"}`,
|
|
42273
|
+
`**Provider:** ${item.provider}`
|
|
42274
|
+
];
|
|
42275
|
+
if (item.metadata.severity) {
|
|
42276
|
+
lines.push(`**Severity:** ${item.metadata.severity}`);
|
|
42277
|
+
}
|
|
42278
|
+
if (item.createdAt) {
|
|
42279
|
+
lines.push(`**Created:** ${item.createdAt}`);
|
|
42280
|
+
}
|
|
42281
|
+
lines.push("");
|
|
42282
|
+
if (item.metadata.tags && item.metadata.tags.length > 0) {
|
|
42283
|
+
lines.push(`**Tags:** ${item.metadata.tags.join(", ")}`);
|
|
42284
|
+
lines.push("");
|
|
42285
|
+
}
|
|
42286
|
+
lines.push("## Summary");
|
|
42287
|
+
lines.push("");
|
|
42288
|
+
lines.push(item.summary);
|
|
42289
|
+
lines.push("");
|
|
42290
|
+
if (item.content) {
|
|
42291
|
+
lines.push("## Content");
|
|
42292
|
+
lines.push("");
|
|
42293
|
+
lines.push(item.content);
|
|
42294
|
+
lines.push("");
|
|
42295
|
+
}
|
|
42296
|
+
if (includeConstraints && item.metadata.extra?.constraints && Array.isArray(item.metadata.extra.constraints)) {
|
|
42297
|
+
const constraints = item.metadata.extra.constraints;
|
|
42298
|
+
if (constraints.length > 0) {
|
|
42299
|
+
lines.push("## Constraints");
|
|
42300
|
+
lines.push("");
|
|
42301
|
+
for (const c of constraints) {
|
|
42302
|
+
lines.push(`### ${c.id ?? "constraint"}`);
|
|
42303
|
+
if (c.operator)
|
|
42304
|
+
lines.push(`- **Operator:** ${c.operator}`);
|
|
42305
|
+
if (c.target)
|
|
42306
|
+
lines.push(`- **Target:** ${c.target}`);
|
|
42307
|
+
if (c.pattern)
|
|
42308
|
+
lines.push(`- **Pattern:** \`${c.pattern}\``);
|
|
42309
|
+
if (c.severity)
|
|
42310
|
+
lines.push(`- **Severity:** ${c.severity}`);
|
|
42311
|
+
if (c.message)
|
|
42312
|
+
lines.push(`- **Message:** ${c.message}`);
|
|
42313
|
+
if (c.appliesTo?.length)
|
|
42314
|
+
lines.push(`- **Applies To:** ${c.appliesTo.join(", ")}`);
|
|
42315
|
+
if (c.excludes?.length)
|
|
42316
|
+
lines.push(`- **Excludes:** ${c.excludes.join(", ")}`);
|
|
42317
|
+
lines.push("");
|
|
42318
|
+
}
|
|
42319
|
+
}
|
|
42320
|
+
}
|
|
42321
|
+
return lines.join(`
|
|
42322
|
+
`);
|
|
42323
|
+
} catch (e) {
|
|
42324
|
+
return `Error showing knowledge: ${e instanceof Error ? e.message : String(e)}`;
|
|
42325
|
+
}
|
|
42326
|
+
}
|
|
42327
|
+
});
|
|
42328
|
+
return {
|
|
42329
|
+
knowledge_query: knowledge_query2,
|
|
42330
|
+
knowledge_list: knowledge_list2,
|
|
42331
|
+
knowledge_show: knowledge_show2
|
|
42332
|
+
};
|
|
42333
|
+
}
|
|
42137
42334
|
// src/tools/interactive-bash/constants.ts
|
|
42138
42335
|
var DEFAULT_TIMEOUT_MS4 = 60000;
|
|
42139
42336
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
@@ -43580,399 +43777,872 @@ class Mem0Adapter {
|
|
|
43580
43777
|
});
|
|
43581
43778
|
}
|
|
43582
43779
|
}
|
|
43583
|
-
// src/features/
|
|
43584
|
-
|
|
43585
|
-
|
|
43586
|
-
|
|
43587
|
-
|
|
43588
|
-
|
|
43589
|
-
|
|
43590
|
-
const directPath = join61(MESSAGE_STORAGE, sessionID);
|
|
43591
|
-
if (existsSync48(directPath))
|
|
43592
|
-
return directPath;
|
|
43593
|
-
for (const dir of readdirSync17(MESSAGE_STORAGE)) {
|
|
43594
|
-
const sessionPath = join61(MESSAGE_STORAGE, dir, sessionID);
|
|
43595
|
-
if (existsSync48(sessionPath))
|
|
43596
|
-
return sessionPath;
|
|
43597
|
-
}
|
|
43598
|
-
return null;
|
|
43599
|
-
}
|
|
43780
|
+
// src/features/knowledge-provider/registry.ts
|
|
43781
|
+
var DEFAULT_CONFIG2 = {
|
|
43782
|
+
defaultLimit: 10,
|
|
43783
|
+
defaultThreshold: 0.5,
|
|
43784
|
+
mergeStrategy: "score",
|
|
43785
|
+
deduplicate: true
|
|
43786
|
+
};
|
|
43600
43787
|
|
|
43601
|
-
class
|
|
43602
|
-
|
|
43603
|
-
|
|
43604
|
-
|
|
43605
|
-
|
|
43606
|
-
pollingInterval;
|
|
43607
|
-
constructor(ctx) {
|
|
43608
|
-
this.tasks = new Map;
|
|
43609
|
-
this.notifications = new Map;
|
|
43610
|
-
this.client = ctx.client;
|
|
43611
|
-
this.directory = ctx.directory;
|
|
43788
|
+
class KnowledgeProviderRegistry {
|
|
43789
|
+
providers = new Map;
|
|
43790
|
+
config;
|
|
43791
|
+
constructor(config3) {
|
|
43792
|
+
this.config = { ...DEFAULT_CONFIG2, ...config3 };
|
|
43612
43793
|
}
|
|
43613
|
-
async
|
|
43614
|
-
if (
|
|
43615
|
-
throw new Error("
|
|
43794
|
+
async register(provider) {
|
|
43795
|
+
if (this.providers.has(provider.name)) {
|
|
43796
|
+
throw new Error(`Provider "${provider.name}" is already registered`);
|
|
43616
43797
|
}
|
|
43617
|
-
|
|
43618
|
-
|
|
43619
|
-
|
|
43620
|
-
|
|
43798
|
+
await provider.initialize();
|
|
43799
|
+
this.providers.set(provider.name, provider);
|
|
43800
|
+
}
|
|
43801
|
+
async unregister(name) {
|
|
43802
|
+
const provider = this.providers.get(name);
|
|
43803
|
+
if (!provider) {
|
|
43804
|
+
return;
|
|
43805
|
+
}
|
|
43806
|
+
if (provider.dispose) {
|
|
43807
|
+
await provider.dispose();
|
|
43808
|
+
}
|
|
43809
|
+
this.providers.delete(name);
|
|
43810
|
+
}
|
|
43811
|
+
async search(query) {
|
|
43812
|
+
const targetProviders = this.getTargetProviders(query.providers);
|
|
43813
|
+
if (targetProviders.length === 0) {
|
|
43814
|
+
return [];
|
|
43815
|
+
}
|
|
43816
|
+
const limit = query.limit ?? this.config.defaultLimit;
|
|
43817
|
+
const threshold = query.threshold ?? this.config.defaultThreshold;
|
|
43818
|
+
const searchPromises = targetProviders.map(async (provider) => {
|
|
43819
|
+
try {
|
|
43820
|
+
const results = await provider.search({
|
|
43821
|
+
...query,
|
|
43822
|
+
limit: limit * 2,
|
|
43823
|
+
threshold
|
|
43824
|
+
});
|
|
43825
|
+
return { provider: provider.name, results };
|
|
43826
|
+
} catch {
|
|
43827
|
+
return { provider: provider.name, results: [] };
|
|
43621
43828
|
}
|
|
43622
43829
|
});
|
|
43623
|
-
|
|
43624
|
-
|
|
43830
|
+
const allResults = await Promise.all(searchPromises);
|
|
43831
|
+
const merged = this.mergeResults(allResults.flatMap((r) => r.results), this.config.mergeStrategy, limit);
|
|
43832
|
+
if (this.config.deduplicate) {
|
|
43833
|
+
return this.deduplicateResults(merged);
|
|
43625
43834
|
}
|
|
43626
|
-
|
|
43627
|
-
|
|
43628
|
-
|
|
43629
|
-
|
|
43630
|
-
|
|
43631
|
-
|
|
43632
|
-
|
|
43633
|
-
|
|
43634
|
-
prompt: input.prompt,
|
|
43635
|
-
agent: input.agent,
|
|
43636
|
-
status: "running",
|
|
43637
|
-
startedAt: new Date,
|
|
43638
|
-
progress: {
|
|
43639
|
-
toolCalls: 0,
|
|
43640
|
-
lastUpdate: new Date
|
|
43641
|
-
},
|
|
43642
|
-
parentModel: input.parentModel
|
|
43643
|
-
};
|
|
43644
|
-
this.tasks.set(task.id, task);
|
|
43645
|
-
this.startPolling();
|
|
43646
|
-
log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent });
|
|
43647
|
-
this.client.session.promptAsync({
|
|
43648
|
-
path: { id: sessionID },
|
|
43649
|
-
body: {
|
|
43650
|
-
agent: input.agent,
|
|
43651
|
-
tools: {
|
|
43652
|
-
task: false,
|
|
43653
|
-
background_task: false
|
|
43654
|
-
},
|
|
43655
|
-
parts: [{ type: "text", text: input.prompt }]
|
|
43835
|
+
return merged.slice(0, limit);
|
|
43836
|
+
}
|
|
43837
|
+
async getById(id) {
|
|
43838
|
+
const [providerName, itemId] = this.parseId(id);
|
|
43839
|
+
if (providerName) {
|
|
43840
|
+
const provider = this.providers.get(providerName);
|
|
43841
|
+
if (provider?.getById) {
|
|
43842
|
+
return provider.getById(itemId);
|
|
43656
43843
|
}
|
|
43657
|
-
|
|
43658
|
-
|
|
43659
|
-
|
|
43660
|
-
if (
|
|
43661
|
-
|
|
43662
|
-
|
|
43663
|
-
|
|
43664
|
-
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
|
|
43665
|
-
} else {
|
|
43666
|
-
existingTask.error = errorMessage;
|
|
43844
|
+
return null;
|
|
43845
|
+
}
|
|
43846
|
+
for (const provider of this.providers.values()) {
|
|
43847
|
+
if (provider.getById) {
|
|
43848
|
+
const item = await provider.getById(id);
|
|
43849
|
+
if (item) {
|
|
43850
|
+
return item;
|
|
43667
43851
|
}
|
|
43668
|
-
existingTask.completedAt = new Date;
|
|
43669
|
-
this.markForNotification(existingTask);
|
|
43670
|
-
this.notifyParentSession(existingTask);
|
|
43671
43852
|
}
|
|
43672
|
-
}
|
|
43673
|
-
return
|
|
43853
|
+
}
|
|
43854
|
+
return null;
|
|
43674
43855
|
}
|
|
43675
|
-
|
|
43676
|
-
return this.
|
|
43856
|
+
getProviders() {
|
|
43857
|
+
return Array.from(this.providers.values());
|
|
43677
43858
|
}
|
|
43678
|
-
|
|
43679
|
-
|
|
43680
|
-
|
|
43681
|
-
|
|
43682
|
-
|
|
43859
|
+
getProvider(name) {
|
|
43860
|
+
return this.providers.get(name);
|
|
43861
|
+
}
|
|
43862
|
+
async healthCheck() {
|
|
43863
|
+
const results = new Map;
|
|
43864
|
+
const checks3 = Array.from(this.providers.entries()).map(async ([name, provider]) => {
|
|
43865
|
+
try {
|
|
43866
|
+
const health = await provider.healthCheck();
|
|
43867
|
+
results.set(name, health);
|
|
43868
|
+
} catch (error45) {
|
|
43869
|
+
results.set(name, {
|
|
43870
|
+
status: "unavailable",
|
|
43871
|
+
message: error45 instanceof Error ? error45.message : "Unknown error",
|
|
43872
|
+
lastChecked: new Date().toISOString()
|
|
43873
|
+
});
|
|
43683
43874
|
}
|
|
43875
|
+
});
|
|
43876
|
+
await Promise.all(checks3);
|
|
43877
|
+
return results;
|
|
43878
|
+
}
|
|
43879
|
+
getTargetProviders(filterNames) {
|
|
43880
|
+
if (!filterNames || filterNames.length === 0) {
|
|
43881
|
+
return Array.from(this.providers.values());
|
|
43882
|
+
}
|
|
43883
|
+
return filterNames.map((name) => this.providers.get(name)).filter((p) => p !== undefined);
|
|
43884
|
+
}
|
|
43885
|
+
mergeResults(results, strategy, limit) {
|
|
43886
|
+
switch (strategy) {
|
|
43887
|
+
case "score":
|
|
43888
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
43889
|
+
case "round-robin":
|
|
43890
|
+
return this.roundRobinMerge(results, limit);
|
|
43891
|
+
case "provider-priority":
|
|
43892
|
+
return this.priorityMerge(results, limit);
|
|
43893
|
+
default:
|
|
43894
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
43895
|
+
}
|
|
43896
|
+
}
|
|
43897
|
+
roundRobinMerge(results, limit) {
|
|
43898
|
+
const byProvider = new Map;
|
|
43899
|
+
for (const result of results) {
|
|
43900
|
+
const provider = result.item.provider;
|
|
43901
|
+
const existing = byProvider.get(provider) ?? [];
|
|
43902
|
+
existing.push(result);
|
|
43903
|
+
byProvider.set(provider, existing);
|
|
43904
|
+
}
|
|
43905
|
+
for (const [provider, items] of byProvider) {
|
|
43906
|
+
byProvider.set(provider, items.sort((a, b) => b.score - a.score));
|
|
43907
|
+
}
|
|
43908
|
+
const merged = [];
|
|
43909
|
+
const providers = Array.from(byProvider.keys());
|
|
43910
|
+
let index = 0;
|
|
43911
|
+
while (merged.length < limit) {
|
|
43912
|
+
let added = false;
|
|
43913
|
+
for (const provider of providers) {
|
|
43914
|
+
const items = byProvider.get(provider);
|
|
43915
|
+
if (index < items.length) {
|
|
43916
|
+
merged.push(items[index]);
|
|
43917
|
+
added = true;
|
|
43918
|
+
if (merged.length >= limit)
|
|
43919
|
+
break;
|
|
43920
|
+
}
|
|
43921
|
+
}
|
|
43922
|
+
if (!added)
|
|
43923
|
+
break;
|
|
43924
|
+
index++;
|
|
43684
43925
|
}
|
|
43685
|
-
return
|
|
43926
|
+
return merged;
|
|
43686
43927
|
}
|
|
43687
|
-
|
|
43688
|
-
const
|
|
43689
|
-
|
|
43690
|
-
|
|
43691
|
-
|
|
43692
|
-
const
|
|
43693
|
-
|
|
43928
|
+
priorityMerge(results, limit) {
|
|
43929
|
+
const priority = this.config.providerPriority ?? [];
|
|
43930
|
+
return results.sort((a, b) => {
|
|
43931
|
+
const aPriority = priority.indexOf(a.item.provider);
|
|
43932
|
+
const bPriority = priority.indexOf(b.item.provider);
|
|
43933
|
+
const aOrder = aPriority === -1 ? Infinity : aPriority;
|
|
43934
|
+
const bOrder = bPriority === -1 ? Infinity : bPriority;
|
|
43935
|
+
if (aOrder !== bOrder) {
|
|
43936
|
+
return aOrder - bOrder;
|
|
43937
|
+
}
|
|
43938
|
+
return b.score - a.score;
|
|
43939
|
+
}).slice(0, limit);
|
|
43940
|
+
}
|
|
43941
|
+
deduplicateResults(results) {
|
|
43942
|
+
const seen = new Set;
|
|
43943
|
+
const deduplicated = [];
|
|
43944
|
+
for (const result of results) {
|
|
43945
|
+
const hash2 = this.contentHash(result.item);
|
|
43946
|
+
if (!seen.has(hash2)) {
|
|
43947
|
+
seen.add(hash2);
|
|
43948
|
+
deduplicated.push(result);
|
|
43949
|
+
}
|
|
43950
|
+
}
|
|
43951
|
+
return deduplicated;
|
|
43952
|
+
}
|
|
43953
|
+
contentHash(item) {
|
|
43954
|
+
const content = item.content ?? item.summary;
|
|
43955
|
+
return `${item.title}:${content.slice(0, 200)}`;
|
|
43956
|
+
}
|
|
43957
|
+
parseId(id) {
|
|
43958
|
+
const separator = "::";
|
|
43959
|
+
const index = id.indexOf(separator);
|
|
43960
|
+
if (index === -1) {
|
|
43961
|
+
return [null, id];
|
|
43694
43962
|
}
|
|
43695
|
-
return
|
|
43963
|
+
return [id.slice(0, index), id.slice(index + separator.length)];
|
|
43696
43964
|
}
|
|
43697
|
-
|
|
43698
|
-
|
|
43699
|
-
|
|
43700
|
-
|
|
43965
|
+
}
|
|
43966
|
+
// src/features/knowledge-provider/providers/local.ts
|
|
43967
|
+
var DEFAULT_CONFIG3 = {
|
|
43968
|
+
enabled: true,
|
|
43969
|
+
rootDir: ".opencode/knowledge"
|
|
43970
|
+
};
|
|
43971
|
+
|
|
43972
|
+
class LocalKnowledgeProvider {
|
|
43973
|
+
name = "local";
|
|
43974
|
+
type = "local";
|
|
43975
|
+
description = "Local file-based knowledge repository";
|
|
43976
|
+
config;
|
|
43977
|
+
repository = null;
|
|
43978
|
+
initialized = false;
|
|
43979
|
+
constructor(config3) {
|
|
43980
|
+
this.config = { ...DEFAULT_CONFIG3, ...config3 };
|
|
43981
|
+
}
|
|
43982
|
+
async initialize() {
|
|
43983
|
+
if (!this.config.enabled) {
|
|
43984
|
+
return;
|
|
43985
|
+
}
|
|
43986
|
+
this.repository = new KnowledgeRepository({
|
|
43987
|
+
rootDir: this.config.rootDir ?? DEFAULT_CONFIG3.rootDir
|
|
43988
|
+
});
|
|
43989
|
+
await this.repository.initialize();
|
|
43990
|
+
this.initialized = true;
|
|
43991
|
+
}
|
|
43992
|
+
async search(query) {
|
|
43993
|
+
if (!this.initialized || !this.repository) {
|
|
43994
|
+
return [];
|
|
43995
|
+
}
|
|
43996
|
+
const filter = {
|
|
43997
|
+
search: query.text,
|
|
43998
|
+
limit: query.limit ?? 10
|
|
43999
|
+
};
|
|
44000
|
+
if (query.layers && query.layers.length > 0) {
|
|
44001
|
+
const validLayers = query.layers.filter(this.isKnowledgeLayer);
|
|
44002
|
+
if (validLayers.length > 0) {
|
|
44003
|
+
filter.layer = validLayers;
|
|
43701
44004
|
}
|
|
43702
44005
|
}
|
|
43703
|
-
|
|
44006
|
+
if (query.types && query.types.length > 0) {
|
|
44007
|
+
const validTypes = query.types.filter(this.isKnowledgeType);
|
|
44008
|
+
if (validTypes.length > 0) {
|
|
44009
|
+
filter.type = validTypes;
|
|
44010
|
+
}
|
|
44011
|
+
}
|
|
44012
|
+
const result = await this.repository.query(filter);
|
|
44013
|
+
return result.items.map((commit) => ({
|
|
44014
|
+
item: this.commitToItem(commit, query.includeContent),
|
|
44015
|
+
score: this.calculateScore(commit, query.text)
|
|
44016
|
+
}));
|
|
43704
44017
|
}
|
|
43705
|
-
async
|
|
44018
|
+
async getById(id) {
|
|
44019
|
+
if (!this.initialized || !this.repository) {
|
|
44020
|
+
return null;
|
|
44021
|
+
}
|
|
44022
|
+
const commit = await this.repository.getCommitById(id);
|
|
44023
|
+
if (!commit) {
|
|
44024
|
+
return null;
|
|
44025
|
+
}
|
|
44026
|
+
return this.commitToItem(commit, true);
|
|
44027
|
+
}
|
|
44028
|
+
async healthCheck() {
|
|
44029
|
+
const now = new Date().toISOString();
|
|
44030
|
+
if (!this.config.enabled) {
|
|
44031
|
+
return {
|
|
44032
|
+
status: "unavailable",
|
|
44033
|
+
message: "Provider is disabled",
|
|
44034
|
+
lastChecked: now
|
|
44035
|
+
};
|
|
44036
|
+
}
|
|
44037
|
+
if (!this.initialized || !this.repository) {
|
|
44038
|
+
return {
|
|
44039
|
+
status: "unavailable",
|
|
44040
|
+
message: "Provider not initialized",
|
|
44041
|
+
lastChecked: now
|
|
44042
|
+
};
|
|
44043
|
+
}
|
|
43706
44044
|
try {
|
|
43707
|
-
const
|
|
43708
|
-
|
|
43709
|
-
|
|
43710
|
-
|
|
43711
|
-
|
|
43712
|
-
|
|
43713
|
-
|
|
43714
|
-
|
|
43715
|
-
} catch {
|
|
43716
|
-
return
|
|
44045
|
+
const start = Date.now();
|
|
44046
|
+
await this.repository.getStats();
|
|
44047
|
+
const latencyMs = Date.now() - start;
|
|
44048
|
+
return {
|
|
44049
|
+
status: "healthy",
|
|
44050
|
+
latencyMs,
|
|
44051
|
+
lastChecked: now
|
|
44052
|
+
};
|
|
44053
|
+
} catch (error45) {
|
|
44054
|
+
return {
|
|
44055
|
+
status: "degraded",
|
|
44056
|
+
message: error45 instanceof Error ? error45.message : "Unknown error",
|
|
44057
|
+
lastChecked: now
|
|
44058
|
+
};
|
|
43717
44059
|
}
|
|
43718
44060
|
}
|
|
43719
|
-
|
|
43720
|
-
|
|
43721
|
-
|
|
43722
|
-
|
|
43723
|
-
|
|
43724
|
-
|
|
43725
|
-
|
|
43726
|
-
|
|
43727
|
-
|
|
43728
|
-
|
|
43729
|
-
|
|
43730
|
-
|
|
43731
|
-
|
|
43732
|
-
|
|
43733
|
-
|
|
43734
|
-
|
|
43735
|
-
|
|
43736
|
-
|
|
44061
|
+
commitToItem(commit, includeContent) {
|
|
44062
|
+
return {
|
|
44063
|
+
id: `local::${commit.id}`,
|
|
44064
|
+
provider: this.name,
|
|
44065
|
+
title: commit.title,
|
|
44066
|
+
summary: commit.summary,
|
|
44067
|
+
content: includeContent ? commit.content : undefined,
|
|
44068
|
+
type: commit.type,
|
|
44069
|
+
layer: commit.layer,
|
|
44070
|
+
metadata: {
|
|
44071
|
+
sourceType: "local",
|
|
44072
|
+
sourceId: commit.id,
|
|
44073
|
+
tags: commit.tags,
|
|
44074
|
+
severity: commit.severity,
|
|
44075
|
+
knowledgeLayer: commit.layer,
|
|
44076
|
+
extra: {
|
|
44077
|
+
constraints: commit.constraints,
|
|
44078
|
+
triggerKeywords: commit.triggerKeywords,
|
|
44079
|
+
author: commit.author
|
|
43737
44080
|
}
|
|
43738
|
-
|
|
43739
|
-
|
|
43740
|
-
|
|
43741
|
-
|
|
44081
|
+
},
|
|
44082
|
+
createdAt: commit.createdAt
|
|
44083
|
+
};
|
|
44084
|
+
}
|
|
44085
|
+
calculateScore(commit, searchText) {
|
|
44086
|
+
if (!searchText) {
|
|
44087
|
+
return 0.5;
|
|
44088
|
+
}
|
|
44089
|
+
const lowerSearch = searchText.toLowerCase();
|
|
44090
|
+
const titleMatch = commit.title.toLowerCase().includes(lowerSearch);
|
|
44091
|
+
const summaryMatch = commit.summary.toLowerCase().includes(lowerSearch);
|
|
44092
|
+
const contentMatch = commit.content.toLowerCase().includes(lowerSearch);
|
|
44093
|
+
const tagMatch = commit.tags.some((t) => t.toLowerCase().includes(lowerSearch));
|
|
44094
|
+
const keywordMatch = commit.triggerKeywords.some((k) => k.toLowerCase().includes(lowerSearch));
|
|
44095
|
+
let score = 0.3;
|
|
44096
|
+
if (titleMatch)
|
|
44097
|
+
score += 0.3;
|
|
44098
|
+
if (summaryMatch)
|
|
44099
|
+
score += 0.15;
|
|
44100
|
+
if (contentMatch)
|
|
44101
|
+
score += 0.1;
|
|
44102
|
+
if (tagMatch)
|
|
44103
|
+
score += 0.1;
|
|
44104
|
+
if (keywordMatch)
|
|
44105
|
+
score += 0.05;
|
|
44106
|
+
return Math.min(score, 1);
|
|
44107
|
+
}
|
|
44108
|
+
isKnowledgeType(type2) {
|
|
44109
|
+
return ["adr", "policy", "pattern", "spec"].includes(type2);
|
|
44110
|
+
}
|
|
44111
|
+
isKnowledgeLayer(layer) {
|
|
44112
|
+
return ["company", "org", "team", "project"].includes(layer);
|
|
44113
|
+
}
|
|
44114
|
+
}
|
|
44115
|
+
// src/features/knowledge-provider/providers/mem0.ts
|
|
44116
|
+
var DEFAULT_SEARCH_LAYERS = [
|
|
44117
|
+
"user",
|
|
44118
|
+
"project",
|
|
44119
|
+
"team",
|
|
44120
|
+
"org",
|
|
44121
|
+
"company"
|
|
44122
|
+
];
|
|
44123
|
+
|
|
44124
|
+
class Mem0KnowledgeProvider {
|
|
44125
|
+
mem0Config;
|
|
44126
|
+
name = "mem0";
|
|
44127
|
+
type = "mem0";
|
|
44128
|
+
description = "Vector-based semantic memory via Mem0";
|
|
44129
|
+
config;
|
|
44130
|
+
adapter = null;
|
|
44131
|
+
initialized = false;
|
|
44132
|
+
constructor(config3, mem0Config) {
|
|
44133
|
+
this.mem0Config = mem0Config;
|
|
44134
|
+
this.config = config3;
|
|
44135
|
+
}
|
|
44136
|
+
async initialize() {
|
|
44137
|
+
if (!this.config.enabled || !this.mem0Config.enabled) {
|
|
44138
|
+
return;
|
|
43742
44139
|
}
|
|
43743
|
-
|
|
43744
|
-
|
|
43745
|
-
|
|
43746
|
-
|
|
43747
|
-
|
|
43748
|
-
|
|
43749
|
-
return;
|
|
43750
|
-
this.checkSessionTodos(sessionID).then((hasIncompleteTodos2) => {
|
|
43751
|
-
if (hasIncompleteTodos2) {
|
|
43752
|
-
log("[background-agent] Task has incomplete todos, waiting for todo-continuation:", task.id);
|
|
43753
|
-
return;
|
|
43754
|
-
}
|
|
43755
|
-
task.status = "completed";
|
|
43756
|
-
task.completedAt = new Date;
|
|
43757
|
-
this.markForNotification(task);
|
|
43758
|
-
this.notifyParentSession(task);
|
|
43759
|
-
log("[background-agent] Task completed via session.idle event:", task.id);
|
|
43760
|
-
});
|
|
44140
|
+
this.adapter = new Mem0Adapter(this.mem0Config);
|
|
44141
|
+
this.initialized = true;
|
|
44142
|
+
}
|
|
44143
|
+
async search(query) {
|
|
44144
|
+
if (!this.initialized || !this.adapter) {
|
|
44145
|
+
return [];
|
|
43761
44146
|
}
|
|
43762
|
-
|
|
43763
|
-
|
|
43764
|
-
|
|
43765
|
-
|
|
43766
|
-
|
|
43767
|
-
|
|
43768
|
-
|
|
43769
|
-
|
|
43770
|
-
|
|
43771
|
-
|
|
43772
|
-
|
|
43773
|
-
|
|
44147
|
+
const layers = this.resolveSearchLayers(query.layers);
|
|
44148
|
+
const results = await this.adapter.search({
|
|
44149
|
+
query: query.text,
|
|
44150
|
+
layer: layers,
|
|
44151
|
+
limit: query.limit ?? 10,
|
|
44152
|
+
threshold: query.threshold ?? 0.5
|
|
44153
|
+
});
|
|
44154
|
+
return results.map((result) => ({
|
|
44155
|
+
item: this.memoryToItem(result.memory, query.includeContent),
|
|
44156
|
+
score: result.score
|
|
44157
|
+
}));
|
|
44158
|
+
}
|
|
44159
|
+
async getById(id) {
|
|
44160
|
+
if (!this.initialized || !this.adapter) {
|
|
44161
|
+
return null;
|
|
44162
|
+
}
|
|
44163
|
+
const memory = await this.adapter.get(id);
|
|
44164
|
+
if (!memory) {
|
|
44165
|
+
return null;
|
|
44166
|
+
}
|
|
44167
|
+
return this.memoryToItem(memory, true);
|
|
44168
|
+
}
|
|
44169
|
+
async index(item) {
|
|
44170
|
+
if (!this.initialized || !this.adapter) {
|
|
44171
|
+
return;
|
|
44172
|
+
}
|
|
44173
|
+
const layer = this.itemLayerToMemoryLayer(item.layer);
|
|
44174
|
+
if (!layer) {
|
|
44175
|
+
return;
|
|
44176
|
+
}
|
|
44177
|
+
await this.adapter.add({
|
|
44178
|
+
content: `${item.title}
|
|
44179
|
+
|
|
44180
|
+
${item.summary}
|
|
44181
|
+
|
|
44182
|
+
${item.content ?? ""}`,
|
|
44183
|
+
layer,
|
|
44184
|
+
metadata: {
|
|
44185
|
+
sourceProvider: item.provider,
|
|
44186
|
+
sourceId: item.metadata.sourceId,
|
|
44187
|
+
type: item.type,
|
|
44188
|
+
tags: item.metadata.tags
|
|
43774
44189
|
}
|
|
43775
|
-
|
|
43776
|
-
|
|
43777
|
-
|
|
44190
|
+
});
|
|
44191
|
+
}
|
|
44192
|
+
async healthCheck() {
|
|
44193
|
+
const now = new Date().toISOString();
|
|
44194
|
+
if (!this.config.enabled) {
|
|
44195
|
+
return {
|
|
44196
|
+
status: "unavailable",
|
|
44197
|
+
message: "Provider is disabled",
|
|
44198
|
+
lastChecked: now
|
|
44199
|
+
};
|
|
44200
|
+
}
|
|
44201
|
+
if (!this.initialized || !this.adapter) {
|
|
44202
|
+
return {
|
|
44203
|
+
status: "unavailable",
|
|
44204
|
+
message: "Provider not initialized",
|
|
44205
|
+
lastChecked: now
|
|
44206
|
+
};
|
|
44207
|
+
}
|
|
44208
|
+
try {
|
|
44209
|
+
const start = Date.now();
|
|
44210
|
+
await this.adapter.getStats();
|
|
44211
|
+
const latencyMs = Date.now() - start;
|
|
44212
|
+
return {
|
|
44213
|
+
status: "healthy",
|
|
44214
|
+
latencyMs,
|
|
44215
|
+
lastChecked: now
|
|
44216
|
+
};
|
|
44217
|
+
} catch (error45) {
|
|
44218
|
+
return {
|
|
44219
|
+
status: "degraded",
|
|
44220
|
+
message: error45 instanceof Error ? error45.message : "Unknown error",
|
|
44221
|
+
lastChecked: now
|
|
44222
|
+
};
|
|
43778
44223
|
}
|
|
43779
44224
|
}
|
|
43780
|
-
|
|
43781
|
-
const
|
|
43782
|
-
|
|
43783
|
-
|
|
44225
|
+
memoryToItem(memory, includeContent) {
|
|
44226
|
+
const metadata = memory.metadata ?? {};
|
|
44227
|
+
return {
|
|
44228
|
+
id: `mem0::${memory.id}`,
|
|
44229
|
+
provider: this.name,
|
|
44230
|
+
title: this.extractTitle(memory.content),
|
|
44231
|
+
summary: this.extractSummary(memory.content),
|
|
44232
|
+
content: includeContent ? memory.content : undefined,
|
|
44233
|
+
type: metadata.type ?? "memory",
|
|
44234
|
+
layer: memory.layer,
|
|
44235
|
+
metadata: {
|
|
44236
|
+
sourceType: "mem0",
|
|
44237
|
+
sourceId: memory.id,
|
|
44238
|
+
tags: metadata.tags,
|
|
44239
|
+
memoryLayer: memory.layer,
|
|
44240
|
+
extra: metadata
|
|
44241
|
+
},
|
|
44242
|
+
createdAt: memory.createdAt,
|
|
44243
|
+
updatedAt: memory.updatedAt
|
|
44244
|
+
};
|
|
43784
44245
|
}
|
|
43785
|
-
|
|
43786
|
-
|
|
44246
|
+
extractTitle(content) {
|
|
44247
|
+
const firstLine = content.split(`
|
|
44248
|
+
`)[0] ?? "";
|
|
44249
|
+
return firstLine.slice(0, 100).trim() || "Untitled Memory";
|
|
43787
44250
|
}
|
|
43788
|
-
|
|
43789
|
-
|
|
44251
|
+
extractSummary(content) {
|
|
44252
|
+
const lines = content.split(`
|
|
44253
|
+
`).filter((l) => l.trim());
|
|
44254
|
+
const summary = lines.slice(0, 2).join(" ");
|
|
44255
|
+
return summary.slice(0, 200).trim() || content.slice(0, 200).trim();
|
|
43790
44256
|
}
|
|
43791
|
-
|
|
43792
|
-
|
|
43793
|
-
|
|
43794
|
-
if (filtered.length === 0) {
|
|
43795
|
-
this.notifications.delete(sessionID);
|
|
43796
|
-
} else {
|
|
43797
|
-
this.notifications.set(sessionID, filtered);
|
|
43798
|
-
}
|
|
44257
|
+
resolveSearchLayers(queryLayers) {
|
|
44258
|
+
if (!queryLayers || queryLayers.length === 0) {
|
|
44259
|
+
return this.config.searchLayers ?? DEFAULT_SEARCH_LAYERS;
|
|
43799
44260
|
}
|
|
44261
|
+
return queryLayers.filter(this.isMemoryLayer);
|
|
43800
44262
|
}
|
|
43801
|
-
|
|
43802
|
-
if (
|
|
43803
|
-
return;
|
|
43804
|
-
|
|
43805
|
-
|
|
43806
|
-
|
|
43807
|
-
|
|
44263
|
+
itemLayerToMemoryLayer(layer) {
|
|
44264
|
+
if (!layer)
|
|
44265
|
+
return "project";
|
|
44266
|
+
const mapping = {
|
|
44267
|
+
company: "company",
|
|
44268
|
+
org: "org",
|
|
44269
|
+
team: "team",
|
|
44270
|
+
project: "project",
|
|
44271
|
+
user: "user",
|
|
44272
|
+
session: "session",
|
|
44273
|
+
agent: "agent"
|
|
44274
|
+
};
|
|
44275
|
+
return mapping[layer] ?? "project";
|
|
43808
44276
|
}
|
|
43809
|
-
|
|
43810
|
-
|
|
43811
|
-
|
|
43812
|
-
|
|
44277
|
+
isMemoryLayer(layer) {
|
|
44278
|
+
return ["user", "session", "project", "team", "org", "company", "agent"].includes(layer);
|
|
44279
|
+
}
|
|
44280
|
+
}
|
|
44281
|
+
// src/features/knowledge-provider/providers/mcp.ts
|
|
44282
|
+
class MCPKnowledgeProvider {
|
|
44283
|
+
name;
|
|
44284
|
+
type = "mcp";
|
|
44285
|
+
description;
|
|
44286
|
+
config;
|
|
44287
|
+
invoker;
|
|
44288
|
+
initialized = false;
|
|
44289
|
+
constructor(config3, invoker) {
|
|
44290
|
+
this.config = config3;
|
|
44291
|
+
this.invoker = invoker;
|
|
44292
|
+
this.name = `mcp:${config3.name}`;
|
|
44293
|
+
this.description = `MCP knowledge provider via ${config3.name}`;
|
|
44294
|
+
}
|
|
44295
|
+
async initialize() {
|
|
44296
|
+
const available = await this.invoker.isServerAvailable(this.config.name);
|
|
44297
|
+
if (!available) {
|
|
44298
|
+
throw new Error(`MCP server "${this.config.name}" is not available`);
|
|
43813
44299
|
}
|
|
44300
|
+
this.initialized = true;
|
|
43814
44301
|
}
|
|
43815
|
-
|
|
43816
|
-
this.
|
|
43817
|
-
|
|
43818
|
-
|
|
44302
|
+
async search(query) {
|
|
44303
|
+
if (!this.initialized) {
|
|
44304
|
+
return [];
|
|
44305
|
+
}
|
|
44306
|
+
try {
|
|
44307
|
+
const result = await this.invoker.invoke(this.config.name, this.config.searchTool, {
|
|
44308
|
+
query: query.text,
|
|
44309
|
+
limit: query.limit ?? 10,
|
|
44310
|
+
...this.config.config
|
|
44311
|
+
});
|
|
44312
|
+
return this.parseSearchResponse(result, query.includeContent);
|
|
44313
|
+
} catch {
|
|
44314
|
+
return [];
|
|
44315
|
+
}
|
|
43819
44316
|
}
|
|
43820
|
-
|
|
43821
|
-
|
|
43822
|
-
|
|
43823
|
-
const tuiClient = this.client;
|
|
43824
|
-
if (tuiClient.tui?.showToast) {
|
|
43825
|
-
tuiClient.tui.showToast({
|
|
43826
|
-
body: {
|
|
43827
|
-
title: "Background Task Completed",
|
|
43828
|
-
message: `Task "${task.description}" finished in ${duration3}.`,
|
|
43829
|
-
variant: "success",
|
|
43830
|
-
duration: 5000
|
|
43831
|
-
}
|
|
43832
|
-
}).catch(() => {});
|
|
44317
|
+
async getById(id) {
|
|
44318
|
+
if (!this.initialized || !this.config.getTool) {
|
|
44319
|
+
return null;
|
|
43833
44320
|
}
|
|
43834
|
-
|
|
43835
|
-
|
|
43836
|
-
|
|
43837
|
-
|
|
43838
|
-
|
|
43839
|
-
|
|
43840
|
-
|
|
43841
|
-
|
|
43842
|
-
|
|
43843
|
-
|
|
43844
|
-
|
|
43845
|
-
|
|
43846
|
-
|
|
43847
|
-
|
|
43848
|
-
|
|
43849
|
-
|
|
43850
|
-
|
|
43851
|
-
|
|
43852
|
-
|
|
43853
|
-
} catch (error45) {
|
|
43854
|
-
log("[background-agent] prompt failed:", String(error45));
|
|
43855
|
-
} finally {
|
|
43856
|
-
this.clearNotificationsForTask(taskId);
|
|
43857
|
-
this.tasks.delete(taskId);
|
|
43858
|
-
log("[background-agent] Removed completed task from memory:", taskId);
|
|
44321
|
+
try {
|
|
44322
|
+
const result = await this.invoker.invoke(this.config.name, this.config.getTool, { id });
|
|
44323
|
+
return this.parseGetResponse(result);
|
|
44324
|
+
} catch {
|
|
44325
|
+
return null;
|
|
44326
|
+
}
|
|
44327
|
+
}
|
|
44328
|
+
async healthCheck() {
|
|
44329
|
+
const now = new Date().toISOString();
|
|
44330
|
+
try {
|
|
44331
|
+
const start = Date.now();
|
|
44332
|
+
const available = await this.invoker.isServerAvailable(this.config.name);
|
|
44333
|
+
const latencyMs = Date.now() - start;
|
|
44334
|
+
if (!available) {
|
|
44335
|
+
return {
|
|
44336
|
+
status: "unavailable",
|
|
44337
|
+
message: `MCP server "${this.config.name}" is not available`,
|
|
44338
|
+
lastChecked: now
|
|
44339
|
+
};
|
|
43859
44340
|
}
|
|
43860
|
-
|
|
44341
|
+
return {
|
|
44342
|
+
status: "healthy",
|
|
44343
|
+
latencyMs,
|
|
44344
|
+
lastChecked: now
|
|
44345
|
+
};
|
|
44346
|
+
} catch (error45) {
|
|
44347
|
+
return {
|
|
44348
|
+
status: "degraded",
|
|
44349
|
+
message: error45 instanceof Error ? error45.message : "Unknown error",
|
|
44350
|
+
lastChecked: now
|
|
44351
|
+
};
|
|
44352
|
+
}
|
|
43861
44353
|
}
|
|
43862
|
-
|
|
43863
|
-
|
|
43864
|
-
|
|
43865
|
-
|
|
43866
|
-
|
|
43867
|
-
|
|
43868
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
43869
|
-
} else if (minutes > 0) {
|
|
43870
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
44354
|
+
async dispose() {
|
|
44355
|
+
this.initialized = false;
|
|
44356
|
+
}
|
|
44357
|
+
parseSearchResponse(response2, includeContent) {
|
|
44358
|
+
if (!response2) {
|
|
44359
|
+
return [];
|
|
43871
44360
|
}
|
|
43872
|
-
|
|
44361
|
+
if (Array.isArray(response2)) {
|
|
44362
|
+
return response2.map((item, index) => this.responseItemToSearchResult(item, index, includeContent));
|
|
44363
|
+
}
|
|
44364
|
+
const obj = response2;
|
|
44365
|
+
if (obj.results && Array.isArray(obj.results)) {
|
|
44366
|
+
return obj.results.map((item, index) => this.responseItemToSearchResult(item, index, includeContent));
|
|
44367
|
+
}
|
|
44368
|
+
if (obj.items && Array.isArray(obj.items)) {
|
|
44369
|
+
return obj.items.map((item, index) => this.responseItemToSearchResult(item, index, includeContent));
|
|
44370
|
+
}
|
|
44371
|
+
return [this.responseItemToSearchResult(response2, 0, includeContent)];
|
|
44372
|
+
}
|
|
44373
|
+
responseItemToSearchResult(item, index, includeContent) {
|
|
44374
|
+
const obj = item ?? {};
|
|
44375
|
+
const id = String(obj.id ?? obj.document_id ?? obj.url ?? `result-${index}`);
|
|
44376
|
+
const title = String(obj.title ?? obj.name ?? "Untitled");
|
|
44377
|
+
const summary = String(obj.summary ?? obj.snippet ?? obj.excerpt ?? obj.description ?? "");
|
|
44378
|
+
const content = obj.content ?? obj.text ?? obj.body;
|
|
44379
|
+
const score = Number(obj.score ?? obj.relevance ?? obj.similarity ?? 0.5);
|
|
44380
|
+
const url2 = obj.url ? String(obj.url) : undefined;
|
|
44381
|
+
const knowledgeItem = {
|
|
44382
|
+
id: `${this.name}::${id}`,
|
|
44383
|
+
provider: this.name,
|
|
44384
|
+
title,
|
|
44385
|
+
summary: summary.slice(0, 200),
|
|
44386
|
+
content: includeContent && content ? String(content) : undefined,
|
|
44387
|
+
type: this.inferType(obj),
|
|
44388
|
+
metadata: {
|
|
44389
|
+
sourceType: "mcp",
|
|
44390
|
+
sourceId: id,
|
|
44391
|
+
url: url2,
|
|
44392
|
+
extra: obj
|
|
44393
|
+
}
|
|
44394
|
+
};
|
|
44395
|
+
return {
|
|
44396
|
+
item: knowledgeItem,
|
|
44397
|
+
score: Math.min(Math.max(score, 0), 1),
|
|
44398
|
+
highlights: obj.highlights ? obj.highlights : obj.snippet ? [String(obj.snippet)] : undefined
|
|
44399
|
+
};
|
|
43873
44400
|
}
|
|
43874
|
-
|
|
43875
|
-
|
|
43876
|
-
|
|
43877
|
-
return true;
|
|
44401
|
+
parseGetResponse(response2) {
|
|
44402
|
+
if (!response2) {
|
|
44403
|
+
return null;
|
|
43878
44404
|
}
|
|
43879
|
-
|
|
44405
|
+
const obj = response2;
|
|
44406
|
+
const id = String(obj.id ?? obj.document_id ?? "unknown");
|
|
44407
|
+
const title = String(obj.title ?? obj.name ?? "Untitled");
|
|
44408
|
+
const content = String(obj.content ?? obj.text ?? obj.body ?? "");
|
|
44409
|
+
return {
|
|
44410
|
+
id: `${this.name}::${id}`,
|
|
44411
|
+
provider: this.name,
|
|
44412
|
+
title,
|
|
44413
|
+
summary: content.slice(0, 200),
|
|
44414
|
+
content,
|
|
44415
|
+
type: this.inferType(obj),
|
|
44416
|
+
metadata: {
|
|
44417
|
+
sourceType: "mcp",
|
|
44418
|
+
sourceId: id,
|
|
44419
|
+
url: obj.url ? String(obj.url) : undefined,
|
|
44420
|
+
extra: obj
|
|
44421
|
+
}
|
|
44422
|
+
};
|
|
43880
44423
|
}
|
|
43881
|
-
|
|
43882
|
-
const
|
|
43883
|
-
|
|
43884
|
-
const
|
|
43885
|
-
if (
|
|
43886
|
-
|
|
43887
|
-
task.status = "error";
|
|
43888
|
-
task.error = "Task timed out after 30 minutes";
|
|
43889
|
-
task.completedAt = new Date;
|
|
43890
|
-
this.clearNotificationsForTask(taskId);
|
|
43891
|
-
this.tasks.delete(taskId);
|
|
43892
|
-
subagentSessions.delete(task.sessionID);
|
|
44424
|
+
inferType(obj) {
|
|
44425
|
+
const typeField = obj.type ?? obj.kind ?? obj.category;
|
|
44426
|
+
if (typeof typeField === "string") {
|
|
44427
|
+
const lowerType = typeField.toLowerCase();
|
|
44428
|
+
if (["adr", "policy", "pattern", "spec"].includes(lowerType)) {
|
|
44429
|
+
return lowerType;
|
|
43893
44430
|
}
|
|
44431
|
+
if (lowerType.includes("doc"))
|
|
44432
|
+
return "document";
|
|
44433
|
+
if (lowerType.includes("article"))
|
|
44434
|
+
return "article";
|
|
44435
|
+
if (lowerType.includes("snippet") || lowerType.includes("code"))
|
|
44436
|
+
return "snippet";
|
|
43894
44437
|
}
|
|
43895
|
-
|
|
43896
|
-
|
|
43897
|
-
this.notifications.delete(sessionID);
|
|
43898
|
-
continue;
|
|
43899
|
-
}
|
|
43900
|
-
const validNotifications = notifications.filter((task) => {
|
|
43901
|
-
const age = now - task.startedAt.getTime();
|
|
43902
|
-
return age <= TASK_TTL_MS;
|
|
43903
|
-
});
|
|
43904
|
-
if (validNotifications.length === 0) {
|
|
43905
|
-
this.notifications.delete(sessionID);
|
|
43906
|
-
} else if (validNotifications.length !== notifications.length) {
|
|
43907
|
-
this.notifications.set(sessionID, validNotifications);
|
|
43908
|
-
}
|
|
44438
|
+
if (obj.url && String(obj.url).includes("github")) {
|
|
44439
|
+
return "snippet";
|
|
43909
44440
|
}
|
|
44441
|
+
return "document";
|
|
43910
44442
|
}
|
|
43911
|
-
|
|
43912
|
-
|
|
43913
|
-
|
|
43914
|
-
|
|
43915
|
-
|
|
43916
|
-
|
|
43917
|
-
|
|
44443
|
+
}
|
|
44444
|
+
// src/features/knowledge-provider/providers/notebooklm.ts
|
|
44445
|
+
class NotebookLMKnowledgeProvider {
|
|
44446
|
+
name = "notebooklm";
|
|
44447
|
+
type = "mcp";
|
|
44448
|
+
description = "Google NotebookLM AI-powered knowledge notebooks";
|
|
44449
|
+
config;
|
|
44450
|
+
invoker;
|
|
44451
|
+
initialized = false;
|
|
44452
|
+
notebooks = new Map;
|
|
44453
|
+
constructor(config3, invoker) {
|
|
44454
|
+
this.config = config3;
|
|
44455
|
+
this.invoker = invoker;
|
|
44456
|
+
}
|
|
44457
|
+
async initialize() {
|
|
44458
|
+
if (!this.config.enabled) {
|
|
44459
|
+
return;
|
|
44460
|
+
}
|
|
44461
|
+
const available = await this.invoker.isServerAvailable("notebooklm-mcp");
|
|
44462
|
+
if (!available) {
|
|
44463
|
+
throw new Error("NotebookLM MCP server is not available");
|
|
44464
|
+
}
|
|
44465
|
+
await this.refreshNotebooks();
|
|
44466
|
+
this.initialized = true;
|
|
44467
|
+
}
|
|
44468
|
+
async search(query) {
|
|
44469
|
+
if (!this.initialized) {
|
|
44470
|
+
return [];
|
|
44471
|
+
}
|
|
44472
|
+
const results = [];
|
|
44473
|
+
const notebookIds = this.getTargetNotebooks(query);
|
|
44474
|
+
for (const notebookId of notebookIds) {
|
|
43918
44475
|
try {
|
|
43919
|
-
const
|
|
43920
|
-
|
|
43921
|
-
|
|
43922
|
-
continue;
|
|
43923
|
-
}
|
|
43924
|
-
if (sessionStatus.type === "idle") {
|
|
43925
|
-
const hasIncompleteTodos2 = await this.checkSessionTodos(task.sessionID);
|
|
43926
|
-
if (hasIncompleteTodos2) {
|
|
43927
|
-
log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
|
|
43928
|
-
continue;
|
|
43929
|
-
}
|
|
43930
|
-
task.status = "completed";
|
|
43931
|
-
task.completedAt = new Date;
|
|
43932
|
-
this.markForNotification(task);
|
|
43933
|
-
this.notifyParentSession(task);
|
|
43934
|
-
log("[background-agent] Task completed via polling:", task.id);
|
|
43935
|
-
continue;
|
|
43936
|
-
}
|
|
43937
|
-
const messagesResult = await this.client.session.messages({
|
|
43938
|
-
path: { id: task.sessionID }
|
|
44476
|
+
const response2 = await this.invoker.invoke("notebooklm-mcp", "notebook_query", {
|
|
44477
|
+
notebook_id: notebookId,
|
|
44478
|
+
query: query.text
|
|
43939
44479
|
});
|
|
43940
|
-
if (
|
|
43941
|
-
const
|
|
43942
|
-
|
|
43943
|
-
|
|
43944
|
-
|
|
43945
|
-
|
|
43946
|
-
|
|
43947
|
-
|
|
43948
|
-
|
|
43949
|
-
|
|
43950
|
-
|
|
43951
|
-
|
|
43952
|
-
|
|
43953
|
-
|
|
43954
|
-
|
|
44480
|
+
if (response2?.answer) {
|
|
44481
|
+
const notebook = this.notebooks.get(notebookId);
|
|
44482
|
+
results.push({
|
|
44483
|
+
item: {
|
|
44484
|
+
id: `notebooklm::${notebookId}::query`,
|
|
44485
|
+
provider: this.name,
|
|
44486
|
+
title: `Answer from "${notebook?.title ?? notebookId}"`,
|
|
44487
|
+
summary: response2.answer.slice(0, 200),
|
|
44488
|
+
content: query.includeContent ? response2.answer : undefined,
|
|
44489
|
+
type: "document",
|
|
44490
|
+
metadata: {
|
|
44491
|
+
sourceType: "mcp",
|
|
44492
|
+
sourceId: notebookId,
|
|
44493
|
+
extra: {
|
|
44494
|
+
notebookId,
|
|
44495
|
+
notebookTitle: notebook?.title,
|
|
44496
|
+
citedSources: response2.sources
|
|
44497
|
+
}
|
|
43955
44498
|
}
|
|
43956
|
-
}
|
|
43957
|
-
|
|
43958
|
-
|
|
43959
|
-
|
|
44499
|
+
},
|
|
44500
|
+
score: 0.9,
|
|
44501
|
+
highlights: response2.sources?.map((s) => s.snippet).filter(Boolean)
|
|
44502
|
+
});
|
|
44503
|
+
}
|
|
44504
|
+
} catch {
|
|
44505
|
+
continue;
|
|
44506
|
+
}
|
|
44507
|
+
}
|
|
44508
|
+
return results.slice(0, query.limit ?? 10);
|
|
44509
|
+
}
|
|
44510
|
+
async getById(id) {
|
|
44511
|
+
if (!this.initialized) {
|
|
44512
|
+
return null;
|
|
44513
|
+
}
|
|
44514
|
+
const parts = id.split("::");
|
|
44515
|
+
if (parts.length < 2) {
|
|
44516
|
+
return null;
|
|
44517
|
+
}
|
|
44518
|
+
const notebookId = parts[0];
|
|
44519
|
+
const itemType = parts[1];
|
|
44520
|
+
try {
|
|
44521
|
+
if (itemType === "notebook") {
|
|
44522
|
+
const response2 = await this.invoker.invoke("notebooklm-mcp", "notebook_describe", { notebook_id: notebookId });
|
|
44523
|
+
const notebook = this.notebooks.get(notebookId);
|
|
44524
|
+
return {
|
|
44525
|
+
id: `notebooklm::${notebookId}::notebook`,
|
|
44526
|
+
provider: this.name,
|
|
44527
|
+
title: notebook?.title ?? "Notebook",
|
|
44528
|
+
summary: response2.description?.slice(0, 200) ?? "",
|
|
44529
|
+
content: response2.description,
|
|
44530
|
+
type: "document",
|
|
44531
|
+
metadata: {
|
|
44532
|
+
sourceType: "mcp",
|
|
44533
|
+
sourceId: notebookId,
|
|
44534
|
+
tags: response2.keywords,
|
|
44535
|
+
extra: { notebookId }
|
|
43960
44536
|
}
|
|
43961
|
-
|
|
43962
|
-
|
|
43963
|
-
|
|
43964
|
-
|
|
43965
|
-
|
|
43966
|
-
|
|
44537
|
+
};
|
|
44538
|
+
}
|
|
44539
|
+
if (itemType === "source") {
|
|
44540
|
+
const sourceId = parts[2];
|
|
44541
|
+
const response2 = await this.invoker.invoke("notebooklm-mcp", "source_describe", { notebook_id: notebookId, source_id: sourceId });
|
|
44542
|
+
return {
|
|
44543
|
+
id: `notebooklm::${notebookId}::source::${sourceId}`,
|
|
44544
|
+
provider: this.name,
|
|
44545
|
+
title: `Source ${sourceId}`,
|
|
44546
|
+
summary: response2.summary?.slice(0, 200) ?? "",
|
|
44547
|
+
content: response2.summary,
|
|
44548
|
+
type: "document",
|
|
44549
|
+
metadata: {
|
|
44550
|
+
sourceType: "mcp",
|
|
44551
|
+
sourceId: `${notebookId}/${sourceId}`,
|
|
44552
|
+
tags: response2.keywords,
|
|
44553
|
+
extra: { notebookId, sourceId }
|
|
43967
44554
|
}
|
|
43968
|
-
}
|
|
43969
|
-
} catch (error45) {
|
|
43970
|
-
log("[background-agent] Poll error for task:", { taskId: task.id, error: error45 });
|
|
44555
|
+
};
|
|
43971
44556
|
}
|
|
44557
|
+
} catch {
|
|
44558
|
+
return null;
|
|
43972
44559
|
}
|
|
43973
|
-
|
|
43974
|
-
|
|
44560
|
+
return null;
|
|
44561
|
+
}
|
|
44562
|
+
async index(item) {
|
|
44563
|
+
if (!this.initialized) {
|
|
44564
|
+
return;
|
|
44565
|
+
}
|
|
44566
|
+
const notebookId = this.config.defaultNotebookId;
|
|
44567
|
+
if (!notebookId) {
|
|
44568
|
+
return;
|
|
44569
|
+
}
|
|
44570
|
+
try {
|
|
44571
|
+
if (item.metadata.url) {
|
|
44572
|
+
await this.invoker.invoke("notebooklm-mcp", "notebook_add_url", {
|
|
44573
|
+
notebook_id: notebookId,
|
|
44574
|
+
url: item.metadata.url
|
|
44575
|
+
});
|
|
44576
|
+
} else if (item.content) {
|
|
44577
|
+
await this.invoker.invoke("notebooklm-mcp", "notebook_add_text", {
|
|
44578
|
+
notebook_id: notebookId,
|
|
44579
|
+
text: `# ${item.title}
|
|
44580
|
+
|
|
44581
|
+
${item.content}`,
|
|
44582
|
+
title: item.title
|
|
44583
|
+
});
|
|
44584
|
+
}
|
|
44585
|
+
} catch {}
|
|
44586
|
+
}
|
|
44587
|
+
async healthCheck() {
|
|
44588
|
+
const now = new Date().toISOString();
|
|
44589
|
+
if (!this.config.enabled) {
|
|
44590
|
+
return {
|
|
44591
|
+
status: "unavailable",
|
|
44592
|
+
message: "Provider is disabled",
|
|
44593
|
+
lastChecked: now
|
|
44594
|
+
};
|
|
44595
|
+
}
|
|
44596
|
+
try {
|
|
44597
|
+
const start = Date.now();
|
|
44598
|
+
const available = await this.invoker.isServerAvailable("notebooklm-mcp");
|
|
44599
|
+
const latencyMs = Date.now() - start;
|
|
44600
|
+
if (!available) {
|
|
44601
|
+
return {
|
|
44602
|
+
status: "unavailable",
|
|
44603
|
+
message: "NotebookLM MCP server is not available",
|
|
44604
|
+
lastChecked: now
|
|
44605
|
+
};
|
|
44606
|
+
}
|
|
44607
|
+
return {
|
|
44608
|
+
status: "healthy",
|
|
44609
|
+
latencyMs,
|
|
44610
|
+
message: `${this.notebooks.size} notebooks cached`,
|
|
44611
|
+
lastChecked: now
|
|
44612
|
+
};
|
|
44613
|
+
} catch (error45) {
|
|
44614
|
+
return {
|
|
44615
|
+
status: "degraded",
|
|
44616
|
+
message: error45 instanceof Error ? error45.message : "Unknown error",
|
|
44617
|
+
lastChecked: now
|
|
44618
|
+
};
|
|
44619
|
+
}
|
|
44620
|
+
}
|
|
44621
|
+
async dispose() {
|
|
44622
|
+
this.initialized = false;
|
|
44623
|
+
this.notebooks.clear();
|
|
44624
|
+
}
|
|
44625
|
+
async listNotebooks() {
|
|
44626
|
+
await this.refreshNotebooks();
|
|
44627
|
+
return Array.from(this.notebooks.values());
|
|
44628
|
+
}
|
|
44629
|
+
async refreshNotebooks() {
|
|
44630
|
+
try {
|
|
44631
|
+
const response2 = await this.invoker.invoke("notebooklm-mcp", "notebook_list", {});
|
|
44632
|
+
this.notebooks.clear();
|
|
44633
|
+
if (response2?.notebooks) {
|
|
44634
|
+
for (const nb of response2.notebooks) {
|
|
44635
|
+
this.notebooks.set(nb.id, nb);
|
|
44636
|
+
}
|
|
44637
|
+
}
|
|
44638
|
+
} catch {}
|
|
44639
|
+
}
|
|
44640
|
+
getTargetNotebooks(query) {
|
|
44641
|
+
if (this.config.defaultNotebookId) {
|
|
44642
|
+
return [this.config.defaultNotebookId];
|
|
43975
44643
|
}
|
|
44644
|
+
const notebookIds = Array.from(this.notebooks.keys());
|
|
44645
|
+
return notebookIds.slice(0, 3);
|
|
43976
44646
|
}
|
|
43977
44647
|
}
|
|
43978
44648
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
@@ -46274,265 +46944,2213 @@ class Client extends Protocol {
|
|
|
46274
46944
|
if (taskSupport === "required" || taskSupport === "optional") {
|
|
46275
46945
|
this._cachedKnownTaskTools.add(tool3.name);
|
|
46276
46946
|
}
|
|
46277
|
-
if (taskSupport === "required") {
|
|
46278
|
-
this._cachedRequiredTaskTools.add(tool3.name);
|
|
46947
|
+
if (taskSupport === "required") {
|
|
46948
|
+
this._cachedRequiredTaskTools.add(tool3.name);
|
|
46949
|
+
}
|
|
46950
|
+
}
|
|
46951
|
+
}
|
|
46952
|
+
getToolOutputValidator(toolName) {
|
|
46953
|
+
return this._cachedToolOutputValidators.get(toolName);
|
|
46954
|
+
}
|
|
46955
|
+
async listTools(params, options) {
|
|
46956
|
+
const result = await this.request({ method: "tools/list", params }, ListToolsResultSchema, options);
|
|
46957
|
+
this.cacheToolMetadata(result.tools);
|
|
46958
|
+
return result;
|
|
46959
|
+
}
|
|
46960
|
+
_setupListChangedHandler(listType, notificationSchema, options, fetcher) {
|
|
46961
|
+
const parseResult = ListChangedOptionsBaseSchema.safeParse(options);
|
|
46962
|
+
if (!parseResult.success) {
|
|
46963
|
+
throw new Error(`Invalid ${listType} listChanged options: ${parseResult.error.message}`);
|
|
46964
|
+
}
|
|
46965
|
+
if (typeof options.onChanged !== "function") {
|
|
46966
|
+
throw new Error(`Invalid ${listType} listChanged options: onChanged must be a function`);
|
|
46967
|
+
}
|
|
46968
|
+
const { autoRefresh, debounceMs } = parseResult.data;
|
|
46969
|
+
const { onChanged } = options;
|
|
46970
|
+
const refresh = async () => {
|
|
46971
|
+
if (!autoRefresh) {
|
|
46972
|
+
onChanged(null, null);
|
|
46973
|
+
return;
|
|
46974
|
+
}
|
|
46975
|
+
try {
|
|
46976
|
+
const items = await fetcher();
|
|
46977
|
+
onChanged(null, items);
|
|
46978
|
+
} catch (e) {
|
|
46979
|
+
const error45 = e instanceof Error ? e : new Error(String(e));
|
|
46980
|
+
onChanged(error45, null);
|
|
46981
|
+
}
|
|
46982
|
+
};
|
|
46983
|
+
const handler = () => {
|
|
46984
|
+
if (debounceMs) {
|
|
46985
|
+
const existingTimer = this._listChangedDebounceTimers.get(listType);
|
|
46986
|
+
if (existingTimer) {
|
|
46987
|
+
clearTimeout(existingTimer);
|
|
46988
|
+
}
|
|
46989
|
+
const timer = setTimeout(refresh, debounceMs);
|
|
46990
|
+
this._listChangedDebounceTimers.set(listType, timer);
|
|
46991
|
+
} else {
|
|
46992
|
+
refresh();
|
|
46993
|
+
}
|
|
46994
|
+
};
|
|
46995
|
+
this.setNotificationHandler(notificationSchema, handler);
|
|
46996
|
+
}
|
|
46997
|
+
async sendRootsListChanged() {
|
|
46998
|
+
return this.notification({ method: "notifications/roots/list_changed" });
|
|
46999
|
+
}
|
|
47000
|
+
}
|
|
47001
|
+
|
|
47002
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js
|
|
47003
|
+
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
47004
|
+
import process2 from "process";
|
|
47005
|
+
import { PassThrough } from "stream";
|
|
47006
|
+
|
|
47007
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
47008
|
+
class ReadBuffer {
|
|
47009
|
+
append(chunk) {
|
|
47010
|
+
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
47011
|
+
}
|
|
47012
|
+
readMessage() {
|
|
47013
|
+
if (!this._buffer) {
|
|
47014
|
+
return null;
|
|
47015
|
+
}
|
|
47016
|
+
const index = this._buffer.indexOf(`
|
|
47017
|
+
`);
|
|
47018
|
+
if (index === -1) {
|
|
47019
|
+
return null;
|
|
47020
|
+
}
|
|
47021
|
+
const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
|
|
47022
|
+
this._buffer = this._buffer.subarray(index + 1);
|
|
47023
|
+
return deserializeMessage(line);
|
|
47024
|
+
}
|
|
47025
|
+
clear() {
|
|
47026
|
+
this._buffer = undefined;
|
|
47027
|
+
}
|
|
47028
|
+
}
|
|
47029
|
+
function deserializeMessage(line) {
|
|
47030
|
+
return JSONRPCMessageSchema.parse(JSON.parse(line));
|
|
47031
|
+
}
|
|
47032
|
+
function serializeMessage(message) {
|
|
47033
|
+
return JSON.stringify(message) + `
|
|
47034
|
+
`;
|
|
47035
|
+
}
|
|
47036
|
+
|
|
47037
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js
|
|
47038
|
+
var DEFAULT_INHERITED_ENV_VARS = process2.platform === "win32" ? [
|
|
47039
|
+
"APPDATA",
|
|
47040
|
+
"HOMEDRIVE",
|
|
47041
|
+
"HOMEPATH",
|
|
47042
|
+
"LOCALAPPDATA",
|
|
47043
|
+
"PATH",
|
|
47044
|
+
"PROCESSOR_ARCHITECTURE",
|
|
47045
|
+
"SYSTEMDRIVE",
|
|
47046
|
+
"SYSTEMROOT",
|
|
47047
|
+
"TEMP",
|
|
47048
|
+
"USERNAME",
|
|
47049
|
+
"USERPROFILE",
|
|
47050
|
+
"PROGRAMFILES"
|
|
47051
|
+
] : ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"];
|
|
47052
|
+
function getDefaultEnvironment() {
|
|
47053
|
+
const env = {};
|
|
47054
|
+
for (const key of DEFAULT_INHERITED_ENV_VARS) {
|
|
47055
|
+
const value = process2.env[key];
|
|
47056
|
+
if (value === undefined) {
|
|
47057
|
+
continue;
|
|
47058
|
+
}
|
|
47059
|
+
if (value.startsWith("()")) {
|
|
47060
|
+
continue;
|
|
47061
|
+
}
|
|
47062
|
+
env[key] = value;
|
|
47063
|
+
}
|
|
47064
|
+
return env;
|
|
47065
|
+
}
|
|
47066
|
+
|
|
47067
|
+
class StdioClientTransport {
|
|
47068
|
+
constructor(server) {
|
|
47069
|
+
this._readBuffer = new ReadBuffer;
|
|
47070
|
+
this._stderrStream = null;
|
|
47071
|
+
this._serverParams = server;
|
|
47072
|
+
if (server.stderr === "pipe" || server.stderr === "overlapped") {
|
|
47073
|
+
this._stderrStream = new PassThrough;
|
|
47074
|
+
}
|
|
47075
|
+
}
|
|
47076
|
+
async start() {
|
|
47077
|
+
if (this._process) {
|
|
47078
|
+
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
47079
|
+
}
|
|
47080
|
+
return new Promise((resolve8, reject) => {
|
|
47081
|
+
this._process = import_cross_spawn.default(this._serverParams.command, this._serverParams.args ?? [], {
|
|
47082
|
+
env: {
|
|
47083
|
+
...getDefaultEnvironment(),
|
|
47084
|
+
...this._serverParams.env
|
|
47085
|
+
},
|
|
47086
|
+
stdio: ["pipe", "pipe", this._serverParams.stderr ?? "inherit"],
|
|
47087
|
+
shell: false,
|
|
47088
|
+
windowsHide: process2.platform === "win32" && isElectron(),
|
|
47089
|
+
cwd: this._serverParams.cwd
|
|
47090
|
+
});
|
|
47091
|
+
this._process.on("error", (error45) => {
|
|
47092
|
+
reject(error45);
|
|
47093
|
+
this.onerror?.(error45);
|
|
47094
|
+
});
|
|
47095
|
+
this._process.on("spawn", () => {
|
|
47096
|
+
resolve8();
|
|
47097
|
+
});
|
|
47098
|
+
this._process.on("close", (_code) => {
|
|
47099
|
+
this._process = undefined;
|
|
47100
|
+
this.onclose?.();
|
|
47101
|
+
});
|
|
47102
|
+
this._process.stdin?.on("error", (error45) => {
|
|
47103
|
+
this.onerror?.(error45);
|
|
47104
|
+
});
|
|
47105
|
+
this._process.stdout?.on("data", (chunk) => {
|
|
47106
|
+
this._readBuffer.append(chunk);
|
|
47107
|
+
this.processReadBuffer();
|
|
47108
|
+
});
|
|
47109
|
+
this._process.stdout?.on("error", (error45) => {
|
|
47110
|
+
this.onerror?.(error45);
|
|
47111
|
+
});
|
|
47112
|
+
if (this._stderrStream && this._process.stderr) {
|
|
47113
|
+
this._process.stderr.pipe(this._stderrStream);
|
|
47114
|
+
}
|
|
47115
|
+
});
|
|
47116
|
+
}
|
|
47117
|
+
get stderr() {
|
|
47118
|
+
if (this._stderrStream) {
|
|
47119
|
+
return this._stderrStream;
|
|
47120
|
+
}
|
|
47121
|
+
return this._process?.stderr ?? null;
|
|
47122
|
+
}
|
|
47123
|
+
get pid() {
|
|
47124
|
+
return this._process?.pid ?? null;
|
|
47125
|
+
}
|
|
47126
|
+
processReadBuffer() {
|
|
47127
|
+
while (true) {
|
|
47128
|
+
try {
|
|
47129
|
+
const message = this._readBuffer.readMessage();
|
|
47130
|
+
if (message === null) {
|
|
47131
|
+
break;
|
|
47132
|
+
}
|
|
47133
|
+
this.onmessage?.(message);
|
|
47134
|
+
} catch (error45) {
|
|
47135
|
+
this.onerror?.(error45);
|
|
47136
|
+
}
|
|
47137
|
+
}
|
|
47138
|
+
}
|
|
47139
|
+
async close() {
|
|
47140
|
+
if (this._process) {
|
|
47141
|
+
const processToClose = this._process;
|
|
47142
|
+
this._process = undefined;
|
|
47143
|
+
const closePromise = new Promise((resolve8) => {
|
|
47144
|
+
processToClose.once("close", () => {
|
|
47145
|
+
resolve8();
|
|
47146
|
+
});
|
|
47147
|
+
});
|
|
47148
|
+
try {
|
|
47149
|
+
processToClose.stdin?.end();
|
|
47150
|
+
} catch {}
|
|
47151
|
+
await Promise.race([closePromise, new Promise((resolve8) => setTimeout(resolve8, 2000).unref())]);
|
|
47152
|
+
if (processToClose.exitCode === null) {
|
|
47153
|
+
try {
|
|
47154
|
+
processToClose.kill("SIGTERM");
|
|
47155
|
+
} catch {}
|
|
47156
|
+
await Promise.race([closePromise, new Promise((resolve8) => setTimeout(resolve8, 2000).unref())]);
|
|
47157
|
+
}
|
|
47158
|
+
if (processToClose.exitCode === null) {
|
|
47159
|
+
try {
|
|
47160
|
+
processToClose.kill("SIGKILL");
|
|
47161
|
+
} catch {}
|
|
47162
|
+
}
|
|
47163
|
+
}
|
|
47164
|
+
this._readBuffer.clear();
|
|
47165
|
+
}
|
|
47166
|
+
send(message) {
|
|
47167
|
+
return new Promise((resolve8) => {
|
|
47168
|
+
if (!this._process?.stdin) {
|
|
47169
|
+
throw new Error("Not connected");
|
|
47170
|
+
}
|
|
47171
|
+
const json3 = serializeMessage(message);
|
|
47172
|
+
if (this._process.stdin.write(json3)) {
|
|
47173
|
+
resolve8();
|
|
47174
|
+
} else {
|
|
47175
|
+
this._process.stdin.once("drain", resolve8);
|
|
47176
|
+
}
|
|
47177
|
+
});
|
|
47178
|
+
}
|
|
47179
|
+
}
|
|
47180
|
+
function isElectron() {
|
|
47181
|
+
return "type" in process2;
|
|
47182
|
+
}
|
|
47183
|
+
|
|
47184
|
+
// node_modules/eventsource-parser/dist/index.js
|
|
47185
|
+
class ParseError2 extends Error {
|
|
47186
|
+
constructor(message, options) {
|
|
47187
|
+
super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
|
|
47188
|
+
}
|
|
47189
|
+
}
|
|
47190
|
+
function noop(_arg) {}
|
|
47191
|
+
function createParser(callbacks) {
|
|
47192
|
+
if (typeof callbacks == "function")
|
|
47193
|
+
throw new TypeError("`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?");
|
|
47194
|
+
const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
|
|
47195
|
+
let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
|
|
47196
|
+
function feed(newChunk) {
|
|
47197
|
+
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
|
|
47198
|
+
for (const line of complete)
|
|
47199
|
+
parseLine(line);
|
|
47200
|
+
incompleteLine = incomplete, isFirstChunk = false;
|
|
47201
|
+
}
|
|
47202
|
+
function parseLine(line) {
|
|
47203
|
+
if (line === "") {
|
|
47204
|
+
dispatchEvent();
|
|
47205
|
+
return;
|
|
47206
|
+
}
|
|
47207
|
+
if (line.startsWith(":")) {
|
|
47208
|
+
onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
|
|
47209
|
+
return;
|
|
47210
|
+
}
|
|
47211
|
+
const fieldSeparatorIndex = line.indexOf(":");
|
|
47212
|
+
if (fieldSeparatorIndex !== -1) {
|
|
47213
|
+
const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
|
|
47214
|
+
processField(field, value, line);
|
|
47215
|
+
return;
|
|
47216
|
+
}
|
|
47217
|
+
processField(line, "", line);
|
|
47218
|
+
}
|
|
47219
|
+
function processField(field, value, line) {
|
|
47220
|
+
switch (field) {
|
|
47221
|
+
case "event":
|
|
47222
|
+
eventType = value;
|
|
47223
|
+
break;
|
|
47224
|
+
case "data":
|
|
47225
|
+
data = `${data}${value}
|
|
47226
|
+
`;
|
|
47227
|
+
break;
|
|
47228
|
+
case "id":
|
|
47229
|
+
id = value.includes("\x00") ? undefined : value;
|
|
47230
|
+
break;
|
|
47231
|
+
case "retry":
|
|
47232
|
+
/^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(new ParseError2(`Invalid \`retry\` value: "${value}"`, {
|
|
47233
|
+
type: "invalid-retry",
|
|
47234
|
+
value,
|
|
47235
|
+
line
|
|
47236
|
+
}));
|
|
47237
|
+
break;
|
|
47238
|
+
default:
|
|
47239
|
+
onError(new ParseError2(`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`, { type: "unknown-field", field, value, line }));
|
|
47240
|
+
break;
|
|
47241
|
+
}
|
|
47242
|
+
}
|
|
47243
|
+
function dispatchEvent() {
|
|
47244
|
+
data.length > 0 && onEvent({
|
|
47245
|
+
id,
|
|
47246
|
+
event: eventType || undefined,
|
|
47247
|
+
data: data.endsWith(`
|
|
47248
|
+
`) ? data.slice(0, -1) : data
|
|
47249
|
+
}), id = undefined, data = "", eventType = "";
|
|
47250
|
+
}
|
|
47251
|
+
function reset(options = {}) {
|
|
47252
|
+
incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = true, id = undefined, data = "", eventType = "", incompleteLine = "";
|
|
47253
|
+
}
|
|
47254
|
+
return { feed, reset };
|
|
47255
|
+
}
|
|
47256
|
+
function splitLines(chunk) {
|
|
47257
|
+
const lines = [];
|
|
47258
|
+
let incompleteLine = "", searchIndex = 0;
|
|
47259
|
+
for (;searchIndex < chunk.length; ) {
|
|
47260
|
+
const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
|
|
47261
|
+
`, searchIndex);
|
|
47262
|
+
let lineEnd = -1;
|
|
47263
|
+
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
|
|
47264
|
+
incompleteLine = chunk.slice(searchIndex);
|
|
47265
|
+
break;
|
|
47266
|
+
} else {
|
|
47267
|
+
const line = chunk.slice(searchIndex, lineEnd);
|
|
47268
|
+
lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
|
|
47269
|
+
` && searchIndex++;
|
|
47270
|
+
}
|
|
47271
|
+
}
|
|
47272
|
+
return [lines, incompleteLine];
|
|
47273
|
+
}
|
|
47274
|
+
|
|
47275
|
+
// node_modules/eventsource/dist/index.js
|
|
47276
|
+
class ErrorEvent extends Event {
|
|
47277
|
+
constructor(type2, errorEventInitDict) {
|
|
47278
|
+
var _a, _b;
|
|
47279
|
+
super(type2), this.code = (_a = errorEventInitDict == null ? undefined : errorEventInitDict.code) != null ? _a : undefined, this.message = (_b = errorEventInitDict == null ? undefined : errorEventInitDict.message) != null ? _b : undefined;
|
|
47280
|
+
}
|
|
47281
|
+
[Symbol.for("nodejs.util.inspect.custom")](_depth, options, inspect) {
|
|
47282
|
+
return inspect(inspectableError(this), options);
|
|
47283
|
+
}
|
|
47284
|
+
[Symbol.for("Deno.customInspect")](inspect, options) {
|
|
47285
|
+
return inspect(inspectableError(this), options);
|
|
47286
|
+
}
|
|
47287
|
+
}
|
|
47288
|
+
function syntaxError(message) {
|
|
47289
|
+
const DomException = globalThis.DOMException;
|
|
47290
|
+
return typeof DomException == "function" ? new DomException(message, "SyntaxError") : new SyntaxError(message);
|
|
47291
|
+
}
|
|
47292
|
+
function flattenError2(err) {
|
|
47293
|
+
return err instanceof Error ? "errors" in err && Array.isArray(err.errors) ? err.errors.map(flattenError2).join(", ") : ("cause" in err) && err.cause instanceof Error ? `${err}: ${flattenError2(err.cause)}` : err.message : `${err}`;
|
|
47294
|
+
}
|
|
47295
|
+
function inspectableError(err) {
|
|
47296
|
+
return {
|
|
47297
|
+
type: err.type,
|
|
47298
|
+
message: err.message,
|
|
47299
|
+
code: err.code,
|
|
47300
|
+
defaultPrevented: err.defaultPrevented,
|
|
47301
|
+
cancelable: err.cancelable,
|
|
47302
|
+
timeStamp: err.timeStamp
|
|
47303
|
+
};
|
|
47304
|
+
}
|
|
47305
|
+
var __typeError = (msg) => {
|
|
47306
|
+
throw TypeError(msg);
|
|
47307
|
+
};
|
|
47308
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
47309
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
47310
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
47311
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
47312
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
47313
|
+
var _readyState;
|
|
47314
|
+
var _url2;
|
|
47315
|
+
var _redirectUrl;
|
|
47316
|
+
var _withCredentials;
|
|
47317
|
+
var _fetch;
|
|
47318
|
+
var _reconnectInterval;
|
|
47319
|
+
var _reconnectTimer;
|
|
47320
|
+
var _lastEventId;
|
|
47321
|
+
var _controller;
|
|
47322
|
+
var _parser;
|
|
47323
|
+
var _onError;
|
|
47324
|
+
var _onMessage;
|
|
47325
|
+
var _onOpen;
|
|
47326
|
+
var _EventSource_instances;
|
|
47327
|
+
var connect_fn;
|
|
47328
|
+
var _onFetchResponse;
|
|
47329
|
+
var _onFetchError;
|
|
47330
|
+
var getRequestOptions_fn;
|
|
47331
|
+
var _onEvent;
|
|
47332
|
+
var _onRetryChange;
|
|
47333
|
+
var failConnection_fn;
|
|
47334
|
+
var scheduleReconnect_fn;
|
|
47335
|
+
var _reconnect;
|
|
47336
|
+
|
|
47337
|
+
class EventSource extends EventTarget {
|
|
47338
|
+
constructor(url2, eventSourceInitDict) {
|
|
47339
|
+
var _a, _b;
|
|
47340
|
+
super(), __privateAdd(this, _EventSource_instances), this.CONNECTING = 0, this.OPEN = 1, this.CLOSED = 2, __privateAdd(this, _readyState), __privateAdd(this, _url2), __privateAdd(this, _redirectUrl), __privateAdd(this, _withCredentials), __privateAdd(this, _fetch), __privateAdd(this, _reconnectInterval), __privateAdd(this, _reconnectTimer), __privateAdd(this, _lastEventId, null), __privateAdd(this, _controller), __privateAdd(this, _parser), __privateAdd(this, _onError, null), __privateAdd(this, _onMessage, null), __privateAdd(this, _onOpen, null), __privateAdd(this, _onFetchResponse, async (response2) => {
|
|
47341
|
+
var _a2;
|
|
47342
|
+
__privateGet(this, _parser).reset();
|
|
47343
|
+
const { body, redirected, status, headers } = response2;
|
|
47344
|
+
if (status === 204) {
|
|
47345
|
+
__privateMethod(this, _EventSource_instances, failConnection_fn).call(this, "Server sent HTTP 204, not reconnecting", 204), this.close();
|
|
47346
|
+
return;
|
|
47347
|
+
}
|
|
47348
|
+
if (redirected ? __privateSet(this, _redirectUrl, new URL(response2.url)) : __privateSet(this, _redirectUrl, undefined), status !== 200) {
|
|
47349
|
+
__privateMethod(this, _EventSource_instances, failConnection_fn).call(this, `Non-200 status code (${status})`, status);
|
|
47350
|
+
return;
|
|
47351
|
+
}
|
|
47352
|
+
if (!(headers.get("content-type") || "").startsWith("text/event-stream")) {
|
|
47353
|
+
__privateMethod(this, _EventSource_instances, failConnection_fn).call(this, 'Invalid content type, expected "text/event-stream"', status);
|
|
47354
|
+
return;
|
|
47355
|
+
}
|
|
47356
|
+
if (__privateGet(this, _readyState) === this.CLOSED)
|
|
47357
|
+
return;
|
|
47358
|
+
__privateSet(this, _readyState, this.OPEN);
|
|
47359
|
+
const openEvent = new Event("open");
|
|
47360
|
+
if ((_a2 = __privateGet(this, _onOpen)) == null || _a2.call(this, openEvent), this.dispatchEvent(openEvent), typeof body != "object" || !body || !("getReader" in body)) {
|
|
47361
|
+
__privateMethod(this, _EventSource_instances, failConnection_fn).call(this, "Invalid response body, expected a web ReadableStream", status), this.close();
|
|
47362
|
+
return;
|
|
47363
|
+
}
|
|
47364
|
+
const decoder2 = new TextDecoder, reader = body.getReader();
|
|
47365
|
+
let open = true;
|
|
47366
|
+
do {
|
|
47367
|
+
const { done, value } = await reader.read();
|
|
47368
|
+
value && __privateGet(this, _parser).feed(decoder2.decode(value, { stream: !done })), done && (open = false, __privateGet(this, _parser).reset(), __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this));
|
|
47369
|
+
} while (open);
|
|
47370
|
+
}), __privateAdd(this, _onFetchError, (err) => {
|
|
47371
|
+
__privateSet(this, _controller, undefined), !(err.name === "AbortError" || err.type === "aborted") && __privateMethod(this, _EventSource_instances, scheduleReconnect_fn).call(this, flattenError2(err));
|
|
47372
|
+
}), __privateAdd(this, _onEvent, (event) => {
|
|
47373
|
+
typeof event.id == "string" && __privateSet(this, _lastEventId, event.id);
|
|
47374
|
+
const messageEvent = new MessageEvent(event.event || "message", {
|
|
47375
|
+
data: event.data,
|
|
47376
|
+
origin: __privateGet(this, _redirectUrl) ? __privateGet(this, _redirectUrl).origin : __privateGet(this, _url2).origin,
|
|
47377
|
+
lastEventId: event.id || ""
|
|
47378
|
+
});
|
|
47379
|
+
__privateGet(this, _onMessage) && (!event.event || event.event === "message") && __privateGet(this, _onMessage).call(this, messageEvent), this.dispatchEvent(messageEvent);
|
|
47380
|
+
}), __privateAdd(this, _onRetryChange, (value) => {
|
|
47381
|
+
__privateSet(this, _reconnectInterval, value);
|
|
47382
|
+
}), __privateAdd(this, _reconnect, () => {
|
|
47383
|
+
__privateSet(this, _reconnectTimer, undefined), __privateGet(this, _readyState) === this.CONNECTING && __privateMethod(this, _EventSource_instances, connect_fn).call(this);
|
|
47384
|
+
});
|
|
47385
|
+
try {
|
|
47386
|
+
if (url2 instanceof URL)
|
|
47387
|
+
__privateSet(this, _url2, url2);
|
|
47388
|
+
else if (typeof url2 == "string")
|
|
47389
|
+
__privateSet(this, _url2, new URL(url2, getBaseURL()));
|
|
47390
|
+
else
|
|
47391
|
+
throw new Error("Invalid URL");
|
|
47392
|
+
} catch {
|
|
47393
|
+
throw syntaxError("An invalid or illegal string was specified");
|
|
47394
|
+
}
|
|
47395
|
+
__privateSet(this, _parser, createParser({
|
|
47396
|
+
onEvent: __privateGet(this, _onEvent),
|
|
47397
|
+
onRetry: __privateGet(this, _onRetryChange)
|
|
47398
|
+
})), __privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _reconnectInterval, 3000), __privateSet(this, _fetch, (_a = eventSourceInitDict == null ? undefined : eventSourceInitDict.fetch) != null ? _a : globalThis.fetch), __privateSet(this, _withCredentials, (_b = eventSourceInitDict == null ? undefined : eventSourceInitDict.withCredentials) != null ? _b : false), __privateMethod(this, _EventSource_instances, connect_fn).call(this);
|
|
47399
|
+
}
|
|
47400
|
+
get readyState() {
|
|
47401
|
+
return __privateGet(this, _readyState);
|
|
47402
|
+
}
|
|
47403
|
+
get url() {
|
|
47404
|
+
return __privateGet(this, _url2).href;
|
|
47405
|
+
}
|
|
47406
|
+
get withCredentials() {
|
|
47407
|
+
return __privateGet(this, _withCredentials);
|
|
47408
|
+
}
|
|
47409
|
+
get onerror() {
|
|
47410
|
+
return __privateGet(this, _onError);
|
|
47411
|
+
}
|
|
47412
|
+
set onerror(value) {
|
|
47413
|
+
__privateSet(this, _onError, value);
|
|
47414
|
+
}
|
|
47415
|
+
get onmessage() {
|
|
47416
|
+
return __privateGet(this, _onMessage);
|
|
47417
|
+
}
|
|
47418
|
+
set onmessage(value) {
|
|
47419
|
+
__privateSet(this, _onMessage, value);
|
|
47420
|
+
}
|
|
47421
|
+
get onopen() {
|
|
47422
|
+
return __privateGet(this, _onOpen);
|
|
47423
|
+
}
|
|
47424
|
+
set onopen(value) {
|
|
47425
|
+
__privateSet(this, _onOpen, value);
|
|
47426
|
+
}
|
|
47427
|
+
addEventListener(type2, listener, options) {
|
|
47428
|
+
const listen = listener;
|
|
47429
|
+
super.addEventListener(type2, listen, options);
|
|
47430
|
+
}
|
|
47431
|
+
removeEventListener(type2, listener, options) {
|
|
47432
|
+
const listen = listener;
|
|
47433
|
+
super.removeEventListener(type2, listen, options);
|
|
47434
|
+
}
|
|
47435
|
+
close() {
|
|
47436
|
+
__privateGet(this, _reconnectTimer) && clearTimeout(__privateGet(this, _reconnectTimer)), __privateGet(this, _readyState) !== this.CLOSED && (__privateGet(this, _controller) && __privateGet(this, _controller).abort(), __privateSet(this, _readyState, this.CLOSED), __privateSet(this, _controller, undefined));
|
|
47437
|
+
}
|
|
47438
|
+
}
|
|
47439
|
+
_readyState = /* @__PURE__ */ new WeakMap, _url2 = /* @__PURE__ */ new WeakMap, _redirectUrl = /* @__PURE__ */ new WeakMap, _withCredentials = /* @__PURE__ */ new WeakMap, _fetch = /* @__PURE__ */ new WeakMap, _reconnectInterval = /* @__PURE__ */ new WeakMap, _reconnectTimer = /* @__PURE__ */ new WeakMap, _lastEventId = /* @__PURE__ */ new WeakMap, _controller = /* @__PURE__ */ new WeakMap, _parser = /* @__PURE__ */ new WeakMap, _onError = /* @__PURE__ */ new WeakMap, _onMessage = /* @__PURE__ */ new WeakMap, _onOpen = /* @__PURE__ */ new WeakMap, _EventSource_instances = /* @__PURE__ */ new WeakSet, connect_fn = function() {
|
|
47440
|
+
__privateSet(this, _readyState, this.CONNECTING), __privateSet(this, _controller, new AbortController), __privateGet(this, _fetch)(__privateGet(this, _url2), __privateMethod(this, _EventSource_instances, getRequestOptions_fn).call(this)).then(__privateGet(this, _onFetchResponse)).catch(__privateGet(this, _onFetchError));
|
|
47441
|
+
}, _onFetchResponse = /* @__PURE__ */ new WeakMap, _onFetchError = /* @__PURE__ */ new WeakMap, getRequestOptions_fn = function() {
|
|
47442
|
+
var _a;
|
|
47443
|
+
const init = {
|
|
47444
|
+
mode: "cors",
|
|
47445
|
+
redirect: "follow",
|
|
47446
|
+
headers: { Accept: "text/event-stream", ...__privateGet(this, _lastEventId) ? { "Last-Event-ID": __privateGet(this, _lastEventId) } : undefined },
|
|
47447
|
+
cache: "no-store",
|
|
47448
|
+
signal: (_a = __privateGet(this, _controller)) == null ? undefined : _a.signal
|
|
47449
|
+
};
|
|
47450
|
+
return "window" in globalThis && (init.credentials = this.withCredentials ? "include" : "same-origin"), init;
|
|
47451
|
+
}, _onEvent = /* @__PURE__ */ new WeakMap, _onRetryChange = /* @__PURE__ */ new WeakMap, failConnection_fn = function(message, code) {
|
|
47452
|
+
var _a;
|
|
47453
|
+
__privateGet(this, _readyState) !== this.CLOSED && __privateSet(this, _readyState, this.CLOSED);
|
|
47454
|
+
const errorEvent = new ErrorEvent("error", { code, message });
|
|
47455
|
+
(_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent);
|
|
47456
|
+
}, scheduleReconnect_fn = function(message, code) {
|
|
47457
|
+
var _a;
|
|
47458
|
+
if (__privateGet(this, _readyState) === this.CLOSED)
|
|
47459
|
+
return;
|
|
47460
|
+
__privateSet(this, _readyState, this.CONNECTING);
|
|
47461
|
+
const errorEvent = new ErrorEvent("error", { code, message });
|
|
47462
|
+
(_a = __privateGet(this, _onError)) == null || _a.call(this, errorEvent), this.dispatchEvent(errorEvent), __privateSet(this, _reconnectTimer, setTimeout(__privateGet(this, _reconnect), __privateGet(this, _reconnectInterval)));
|
|
47463
|
+
}, _reconnect = /* @__PURE__ */ new WeakMap, EventSource.CONNECTING = 0, EventSource.OPEN = 1, EventSource.CLOSED = 2;
|
|
47464
|
+
function getBaseURL() {
|
|
47465
|
+
const doc2 = "document" in globalThis ? globalThis.document : undefined;
|
|
47466
|
+
return doc2 && typeof doc2 == "object" && "baseURI" in doc2 && typeof doc2.baseURI == "string" ? doc2.baseURI : undefined;
|
|
47467
|
+
}
|
|
47468
|
+
|
|
47469
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.js
|
|
47470
|
+
function normalizeHeaders(headers) {
|
|
47471
|
+
if (!headers)
|
|
47472
|
+
return {};
|
|
47473
|
+
if (headers instanceof Headers) {
|
|
47474
|
+
return Object.fromEntries(headers.entries());
|
|
47475
|
+
}
|
|
47476
|
+
if (Array.isArray(headers)) {
|
|
47477
|
+
return Object.fromEntries(headers);
|
|
47478
|
+
}
|
|
47479
|
+
return { ...headers };
|
|
47480
|
+
}
|
|
47481
|
+
function createFetchWithInit(baseFetch = fetch, baseInit) {
|
|
47482
|
+
if (!baseInit) {
|
|
47483
|
+
return baseFetch;
|
|
47484
|
+
}
|
|
47485
|
+
return async (url2, init) => {
|
|
47486
|
+
const mergedInit = {
|
|
47487
|
+
...baseInit,
|
|
47488
|
+
...init,
|
|
47489
|
+
headers: init?.headers ? { ...normalizeHeaders(baseInit.headers), ...normalizeHeaders(init.headers) } : baseInit.headers
|
|
47490
|
+
};
|
|
47491
|
+
return baseFetch(url2, mergedInit);
|
|
47492
|
+
};
|
|
47493
|
+
}
|
|
47494
|
+
|
|
47495
|
+
// node_modules/pkce-challenge/dist/index.node.js
|
|
47496
|
+
var crypto2;
|
|
47497
|
+
crypto2 = globalThis.crypto?.webcrypto ?? globalThis.crypto ?? import("crypto").then((m) => m.webcrypto);
|
|
47498
|
+
async function getRandomValues(size) {
|
|
47499
|
+
return (await crypto2).getRandomValues(new Uint8Array(size));
|
|
47500
|
+
}
|
|
47501
|
+
async function random(size) {
|
|
47502
|
+
const mask = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
|
|
47503
|
+
const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
|
|
47504
|
+
let result = "";
|
|
47505
|
+
while (result.length < size) {
|
|
47506
|
+
const randomBytes = await getRandomValues(size - result.length);
|
|
47507
|
+
for (const randomByte of randomBytes) {
|
|
47508
|
+
if (randomByte < evenDistCutoff) {
|
|
47509
|
+
result += mask[randomByte % mask.length];
|
|
47510
|
+
}
|
|
47511
|
+
}
|
|
47512
|
+
}
|
|
47513
|
+
return result;
|
|
47514
|
+
}
|
|
47515
|
+
async function generateVerifier2(length) {
|
|
47516
|
+
return await random(length);
|
|
47517
|
+
}
|
|
47518
|
+
async function generateChallenge2(code_verifier) {
|
|
47519
|
+
const buffer = await (await crypto2).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
|
|
47520
|
+
return btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
|
|
47521
|
+
}
|
|
47522
|
+
async function pkceChallenge(length) {
|
|
47523
|
+
if (!length)
|
|
47524
|
+
length = 43;
|
|
47525
|
+
if (length < 43 || length > 128) {
|
|
47526
|
+
throw `Expected a length between 43 and 128. Received ${length}.`;
|
|
47527
|
+
}
|
|
47528
|
+
const verifier = await generateVerifier2(length);
|
|
47529
|
+
const challenge = await generateChallenge2(verifier);
|
|
47530
|
+
return {
|
|
47531
|
+
code_verifier: verifier,
|
|
47532
|
+
code_challenge: challenge
|
|
47533
|
+
};
|
|
47534
|
+
}
|
|
47535
|
+
|
|
47536
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/auth.js
|
|
47537
|
+
var SafeUrlSchema = url().superRefine((val, ctx) => {
|
|
47538
|
+
if (!URL.canParse(val)) {
|
|
47539
|
+
ctx.addIssue({
|
|
47540
|
+
code: ZodIssueCode.custom,
|
|
47541
|
+
message: "URL must be parseable",
|
|
47542
|
+
fatal: true
|
|
47543
|
+
});
|
|
47544
|
+
return NEVER;
|
|
47545
|
+
}
|
|
47546
|
+
}).refine((url2) => {
|
|
47547
|
+
const u = new URL(url2);
|
|
47548
|
+
return u.protocol !== "javascript:" && u.protocol !== "data:" && u.protocol !== "vbscript:";
|
|
47549
|
+
}, { message: "URL cannot use javascript:, data:, or vbscript: scheme" });
|
|
47550
|
+
var OAuthProtectedResourceMetadataSchema = looseObject({
|
|
47551
|
+
resource: string2().url(),
|
|
47552
|
+
authorization_servers: array(SafeUrlSchema).optional(),
|
|
47553
|
+
jwks_uri: string2().url().optional(),
|
|
47554
|
+
scopes_supported: array(string2()).optional(),
|
|
47555
|
+
bearer_methods_supported: array(string2()).optional(),
|
|
47556
|
+
resource_signing_alg_values_supported: array(string2()).optional(),
|
|
47557
|
+
resource_name: string2().optional(),
|
|
47558
|
+
resource_documentation: string2().optional(),
|
|
47559
|
+
resource_policy_uri: string2().url().optional(),
|
|
47560
|
+
resource_tos_uri: string2().url().optional(),
|
|
47561
|
+
tls_client_certificate_bound_access_tokens: boolean2().optional(),
|
|
47562
|
+
authorization_details_types_supported: array(string2()).optional(),
|
|
47563
|
+
dpop_signing_alg_values_supported: array(string2()).optional(),
|
|
47564
|
+
dpop_bound_access_tokens_required: boolean2().optional()
|
|
47565
|
+
});
|
|
47566
|
+
var OAuthMetadataSchema = looseObject({
|
|
47567
|
+
issuer: string2(),
|
|
47568
|
+
authorization_endpoint: SafeUrlSchema,
|
|
47569
|
+
token_endpoint: SafeUrlSchema,
|
|
47570
|
+
registration_endpoint: SafeUrlSchema.optional(),
|
|
47571
|
+
scopes_supported: array(string2()).optional(),
|
|
47572
|
+
response_types_supported: array(string2()),
|
|
47573
|
+
response_modes_supported: array(string2()).optional(),
|
|
47574
|
+
grant_types_supported: array(string2()).optional(),
|
|
47575
|
+
token_endpoint_auth_methods_supported: array(string2()).optional(),
|
|
47576
|
+
token_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
|
|
47577
|
+
service_documentation: SafeUrlSchema.optional(),
|
|
47578
|
+
revocation_endpoint: SafeUrlSchema.optional(),
|
|
47579
|
+
revocation_endpoint_auth_methods_supported: array(string2()).optional(),
|
|
47580
|
+
revocation_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
|
|
47581
|
+
introspection_endpoint: string2().optional(),
|
|
47582
|
+
introspection_endpoint_auth_methods_supported: array(string2()).optional(),
|
|
47583
|
+
introspection_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
|
|
47584
|
+
code_challenge_methods_supported: array(string2()).optional(),
|
|
47585
|
+
client_id_metadata_document_supported: boolean2().optional()
|
|
47586
|
+
});
|
|
47587
|
+
var OpenIdProviderMetadataSchema = looseObject({
|
|
47588
|
+
issuer: string2(),
|
|
47589
|
+
authorization_endpoint: SafeUrlSchema,
|
|
47590
|
+
token_endpoint: SafeUrlSchema,
|
|
47591
|
+
userinfo_endpoint: SafeUrlSchema.optional(),
|
|
47592
|
+
jwks_uri: SafeUrlSchema,
|
|
47593
|
+
registration_endpoint: SafeUrlSchema.optional(),
|
|
47594
|
+
scopes_supported: array(string2()).optional(),
|
|
47595
|
+
response_types_supported: array(string2()),
|
|
47596
|
+
response_modes_supported: array(string2()).optional(),
|
|
47597
|
+
grant_types_supported: array(string2()).optional(),
|
|
47598
|
+
acr_values_supported: array(string2()).optional(),
|
|
47599
|
+
subject_types_supported: array(string2()),
|
|
47600
|
+
id_token_signing_alg_values_supported: array(string2()),
|
|
47601
|
+
id_token_encryption_alg_values_supported: array(string2()).optional(),
|
|
47602
|
+
id_token_encryption_enc_values_supported: array(string2()).optional(),
|
|
47603
|
+
userinfo_signing_alg_values_supported: array(string2()).optional(),
|
|
47604
|
+
userinfo_encryption_alg_values_supported: array(string2()).optional(),
|
|
47605
|
+
userinfo_encryption_enc_values_supported: array(string2()).optional(),
|
|
47606
|
+
request_object_signing_alg_values_supported: array(string2()).optional(),
|
|
47607
|
+
request_object_encryption_alg_values_supported: array(string2()).optional(),
|
|
47608
|
+
request_object_encryption_enc_values_supported: array(string2()).optional(),
|
|
47609
|
+
token_endpoint_auth_methods_supported: array(string2()).optional(),
|
|
47610
|
+
token_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
|
|
47611
|
+
display_values_supported: array(string2()).optional(),
|
|
47612
|
+
claim_types_supported: array(string2()).optional(),
|
|
47613
|
+
claims_supported: array(string2()).optional(),
|
|
47614
|
+
service_documentation: string2().optional(),
|
|
47615
|
+
claims_locales_supported: array(string2()).optional(),
|
|
47616
|
+
ui_locales_supported: array(string2()).optional(),
|
|
47617
|
+
claims_parameter_supported: boolean2().optional(),
|
|
47618
|
+
request_parameter_supported: boolean2().optional(),
|
|
47619
|
+
request_uri_parameter_supported: boolean2().optional(),
|
|
47620
|
+
require_request_uri_registration: boolean2().optional(),
|
|
47621
|
+
op_policy_uri: SafeUrlSchema.optional(),
|
|
47622
|
+
op_tos_uri: SafeUrlSchema.optional(),
|
|
47623
|
+
client_id_metadata_document_supported: boolean2().optional()
|
|
47624
|
+
});
|
|
47625
|
+
var OpenIdProviderDiscoveryMetadataSchema = object({
|
|
47626
|
+
...OpenIdProviderMetadataSchema.shape,
|
|
47627
|
+
...OAuthMetadataSchema.pick({
|
|
47628
|
+
code_challenge_methods_supported: true
|
|
47629
|
+
}).shape
|
|
47630
|
+
});
|
|
47631
|
+
var OAuthTokensSchema = object({
|
|
47632
|
+
access_token: string2(),
|
|
47633
|
+
id_token: string2().optional(),
|
|
47634
|
+
token_type: string2(),
|
|
47635
|
+
expires_in: exports_coerce.number().optional(),
|
|
47636
|
+
scope: string2().optional(),
|
|
47637
|
+
refresh_token: string2().optional()
|
|
47638
|
+
}).strip();
|
|
47639
|
+
var OAuthErrorResponseSchema = object({
|
|
47640
|
+
error: string2(),
|
|
47641
|
+
error_description: string2().optional(),
|
|
47642
|
+
error_uri: string2().optional()
|
|
47643
|
+
});
|
|
47644
|
+
var OptionalSafeUrlSchema = SafeUrlSchema.optional().or(literal("").transform(() => {
|
|
47645
|
+
return;
|
|
47646
|
+
}));
|
|
47647
|
+
var OAuthClientMetadataSchema = object({
|
|
47648
|
+
redirect_uris: array(SafeUrlSchema),
|
|
47649
|
+
token_endpoint_auth_method: string2().optional(),
|
|
47650
|
+
grant_types: array(string2()).optional(),
|
|
47651
|
+
response_types: array(string2()).optional(),
|
|
47652
|
+
client_name: string2().optional(),
|
|
47653
|
+
client_uri: SafeUrlSchema.optional(),
|
|
47654
|
+
logo_uri: OptionalSafeUrlSchema,
|
|
47655
|
+
scope: string2().optional(),
|
|
47656
|
+
contacts: array(string2()).optional(),
|
|
47657
|
+
tos_uri: OptionalSafeUrlSchema,
|
|
47658
|
+
policy_uri: string2().optional(),
|
|
47659
|
+
jwks_uri: SafeUrlSchema.optional(),
|
|
47660
|
+
jwks: any().optional(),
|
|
47661
|
+
software_id: string2().optional(),
|
|
47662
|
+
software_version: string2().optional(),
|
|
47663
|
+
software_statement: string2().optional()
|
|
47664
|
+
}).strip();
|
|
47665
|
+
var OAuthClientInformationSchema = object({
|
|
47666
|
+
client_id: string2(),
|
|
47667
|
+
client_secret: string2().optional(),
|
|
47668
|
+
client_id_issued_at: number2().optional(),
|
|
47669
|
+
client_secret_expires_at: number2().optional()
|
|
47670
|
+
}).strip();
|
|
47671
|
+
var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
|
|
47672
|
+
var OAuthClientRegistrationErrorSchema = object({
|
|
47673
|
+
error: string2(),
|
|
47674
|
+
error_description: string2().optional()
|
|
47675
|
+
}).strip();
|
|
47676
|
+
var OAuthTokenRevocationRequestSchema = object({
|
|
47677
|
+
token: string2(),
|
|
47678
|
+
token_type_hint: string2().optional()
|
|
47679
|
+
}).strip();
|
|
47680
|
+
|
|
47681
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/auth-utils.js
|
|
47682
|
+
function resourceUrlFromServerUrl(url2) {
|
|
47683
|
+
const resourceURL = typeof url2 === "string" ? new URL(url2) : new URL(url2.href);
|
|
47684
|
+
resourceURL.hash = "";
|
|
47685
|
+
return resourceURL;
|
|
47686
|
+
}
|
|
47687
|
+
function checkResourceAllowed({ requestedResource, configuredResource }) {
|
|
47688
|
+
const requested = typeof requestedResource === "string" ? new URL(requestedResource) : new URL(requestedResource.href);
|
|
47689
|
+
const configured = typeof configuredResource === "string" ? new URL(configuredResource) : new URL(configuredResource.href);
|
|
47690
|
+
if (requested.origin !== configured.origin) {
|
|
47691
|
+
return false;
|
|
47692
|
+
}
|
|
47693
|
+
if (requested.pathname.length < configured.pathname.length) {
|
|
47694
|
+
return false;
|
|
47695
|
+
}
|
|
47696
|
+
const requestedPath = requested.pathname.endsWith("/") ? requested.pathname : requested.pathname + "/";
|
|
47697
|
+
const configuredPath = configured.pathname.endsWith("/") ? configured.pathname : configured.pathname + "/";
|
|
47698
|
+
return requestedPath.startsWith(configuredPath);
|
|
47699
|
+
}
|
|
47700
|
+
|
|
47701
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/auth/errors.js
|
|
47702
|
+
class OAuthError extends Error {
|
|
47703
|
+
constructor(message, errorUri) {
|
|
47704
|
+
super(message);
|
|
47705
|
+
this.errorUri = errorUri;
|
|
47706
|
+
this.name = this.constructor.name;
|
|
47707
|
+
}
|
|
47708
|
+
toResponseObject() {
|
|
47709
|
+
const response2 = {
|
|
47710
|
+
error: this.errorCode,
|
|
47711
|
+
error_description: this.message
|
|
47712
|
+
};
|
|
47713
|
+
if (this.errorUri) {
|
|
47714
|
+
response2.error_uri = this.errorUri;
|
|
47715
|
+
}
|
|
47716
|
+
return response2;
|
|
47717
|
+
}
|
|
47718
|
+
get errorCode() {
|
|
47719
|
+
return this.constructor.errorCode;
|
|
47720
|
+
}
|
|
47721
|
+
}
|
|
47722
|
+
|
|
47723
|
+
class InvalidRequestError extends OAuthError {
|
|
47724
|
+
}
|
|
47725
|
+
InvalidRequestError.errorCode = "invalid_request";
|
|
47726
|
+
|
|
47727
|
+
class InvalidClientError extends OAuthError {
|
|
47728
|
+
}
|
|
47729
|
+
InvalidClientError.errorCode = "invalid_client";
|
|
47730
|
+
|
|
47731
|
+
class InvalidGrantError extends OAuthError {
|
|
47732
|
+
}
|
|
47733
|
+
InvalidGrantError.errorCode = "invalid_grant";
|
|
47734
|
+
|
|
47735
|
+
class UnauthorizedClientError extends OAuthError {
|
|
47736
|
+
}
|
|
47737
|
+
UnauthorizedClientError.errorCode = "unauthorized_client";
|
|
47738
|
+
|
|
47739
|
+
class UnsupportedGrantTypeError extends OAuthError {
|
|
47740
|
+
}
|
|
47741
|
+
UnsupportedGrantTypeError.errorCode = "unsupported_grant_type";
|
|
47742
|
+
|
|
47743
|
+
class InvalidScopeError extends OAuthError {
|
|
47744
|
+
}
|
|
47745
|
+
InvalidScopeError.errorCode = "invalid_scope";
|
|
47746
|
+
|
|
47747
|
+
class AccessDeniedError extends OAuthError {
|
|
47748
|
+
}
|
|
47749
|
+
AccessDeniedError.errorCode = "access_denied";
|
|
47750
|
+
|
|
47751
|
+
class ServerError extends OAuthError {
|
|
47752
|
+
}
|
|
47753
|
+
ServerError.errorCode = "server_error";
|
|
47754
|
+
|
|
47755
|
+
class TemporarilyUnavailableError extends OAuthError {
|
|
47756
|
+
}
|
|
47757
|
+
TemporarilyUnavailableError.errorCode = "temporarily_unavailable";
|
|
47758
|
+
|
|
47759
|
+
class UnsupportedResponseTypeError extends OAuthError {
|
|
47760
|
+
}
|
|
47761
|
+
UnsupportedResponseTypeError.errorCode = "unsupported_response_type";
|
|
47762
|
+
|
|
47763
|
+
class UnsupportedTokenTypeError extends OAuthError {
|
|
47764
|
+
}
|
|
47765
|
+
UnsupportedTokenTypeError.errorCode = "unsupported_token_type";
|
|
47766
|
+
|
|
47767
|
+
class InvalidTokenError extends OAuthError {
|
|
47768
|
+
}
|
|
47769
|
+
InvalidTokenError.errorCode = "invalid_token";
|
|
47770
|
+
|
|
47771
|
+
class MethodNotAllowedError extends OAuthError {
|
|
47772
|
+
}
|
|
47773
|
+
MethodNotAllowedError.errorCode = "method_not_allowed";
|
|
47774
|
+
|
|
47775
|
+
class TooManyRequestsError extends OAuthError {
|
|
47776
|
+
}
|
|
47777
|
+
TooManyRequestsError.errorCode = "too_many_requests";
|
|
47778
|
+
|
|
47779
|
+
class InvalidClientMetadataError extends OAuthError {
|
|
47780
|
+
}
|
|
47781
|
+
InvalidClientMetadataError.errorCode = "invalid_client_metadata";
|
|
47782
|
+
|
|
47783
|
+
class InsufficientScopeError extends OAuthError {
|
|
47784
|
+
}
|
|
47785
|
+
InsufficientScopeError.errorCode = "insufficient_scope";
|
|
47786
|
+
|
|
47787
|
+
class InvalidTargetError extends OAuthError {
|
|
47788
|
+
}
|
|
47789
|
+
InvalidTargetError.errorCode = "invalid_target";
|
|
47790
|
+
var OAUTH_ERRORS = {
|
|
47791
|
+
[InvalidRequestError.errorCode]: InvalidRequestError,
|
|
47792
|
+
[InvalidClientError.errorCode]: InvalidClientError,
|
|
47793
|
+
[InvalidGrantError.errorCode]: InvalidGrantError,
|
|
47794
|
+
[UnauthorizedClientError.errorCode]: UnauthorizedClientError,
|
|
47795
|
+
[UnsupportedGrantTypeError.errorCode]: UnsupportedGrantTypeError,
|
|
47796
|
+
[InvalidScopeError.errorCode]: InvalidScopeError,
|
|
47797
|
+
[AccessDeniedError.errorCode]: AccessDeniedError,
|
|
47798
|
+
[ServerError.errorCode]: ServerError,
|
|
47799
|
+
[TemporarilyUnavailableError.errorCode]: TemporarilyUnavailableError,
|
|
47800
|
+
[UnsupportedResponseTypeError.errorCode]: UnsupportedResponseTypeError,
|
|
47801
|
+
[UnsupportedTokenTypeError.errorCode]: UnsupportedTokenTypeError,
|
|
47802
|
+
[InvalidTokenError.errorCode]: InvalidTokenError,
|
|
47803
|
+
[MethodNotAllowedError.errorCode]: MethodNotAllowedError,
|
|
47804
|
+
[TooManyRequestsError.errorCode]: TooManyRequestsError,
|
|
47805
|
+
[InvalidClientMetadataError.errorCode]: InvalidClientMetadataError,
|
|
47806
|
+
[InsufficientScopeError.errorCode]: InsufficientScopeError,
|
|
47807
|
+
[InvalidTargetError.errorCode]: InvalidTargetError
|
|
47808
|
+
};
|
|
47809
|
+
|
|
47810
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js
|
|
47811
|
+
class UnauthorizedError extends Error {
|
|
47812
|
+
constructor(message) {
|
|
47813
|
+
super(message ?? "Unauthorized");
|
|
47814
|
+
}
|
|
47815
|
+
}
|
|
47816
|
+
function isClientAuthMethod(method) {
|
|
47817
|
+
return ["client_secret_basic", "client_secret_post", "none"].includes(method);
|
|
47818
|
+
}
|
|
47819
|
+
var AUTHORIZATION_CODE_RESPONSE_TYPE = "code";
|
|
47820
|
+
var AUTHORIZATION_CODE_CHALLENGE_METHOD = "S256";
|
|
47821
|
+
function selectClientAuthMethod(clientInformation, supportedMethods) {
|
|
47822
|
+
const hasClientSecret = clientInformation.client_secret !== undefined;
|
|
47823
|
+
if (supportedMethods.length === 0) {
|
|
47824
|
+
return hasClientSecret ? "client_secret_post" : "none";
|
|
47825
|
+
}
|
|
47826
|
+
if ("token_endpoint_auth_method" in clientInformation && clientInformation.token_endpoint_auth_method && isClientAuthMethod(clientInformation.token_endpoint_auth_method) && supportedMethods.includes(clientInformation.token_endpoint_auth_method)) {
|
|
47827
|
+
return clientInformation.token_endpoint_auth_method;
|
|
47828
|
+
}
|
|
47829
|
+
if (hasClientSecret && supportedMethods.includes("client_secret_basic")) {
|
|
47830
|
+
return "client_secret_basic";
|
|
47831
|
+
}
|
|
47832
|
+
if (hasClientSecret && supportedMethods.includes("client_secret_post")) {
|
|
47833
|
+
return "client_secret_post";
|
|
47834
|
+
}
|
|
47835
|
+
if (supportedMethods.includes("none")) {
|
|
47836
|
+
return "none";
|
|
47837
|
+
}
|
|
47838
|
+
return hasClientSecret ? "client_secret_post" : "none";
|
|
47839
|
+
}
|
|
47840
|
+
function applyClientAuthentication(method, clientInformation, headers, params) {
|
|
47841
|
+
const { client_id, client_secret } = clientInformation;
|
|
47842
|
+
switch (method) {
|
|
47843
|
+
case "client_secret_basic":
|
|
47844
|
+
applyBasicAuth(client_id, client_secret, headers);
|
|
47845
|
+
return;
|
|
47846
|
+
case "client_secret_post":
|
|
47847
|
+
applyPostAuth(client_id, client_secret, params);
|
|
47848
|
+
return;
|
|
47849
|
+
case "none":
|
|
47850
|
+
applyPublicAuth(client_id, params);
|
|
47851
|
+
return;
|
|
47852
|
+
default:
|
|
47853
|
+
throw new Error(`Unsupported client authentication method: ${method}`);
|
|
47854
|
+
}
|
|
47855
|
+
}
|
|
47856
|
+
function applyBasicAuth(clientId, clientSecret, headers) {
|
|
47857
|
+
if (!clientSecret) {
|
|
47858
|
+
throw new Error("client_secret_basic authentication requires a client_secret");
|
|
47859
|
+
}
|
|
47860
|
+
const credentials = btoa(`${clientId}:${clientSecret}`);
|
|
47861
|
+
headers.set("Authorization", `Basic ${credentials}`);
|
|
47862
|
+
}
|
|
47863
|
+
function applyPostAuth(clientId, clientSecret, params) {
|
|
47864
|
+
params.set("client_id", clientId);
|
|
47865
|
+
if (clientSecret) {
|
|
47866
|
+
params.set("client_secret", clientSecret);
|
|
47867
|
+
}
|
|
47868
|
+
}
|
|
47869
|
+
function applyPublicAuth(clientId, params) {
|
|
47870
|
+
params.set("client_id", clientId);
|
|
47871
|
+
}
|
|
47872
|
+
async function parseErrorResponse(input) {
|
|
47873
|
+
const statusCode = input instanceof Response ? input.status : undefined;
|
|
47874
|
+
const body = input instanceof Response ? await input.text() : input;
|
|
47875
|
+
try {
|
|
47876
|
+
const result = OAuthErrorResponseSchema.parse(JSON.parse(body));
|
|
47877
|
+
const { error: error45, error_description, error_uri } = result;
|
|
47878
|
+
const errorClass = OAUTH_ERRORS[error45] || ServerError;
|
|
47879
|
+
return new errorClass(error_description || "", error_uri);
|
|
47880
|
+
} catch (error45) {
|
|
47881
|
+
const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ""}Invalid OAuth error response: ${error45}. Raw body: ${body}`;
|
|
47882
|
+
return new ServerError(errorMessage);
|
|
47883
|
+
}
|
|
47884
|
+
}
|
|
47885
|
+
async function auth(provider, options) {
|
|
47886
|
+
try {
|
|
47887
|
+
return await authInternal(provider, options);
|
|
47888
|
+
} catch (error45) {
|
|
47889
|
+
if (error45 instanceof InvalidClientError || error45 instanceof UnauthorizedClientError) {
|
|
47890
|
+
await provider.invalidateCredentials?.("all");
|
|
47891
|
+
return await authInternal(provider, options);
|
|
47892
|
+
} else if (error45 instanceof InvalidGrantError) {
|
|
47893
|
+
await provider.invalidateCredentials?.("tokens");
|
|
47894
|
+
return await authInternal(provider, options);
|
|
47895
|
+
}
|
|
47896
|
+
throw error45;
|
|
47897
|
+
}
|
|
47898
|
+
}
|
|
47899
|
+
async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
|
|
47900
|
+
let resourceMetadata;
|
|
47901
|
+
let authorizationServerUrl;
|
|
47902
|
+
try {
|
|
47903
|
+
resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl }, fetchFn);
|
|
47904
|
+
if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
|
|
47905
|
+
authorizationServerUrl = resourceMetadata.authorization_servers[0];
|
|
47906
|
+
}
|
|
47907
|
+
} catch {}
|
|
47908
|
+
if (!authorizationServerUrl) {
|
|
47909
|
+
authorizationServerUrl = new URL("/", serverUrl);
|
|
47910
|
+
}
|
|
47911
|
+
const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
|
|
47912
|
+
const metadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, {
|
|
47913
|
+
fetchFn
|
|
47914
|
+
});
|
|
47915
|
+
let clientInformation = await Promise.resolve(provider.clientInformation());
|
|
47916
|
+
if (!clientInformation) {
|
|
47917
|
+
if (authorizationCode !== undefined) {
|
|
47918
|
+
throw new Error("Existing OAuth client information is required when exchanging an authorization code");
|
|
47919
|
+
}
|
|
47920
|
+
const supportsUrlBasedClientId = metadata?.client_id_metadata_document_supported === true;
|
|
47921
|
+
const clientMetadataUrl = provider.clientMetadataUrl;
|
|
47922
|
+
if (clientMetadataUrl && !isHttpsUrl(clientMetadataUrl)) {
|
|
47923
|
+
throw new InvalidClientMetadataError(`clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: ${clientMetadataUrl}`);
|
|
47924
|
+
}
|
|
47925
|
+
const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
|
|
47926
|
+
if (shouldUseUrlBasedClientId) {
|
|
47927
|
+
clientInformation = {
|
|
47928
|
+
client_id: clientMetadataUrl
|
|
47929
|
+
};
|
|
47930
|
+
await provider.saveClientInformation?.(clientInformation);
|
|
47931
|
+
} else {
|
|
47932
|
+
if (!provider.saveClientInformation) {
|
|
47933
|
+
throw new Error("OAuth client information must be saveable for dynamic registration");
|
|
47934
|
+
}
|
|
47935
|
+
const fullInformation = await registerClient(authorizationServerUrl, {
|
|
47936
|
+
metadata,
|
|
47937
|
+
clientMetadata: provider.clientMetadata,
|
|
47938
|
+
fetchFn
|
|
47939
|
+
});
|
|
47940
|
+
await provider.saveClientInformation(fullInformation);
|
|
47941
|
+
clientInformation = fullInformation;
|
|
47942
|
+
}
|
|
47943
|
+
}
|
|
47944
|
+
const nonInteractiveFlow = !provider.redirectUrl;
|
|
47945
|
+
if (authorizationCode !== undefined || nonInteractiveFlow) {
|
|
47946
|
+
const tokens2 = await fetchToken(provider, authorizationServerUrl, {
|
|
47947
|
+
metadata,
|
|
47948
|
+
resource,
|
|
47949
|
+
authorizationCode,
|
|
47950
|
+
fetchFn
|
|
47951
|
+
});
|
|
47952
|
+
await provider.saveTokens(tokens2);
|
|
47953
|
+
return "AUTHORIZED";
|
|
47954
|
+
}
|
|
47955
|
+
const tokens = await provider.tokens();
|
|
47956
|
+
if (tokens?.refresh_token) {
|
|
47957
|
+
try {
|
|
47958
|
+
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
47959
|
+
metadata,
|
|
47960
|
+
clientInformation,
|
|
47961
|
+
refreshToken: tokens.refresh_token,
|
|
47962
|
+
resource,
|
|
47963
|
+
addClientAuthentication: provider.addClientAuthentication,
|
|
47964
|
+
fetchFn
|
|
47965
|
+
});
|
|
47966
|
+
await provider.saveTokens(newTokens);
|
|
47967
|
+
return "AUTHORIZED";
|
|
47968
|
+
} catch (error45) {
|
|
47969
|
+
if (!(error45 instanceof OAuthError) || error45 instanceof ServerError) {} else {
|
|
47970
|
+
throw error45;
|
|
47971
|
+
}
|
|
47972
|
+
}
|
|
47973
|
+
}
|
|
47974
|
+
const state2 = provider.state ? await provider.state() : undefined;
|
|
47975
|
+
const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
|
|
47976
|
+
metadata,
|
|
47977
|
+
clientInformation,
|
|
47978
|
+
state: state2,
|
|
47979
|
+
redirectUrl: provider.redirectUrl,
|
|
47980
|
+
scope: scope || resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope,
|
|
47981
|
+
resource
|
|
47982
|
+
});
|
|
47983
|
+
await provider.saveCodeVerifier(codeVerifier);
|
|
47984
|
+
await provider.redirectToAuthorization(authorizationUrl);
|
|
47985
|
+
return "REDIRECT";
|
|
47986
|
+
}
|
|
47987
|
+
function isHttpsUrl(value) {
|
|
47988
|
+
if (!value)
|
|
47989
|
+
return false;
|
|
47990
|
+
try {
|
|
47991
|
+
const url2 = new URL(value);
|
|
47992
|
+
return url2.protocol === "https:" && url2.pathname !== "/";
|
|
47993
|
+
} catch {
|
|
47994
|
+
return false;
|
|
47995
|
+
}
|
|
47996
|
+
}
|
|
47997
|
+
async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
|
47998
|
+
const defaultResource = resourceUrlFromServerUrl(serverUrl);
|
|
47999
|
+
if (provider.validateResourceURL) {
|
|
48000
|
+
return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
|
|
48001
|
+
}
|
|
48002
|
+
if (!resourceMetadata) {
|
|
48003
|
+
return;
|
|
48004
|
+
}
|
|
48005
|
+
if (!checkResourceAllowed({ requestedResource: defaultResource, configuredResource: resourceMetadata.resource })) {
|
|
48006
|
+
throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)`);
|
|
48007
|
+
}
|
|
48008
|
+
return new URL(resourceMetadata.resource);
|
|
48009
|
+
}
|
|
48010
|
+
function extractWWWAuthenticateParams(res) {
|
|
48011
|
+
const authenticateHeader = res.headers.get("WWW-Authenticate");
|
|
48012
|
+
if (!authenticateHeader) {
|
|
48013
|
+
return {};
|
|
48014
|
+
}
|
|
48015
|
+
const [type2, scheme] = authenticateHeader.split(" ");
|
|
48016
|
+
if (type2.toLowerCase() !== "bearer" || !scheme) {
|
|
48017
|
+
return {};
|
|
48018
|
+
}
|
|
48019
|
+
const resourceMetadataMatch = extractFieldFromWwwAuth(res, "resource_metadata") || undefined;
|
|
48020
|
+
let resourceMetadataUrl;
|
|
48021
|
+
if (resourceMetadataMatch) {
|
|
48022
|
+
try {
|
|
48023
|
+
resourceMetadataUrl = new URL(resourceMetadataMatch);
|
|
48024
|
+
} catch {}
|
|
48025
|
+
}
|
|
48026
|
+
const scope = extractFieldFromWwwAuth(res, "scope") || undefined;
|
|
48027
|
+
const error45 = extractFieldFromWwwAuth(res, "error") || undefined;
|
|
48028
|
+
return {
|
|
48029
|
+
resourceMetadataUrl,
|
|
48030
|
+
scope,
|
|
48031
|
+
error: error45
|
|
48032
|
+
};
|
|
48033
|
+
}
|
|
48034
|
+
function extractFieldFromWwwAuth(response2, fieldName) {
|
|
48035
|
+
const wwwAuthHeader = response2.headers.get("WWW-Authenticate");
|
|
48036
|
+
if (!wwwAuthHeader) {
|
|
48037
|
+
return null;
|
|
48038
|
+
}
|
|
48039
|
+
const pattern = new RegExp(`${fieldName}=(?:"([^"]+)"|([^\\s,]+))`);
|
|
48040
|
+
const match = wwwAuthHeader.match(pattern);
|
|
48041
|
+
if (match) {
|
|
48042
|
+
return match[1] || match[2];
|
|
48043
|
+
}
|
|
48044
|
+
return null;
|
|
48045
|
+
}
|
|
48046
|
+
async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn = fetch) {
|
|
48047
|
+
const response2 = await discoverMetadataWithFallback(serverUrl, "oauth-protected-resource", fetchFn, {
|
|
48048
|
+
protocolVersion: opts?.protocolVersion,
|
|
48049
|
+
metadataUrl: opts?.resourceMetadataUrl
|
|
48050
|
+
});
|
|
48051
|
+
if (!response2 || response2.status === 404) {
|
|
48052
|
+
await response2?.body?.cancel();
|
|
48053
|
+
throw new Error(`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`);
|
|
48054
|
+
}
|
|
48055
|
+
if (!response2.ok) {
|
|
48056
|
+
await response2.body?.cancel();
|
|
48057
|
+
throw new Error(`HTTP ${response2.status} trying to load well-known OAuth protected resource metadata.`);
|
|
48058
|
+
}
|
|
48059
|
+
return OAuthProtectedResourceMetadataSchema.parse(await response2.json());
|
|
48060
|
+
}
|
|
48061
|
+
async function fetchWithCorsRetry(url2, headers, fetchFn = fetch) {
|
|
48062
|
+
try {
|
|
48063
|
+
return await fetchFn(url2, { headers });
|
|
48064
|
+
} catch (error45) {
|
|
48065
|
+
if (error45 instanceof TypeError) {
|
|
48066
|
+
if (headers) {
|
|
48067
|
+
return fetchWithCorsRetry(url2, undefined, fetchFn);
|
|
48068
|
+
} else {
|
|
48069
|
+
return;
|
|
48070
|
+
}
|
|
48071
|
+
}
|
|
48072
|
+
throw error45;
|
|
48073
|
+
}
|
|
48074
|
+
}
|
|
48075
|
+
function buildWellKnownPath(wellKnownPrefix, pathname = "", options = {}) {
|
|
48076
|
+
if (pathname.endsWith("/")) {
|
|
48077
|
+
pathname = pathname.slice(0, -1);
|
|
48078
|
+
}
|
|
48079
|
+
return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`;
|
|
48080
|
+
}
|
|
48081
|
+
async function tryMetadataDiscovery(url2, protocolVersion, fetchFn = fetch) {
|
|
48082
|
+
const headers = {
|
|
48083
|
+
"MCP-Protocol-Version": protocolVersion
|
|
48084
|
+
};
|
|
48085
|
+
return await fetchWithCorsRetry(url2, headers, fetchFn);
|
|
48086
|
+
}
|
|
48087
|
+
function shouldAttemptFallback(response2, pathname) {
|
|
48088
|
+
return !response2 || response2.status >= 400 && response2.status < 500 && pathname !== "/";
|
|
48089
|
+
}
|
|
48090
|
+
async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
|
|
48091
|
+
const issuer = new URL(serverUrl);
|
|
48092
|
+
const protocolVersion = opts?.protocolVersion ?? LATEST_PROTOCOL_VERSION;
|
|
48093
|
+
let url2;
|
|
48094
|
+
if (opts?.metadataUrl) {
|
|
48095
|
+
url2 = new URL(opts.metadataUrl);
|
|
48096
|
+
} else {
|
|
48097
|
+
const wellKnownPath = buildWellKnownPath(wellKnownType, issuer.pathname);
|
|
48098
|
+
url2 = new URL(wellKnownPath, opts?.metadataServerUrl ?? issuer);
|
|
48099
|
+
url2.search = issuer.search;
|
|
48100
|
+
}
|
|
48101
|
+
let response2 = await tryMetadataDiscovery(url2, protocolVersion, fetchFn);
|
|
48102
|
+
if (!opts?.metadataUrl && shouldAttemptFallback(response2, issuer.pathname)) {
|
|
48103
|
+
const rootUrl = new URL(`/.well-known/${wellKnownType}`, issuer);
|
|
48104
|
+
response2 = await tryMetadataDiscovery(rootUrl, protocolVersion, fetchFn);
|
|
48105
|
+
}
|
|
48106
|
+
return response2;
|
|
48107
|
+
}
|
|
48108
|
+
function buildDiscoveryUrls(authorizationServerUrl) {
|
|
48109
|
+
const url2 = typeof authorizationServerUrl === "string" ? new URL(authorizationServerUrl) : authorizationServerUrl;
|
|
48110
|
+
const hasPath = url2.pathname !== "/";
|
|
48111
|
+
const urlsToTry = [];
|
|
48112
|
+
if (!hasPath) {
|
|
48113
|
+
urlsToTry.push({
|
|
48114
|
+
url: new URL("/.well-known/oauth-authorization-server", url2.origin),
|
|
48115
|
+
type: "oauth"
|
|
48116
|
+
});
|
|
48117
|
+
urlsToTry.push({
|
|
48118
|
+
url: new URL(`/.well-known/openid-configuration`, url2.origin),
|
|
48119
|
+
type: "oidc"
|
|
48120
|
+
});
|
|
48121
|
+
return urlsToTry;
|
|
48122
|
+
}
|
|
48123
|
+
let pathname = url2.pathname;
|
|
48124
|
+
if (pathname.endsWith("/")) {
|
|
48125
|
+
pathname = pathname.slice(0, -1);
|
|
48126
|
+
}
|
|
48127
|
+
urlsToTry.push({
|
|
48128
|
+
url: new URL(`/.well-known/oauth-authorization-server${pathname}`, url2.origin),
|
|
48129
|
+
type: "oauth"
|
|
48130
|
+
});
|
|
48131
|
+
urlsToTry.push({
|
|
48132
|
+
url: new URL(`/.well-known/openid-configuration${pathname}`, url2.origin),
|
|
48133
|
+
type: "oidc"
|
|
48134
|
+
});
|
|
48135
|
+
urlsToTry.push({
|
|
48136
|
+
url: new URL(`${pathname}/.well-known/openid-configuration`, url2.origin),
|
|
48137
|
+
type: "oidc"
|
|
48138
|
+
});
|
|
48139
|
+
return urlsToTry;
|
|
48140
|
+
}
|
|
48141
|
+
async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = LATEST_PROTOCOL_VERSION } = {}) {
|
|
48142
|
+
const headers = {
|
|
48143
|
+
"MCP-Protocol-Version": protocolVersion,
|
|
48144
|
+
Accept: "application/json"
|
|
48145
|
+
};
|
|
48146
|
+
const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
|
|
48147
|
+
for (const { url: endpointUrl, type: type2 } of urlsToTry) {
|
|
48148
|
+
const response2 = await fetchWithCorsRetry(endpointUrl, headers, fetchFn);
|
|
48149
|
+
if (!response2) {
|
|
48150
|
+
continue;
|
|
48151
|
+
}
|
|
48152
|
+
if (!response2.ok) {
|
|
48153
|
+
await response2.body?.cancel();
|
|
48154
|
+
if (response2.status >= 400 && response2.status < 500) {
|
|
48155
|
+
continue;
|
|
48156
|
+
}
|
|
48157
|
+
throw new Error(`HTTP ${response2.status} trying to load ${type2 === "oauth" ? "OAuth" : "OpenID provider"} metadata from ${endpointUrl}`);
|
|
48158
|
+
}
|
|
48159
|
+
if (type2 === "oauth") {
|
|
48160
|
+
return OAuthMetadataSchema.parse(await response2.json());
|
|
48161
|
+
} else {
|
|
48162
|
+
return OpenIdProviderDiscoveryMetadataSchema.parse(await response2.json());
|
|
48163
|
+
}
|
|
48164
|
+
}
|
|
48165
|
+
return;
|
|
48166
|
+
}
|
|
48167
|
+
async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state: state2, resource }) {
|
|
48168
|
+
let authorizationUrl;
|
|
48169
|
+
if (metadata) {
|
|
48170
|
+
authorizationUrl = new URL(metadata.authorization_endpoint);
|
|
48171
|
+
if (!metadata.response_types_supported.includes(AUTHORIZATION_CODE_RESPONSE_TYPE)) {
|
|
48172
|
+
throw new Error(`Incompatible auth server: does not support response type ${AUTHORIZATION_CODE_RESPONSE_TYPE}`);
|
|
48173
|
+
}
|
|
48174
|
+
if (metadata.code_challenge_methods_supported && !metadata.code_challenge_methods_supported.includes(AUTHORIZATION_CODE_CHALLENGE_METHOD)) {
|
|
48175
|
+
throw new Error(`Incompatible auth server: does not support code challenge method ${AUTHORIZATION_CODE_CHALLENGE_METHOD}`);
|
|
48176
|
+
}
|
|
48177
|
+
} else {
|
|
48178
|
+
authorizationUrl = new URL("/authorize", authorizationServerUrl);
|
|
48179
|
+
}
|
|
48180
|
+
const challenge = await pkceChallenge();
|
|
48181
|
+
const codeVerifier = challenge.code_verifier;
|
|
48182
|
+
const codeChallenge = challenge.code_challenge;
|
|
48183
|
+
authorizationUrl.searchParams.set("response_type", AUTHORIZATION_CODE_RESPONSE_TYPE);
|
|
48184
|
+
authorizationUrl.searchParams.set("client_id", clientInformation.client_id);
|
|
48185
|
+
authorizationUrl.searchParams.set("code_challenge", codeChallenge);
|
|
48186
|
+
authorizationUrl.searchParams.set("code_challenge_method", AUTHORIZATION_CODE_CHALLENGE_METHOD);
|
|
48187
|
+
authorizationUrl.searchParams.set("redirect_uri", String(redirectUrl));
|
|
48188
|
+
if (state2) {
|
|
48189
|
+
authorizationUrl.searchParams.set("state", state2);
|
|
48190
|
+
}
|
|
48191
|
+
if (scope) {
|
|
48192
|
+
authorizationUrl.searchParams.set("scope", scope);
|
|
48193
|
+
}
|
|
48194
|
+
if (scope?.includes("offline_access")) {
|
|
48195
|
+
authorizationUrl.searchParams.append("prompt", "consent");
|
|
48196
|
+
}
|
|
48197
|
+
if (resource) {
|
|
48198
|
+
authorizationUrl.searchParams.set("resource", resource.href);
|
|
48199
|
+
}
|
|
48200
|
+
return { authorizationUrl, codeVerifier };
|
|
48201
|
+
}
|
|
48202
|
+
function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri) {
|
|
48203
|
+
return new URLSearchParams({
|
|
48204
|
+
grant_type: "authorization_code",
|
|
48205
|
+
code: authorizationCode,
|
|
48206
|
+
code_verifier: codeVerifier,
|
|
48207
|
+
redirect_uri: String(redirectUri)
|
|
48208
|
+
});
|
|
48209
|
+
}
|
|
48210
|
+
async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
|
|
48211
|
+
const tokenUrl = metadata?.token_endpoint ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl);
|
|
48212
|
+
const headers = new Headers({
|
|
48213
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
48214
|
+
Accept: "application/json"
|
|
48215
|
+
});
|
|
48216
|
+
if (resource) {
|
|
48217
|
+
tokenRequestParams.set("resource", resource.href);
|
|
48218
|
+
}
|
|
48219
|
+
if (addClientAuthentication) {
|
|
48220
|
+
await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
|
|
48221
|
+
} else if (clientInformation) {
|
|
48222
|
+
const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? [];
|
|
48223
|
+
const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
|
|
48224
|
+
applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
|
|
48225
|
+
}
|
|
48226
|
+
const response2 = await (fetchFn ?? fetch)(tokenUrl, {
|
|
48227
|
+
method: "POST",
|
|
48228
|
+
headers,
|
|
48229
|
+
body: tokenRequestParams
|
|
48230
|
+
});
|
|
48231
|
+
if (!response2.ok) {
|
|
48232
|
+
throw await parseErrorResponse(response2);
|
|
48233
|
+
}
|
|
48234
|
+
return OAuthTokensSchema.parse(await response2.json());
|
|
48235
|
+
}
|
|
48236
|
+
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
|
|
48237
|
+
const tokenRequestParams = new URLSearchParams({
|
|
48238
|
+
grant_type: "refresh_token",
|
|
48239
|
+
refresh_token: refreshToken
|
|
48240
|
+
});
|
|
48241
|
+
const tokens = await executeTokenRequest(authorizationServerUrl, {
|
|
48242
|
+
metadata,
|
|
48243
|
+
tokenRequestParams,
|
|
48244
|
+
clientInformation,
|
|
48245
|
+
addClientAuthentication,
|
|
48246
|
+
resource,
|
|
48247
|
+
fetchFn
|
|
48248
|
+
});
|
|
48249
|
+
return { refresh_token: refreshToken, ...tokens };
|
|
48250
|
+
}
|
|
48251
|
+
async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
|
|
48252
|
+
const scope = provider.clientMetadata.scope;
|
|
48253
|
+
let tokenRequestParams;
|
|
48254
|
+
if (provider.prepareTokenRequest) {
|
|
48255
|
+
tokenRequestParams = await provider.prepareTokenRequest(scope);
|
|
48256
|
+
}
|
|
48257
|
+
if (!tokenRequestParams) {
|
|
48258
|
+
if (!authorizationCode) {
|
|
48259
|
+
throw new Error("Either provider.prepareTokenRequest() or authorizationCode is required");
|
|
48260
|
+
}
|
|
48261
|
+
if (!provider.redirectUrl) {
|
|
48262
|
+
throw new Error("redirectUrl is required for authorization_code flow");
|
|
48263
|
+
}
|
|
48264
|
+
const codeVerifier = await provider.codeVerifier();
|
|
48265
|
+
tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
|
|
48266
|
+
}
|
|
48267
|
+
const clientInformation = await provider.clientInformation();
|
|
48268
|
+
return executeTokenRequest(authorizationServerUrl, {
|
|
48269
|
+
metadata,
|
|
48270
|
+
tokenRequestParams,
|
|
48271
|
+
clientInformation: clientInformation ?? undefined,
|
|
48272
|
+
addClientAuthentication: provider.addClientAuthentication,
|
|
48273
|
+
resource,
|
|
48274
|
+
fetchFn
|
|
48275
|
+
});
|
|
48276
|
+
}
|
|
48277
|
+
async function registerClient(authorizationServerUrl, { metadata, clientMetadata, fetchFn }) {
|
|
48278
|
+
let registrationUrl;
|
|
48279
|
+
if (metadata) {
|
|
48280
|
+
if (!metadata.registration_endpoint) {
|
|
48281
|
+
throw new Error("Incompatible auth server: does not support dynamic client registration");
|
|
48282
|
+
}
|
|
48283
|
+
registrationUrl = new URL(metadata.registration_endpoint);
|
|
48284
|
+
} else {
|
|
48285
|
+
registrationUrl = new URL("/register", authorizationServerUrl);
|
|
48286
|
+
}
|
|
48287
|
+
const response2 = await (fetchFn ?? fetch)(registrationUrl, {
|
|
48288
|
+
method: "POST",
|
|
48289
|
+
headers: {
|
|
48290
|
+
"Content-Type": "application/json"
|
|
48291
|
+
},
|
|
48292
|
+
body: JSON.stringify(clientMetadata)
|
|
48293
|
+
});
|
|
48294
|
+
if (!response2.ok) {
|
|
48295
|
+
throw await parseErrorResponse(response2);
|
|
48296
|
+
}
|
|
48297
|
+
return OAuthClientInformationFullSchema.parse(await response2.json());
|
|
48298
|
+
}
|
|
48299
|
+
|
|
48300
|
+
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/sse.js
|
|
48301
|
+
class SseError extends Error {
|
|
48302
|
+
constructor(code, message, event) {
|
|
48303
|
+
super(`SSE error: ${message}`);
|
|
48304
|
+
this.code = code;
|
|
48305
|
+
this.event = event;
|
|
48306
|
+
}
|
|
48307
|
+
}
|
|
48308
|
+
|
|
48309
|
+
class SSEClientTransport {
|
|
48310
|
+
constructor(url2, opts) {
|
|
48311
|
+
this._url = url2;
|
|
48312
|
+
this._resourceMetadataUrl = undefined;
|
|
48313
|
+
this._scope = undefined;
|
|
48314
|
+
this._eventSourceInit = opts?.eventSourceInit;
|
|
48315
|
+
this._requestInit = opts?.requestInit;
|
|
48316
|
+
this._authProvider = opts?.authProvider;
|
|
48317
|
+
this._fetch = opts?.fetch;
|
|
48318
|
+
this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
|
|
48319
|
+
}
|
|
48320
|
+
async _authThenStart() {
|
|
48321
|
+
if (!this._authProvider) {
|
|
48322
|
+
throw new UnauthorizedError("No auth provider");
|
|
48323
|
+
}
|
|
48324
|
+
let result;
|
|
48325
|
+
try {
|
|
48326
|
+
result = await auth(this._authProvider, {
|
|
48327
|
+
serverUrl: this._url,
|
|
48328
|
+
resourceMetadataUrl: this._resourceMetadataUrl,
|
|
48329
|
+
scope: this._scope,
|
|
48330
|
+
fetchFn: this._fetchWithInit
|
|
48331
|
+
});
|
|
48332
|
+
} catch (error45) {
|
|
48333
|
+
this.onerror?.(error45);
|
|
48334
|
+
throw error45;
|
|
48335
|
+
}
|
|
48336
|
+
if (result !== "AUTHORIZED") {
|
|
48337
|
+
throw new UnauthorizedError;
|
|
48338
|
+
}
|
|
48339
|
+
return await this._startOrAuth();
|
|
48340
|
+
}
|
|
48341
|
+
async _commonHeaders() {
|
|
48342
|
+
const headers = {};
|
|
48343
|
+
if (this._authProvider) {
|
|
48344
|
+
const tokens = await this._authProvider.tokens();
|
|
48345
|
+
if (tokens) {
|
|
48346
|
+
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
|
48347
|
+
}
|
|
48348
|
+
}
|
|
48349
|
+
if (this._protocolVersion) {
|
|
48350
|
+
headers["mcp-protocol-version"] = this._protocolVersion;
|
|
48351
|
+
}
|
|
48352
|
+
const extraHeaders = normalizeHeaders(this._requestInit?.headers);
|
|
48353
|
+
return new Headers({
|
|
48354
|
+
...headers,
|
|
48355
|
+
...extraHeaders
|
|
48356
|
+
});
|
|
48357
|
+
}
|
|
48358
|
+
_startOrAuth() {
|
|
48359
|
+
const fetchImpl = this?._eventSourceInit?.fetch ?? this._fetch ?? fetch;
|
|
48360
|
+
return new Promise((resolve8, reject) => {
|
|
48361
|
+
this._eventSource = new EventSource(this._url.href, {
|
|
48362
|
+
...this._eventSourceInit,
|
|
48363
|
+
fetch: async (url2, init) => {
|
|
48364
|
+
const headers = await this._commonHeaders();
|
|
48365
|
+
headers.set("Accept", "text/event-stream");
|
|
48366
|
+
const response2 = await fetchImpl(url2, {
|
|
48367
|
+
...init,
|
|
48368
|
+
headers
|
|
48369
|
+
});
|
|
48370
|
+
if (response2.status === 401 && response2.headers.has("www-authenticate")) {
|
|
48371
|
+
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response2);
|
|
48372
|
+
this._resourceMetadataUrl = resourceMetadataUrl;
|
|
48373
|
+
this._scope = scope;
|
|
48374
|
+
}
|
|
48375
|
+
return response2;
|
|
48376
|
+
}
|
|
48377
|
+
});
|
|
48378
|
+
this._abortController = new AbortController;
|
|
48379
|
+
this._eventSource.onerror = (event) => {
|
|
48380
|
+
if (event.code === 401 && this._authProvider) {
|
|
48381
|
+
this._authThenStart().then(resolve8, reject);
|
|
48382
|
+
return;
|
|
48383
|
+
}
|
|
48384
|
+
const error45 = new SseError(event.code, event.message, event);
|
|
48385
|
+
reject(error45);
|
|
48386
|
+
this.onerror?.(error45);
|
|
48387
|
+
};
|
|
48388
|
+
this._eventSource.onopen = () => {};
|
|
48389
|
+
this._eventSource.addEventListener("endpoint", (event) => {
|
|
48390
|
+
const messageEvent = event;
|
|
48391
|
+
try {
|
|
48392
|
+
this._endpoint = new URL(messageEvent.data, this._url);
|
|
48393
|
+
if (this._endpoint.origin !== this._url.origin) {
|
|
48394
|
+
throw new Error(`Endpoint origin does not match connection origin: ${this._endpoint.origin}`);
|
|
48395
|
+
}
|
|
48396
|
+
} catch (error45) {
|
|
48397
|
+
reject(error45);
|
|
48398
|
+
this.onerror?.(error45);
|
|
48399
|
+
this.close();
|
|
48400
|
+
return;
|
|
48401
|
+
}
|
|
48402
|
+
resolve8();
|
|
48403
|
+
});
|
|
48404
|
+
this._eventSource.onmessage = (event) => {
|
|
48405
|
+
const messageEvent = event;
|
|
48406
|
+
let message;
|
|
48407
|
+
try {
|
|
48408
|
+
message = JSONRPCMessageSchema.parse(JSON.parse(messageEvent.data));
|
|
48409
|
+
} catch (error45) {
|
|
48410
|
+
this.onerror?.(error45);
|
|
48411
|
+
return;
|
|
48412
|
+
}
|
|
48413
|
+
this.onmessage?.(message);
|
|
48414
|
+
};
|
|
48415
|
+
});
|
|
48416
|
+
}
|
|
48417
|
+
async start() {
|
|
48418
|
+
if (this._eventSource) {
|
|
48419
|
+
throw new Error("SSEClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
|
48420
|
+
}
|
|
48421
|
+
return await this._startOrAuth();
|
|
48422
|
+
}
|
|
48423
|
+
async finishAuth(authorizationCode) {
|
|
48424
|
+
if (!this._authProvider) {
|
|
48425
|
+
throw new UnauthorizedError("No auth provider");
|
|
48426
|
+
}
|
|
48427
|
+
const result = await auth(this._authProvider, {
|
|
48428
|
+
serverUrl: this._url,
|
|
48429
|
+
authorizationCode,
|
|
48430
|
+
resourceMetadataUrl: this._resourceMetadataUrl,
|
|
48431
|
+
scope: this._scope,
|
|
48432
|
+
fetchFn: this._fetchWithInit
|
|
48433
|
+
});
|
|
48434
|
+
if (result !== "AUTHORIZED") {
|
|
48435
|
+
throw new UnauthorizedError("Failed to authorize");
|
|
48436
|
+
}
|
|
48437
|
+
}
|
|
48438
|
+
async close() {
|
|
48439
|
+
this._abortController?.abort();
|
|
48440
|
+
this._eventSource?.close();
|
|
48441
|
+
this.onclose?.();
|
|
48442
|
+
}
|
|
48443
|
+
async send(message) {
|
|
48444
|
+
if (!this._endpoint) {
|
|
48445
|
+
throw new Error("Not connected");
|
|
48446
|
+
}
|
|
48447
|
+
try {
|
|
48448
|
+
const headers = await this._commonHeaders();
|
|
48449
|
+
headers.set("content-type", "application/json");
|
|
48450
|
+
const init = {
|
|
48451
|
+
...this._requestInit,
|
|
48452
|
+
method: "POST",
|
|
48453
|
+
headers,
|
|
48454
|
+
body: JSON.stringify(message),
|
|
48455
|
+
signal: this._abortController?.signal
|
|
48456
|
+
};
|
|
48457
|
+
const response2 = await (this._fetch ?? fetch)(this._endpoint, init);
|
|
48458
|
+
if (!response2.ok) {
|
|
48459
|
+
const text = await response2.text().catch(() => null);
|
|
48460
|
+
if (response2.status === 401 && this._authProvider) {
|
|
48461
|
+
const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response2);
|
|
48462
|
+
this._resourceMetadataUrl = resourceMetadataUrl;
|
|
48463
|
+
this._scope = scope;
|
|
48464
|
+
const result = await auth(this._authProvider, {
|
|
48465
|
+
serverUrl: this._url,
|
|
48466
|
+
resourceMetadataUrl: this._resourceMetadataUrl,
|
|
48467
|
+
scope: this._scope,
|
|
48468
|
+
fetchFn: this._fetchWithInit
|
|
48469
|
+
});
|
|
48470
|
+
if (result !== "AUTHORIZED") {
|
|
48471
|
+
throw new UnauthorizedError;
|
|
48472
|
+
}
|
|
48473
|
+
return this.send(message);
|
|
48474
|
+
}
|
|
48475
|
+
throw new Error(`Error POSTing to endpoint (HTTP ${response2.status}): ${text}`);
|
|
48476
|
+
}
|
|
48477
|
+
await response2.body?.cancel();
|
|
48478
|
+
} catch (error45) {
|
|
48479
|
+
this.onerror?.(error45);
|
|
48480
|
+
throw error45;
|
|
48481
|
+
}
|
|
48482
|
+
}
|
|
48483
|
+
setProtocolVersion(version2) {
|
|
48484
|
+
this._protocolVersion = version2;
|
|
48485
|
+
}
|
|
48486
|
+
}
|
|
48487
|
+
|
|
48488
|
+
// src/features/skill-mcp-manager/env-cleaner.ts
|
|
48489
|
+
var EXCLUDED_ENV_PATTERNS = [
|
|
48490
|
+
/^NPM_CONFIG_/i,
|
|
48491
|
+
/^npm_config_/,
|
|
48492
|
+
/^YARN_/,
|
|
48493
|
+
/^PNPM_/,
|
|
48494
|
+
/^NO_UPDATE_NOTIFIER$/
|
|
48495
|
+
];
|
|
48496
|
+
function createCleanMcpEnvironment(customEnv = {}) {
|
|
48497
|
+
const cleanEnv = {};
|
|
48498
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
48499
|
+
if (value === undefined)
|
|
48500
|
+
continue;
|
|
48501
|
+
const shouldExclude = EXCLUDED_ENV_PATTERNS.some((pattern) => pattern.test(key));
|
|
48502
|
+
if (!shouldExclude) {
|
|
48503
|
+
cleanEnv[key] = value;
|
|
48504
|
+
}
|
|
48505
|
+
}
|
|
48506
|
+
Object.assign(cleanEnv, customEnv);
|
|
48507
|
+
return cleanEnv;
|
|
48508
|
+
}
|
|
48509
|
+
|
|
48510
|
+
// src/features/knowledge-provider/mcp-manager.ts
|
|
48511
|
+
class KnowledgeMcpManager {
|
|
48512
|
+
clients = new Map;
|
|
48513
|
+
pendingConnections = new Map;
|
|
48514
|
+
serverConfigs = new Map;
|
|
48515
|
+
cleanupRegistered = false;
|
|
48516
|
+
cleanupInterval = null;
|
|
48517
|
+
IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
48518
|
+
registerServer(config3) {
|
|
48519
|
+
this.serverConfigs.set(config3.name, config3);
|
|
48520
|
+
}
|
|
48521
|
+
async unregisterServer(serverName) {
|
|
48522
|
+
this.serverConfigs.delete(serverName);
|
|
48523
|
+
await this.disconnectServer(serverName);
|
|
48524
|
+
}
|
|
48525
|
+
async isServerAvailable(serverName) {
|
|
48526
|
+
const config3 = this.serverConfigs.get(serverName);
|
|
48527
|
+
if (!config3) {
|
|
48528
|
+
return false;
|
|
48529
|
+
}
|
|
48530
|
+
try {
|
|
48531
|
+
const client2 = await this.getOrCreateClient(serverName);
|
|
48532
|
+
return client2 !== null;
|
|
48533
|
+
} catch {
|
|
48534
|
+
return false;
|
|
48535
|
+
}
|
|
48536
|
+
}
|
|
48537
|
+
async invoke(serverName, toolName, args) {
|
|
48538
|
+
const client2 = await this.getOrCreateClient(serverName);
|
|
48539
|
+
if (!client2) {
|
|
48540
|
+
throw new Error(`MCP server "${serverName}" is not available`);
|
|
48541
|
+
}
|
|
48542
|
+
const result = await client2.callTool({ name: toolName, arguments: args });
|
|
48543
|
+
return result.content;
|
|
48544
|
+
}
|
|
48545
|
+
async getOrCreateClient(serverName) {
|
|
48546
|
+
const existing = this.clients.get(serverName);
|
|
48547
|
+
if (existing) {
|
|
48548
|
+
existing.lastUsedAt = Date.now();
|
|
48549
|
+
return existing.client;
|
|
48550
|
+
}
|
|
48551
|
+
const pending = this.pendingConnections.get(serverName);
|
|
48552
|
+
if (pending) {
|
|
48553
|
+
return pending;
|
|
48554
|
+
}
|
|
48555
|
+
const config3 = this.serverConfigs.get(serverName);
|
|
48556
|
+
if (!config3) {
|
|
48557
|
+
return null;
|
|
48558
|
+
}
|
|
48559
|
+
const expandedConfig = expandEnvVarsInObject(config3);
|
|
48560
|
+
const connectionPromise = this.createClient(serverName, expandedConfig);
|
|
48561
|
+
this.pendingConnections.set(serverName, connectionPromise);
|
|
48562
|
+
try {
|
|
48563
|
+
const client2 = await connectionPromise;
|
|
48564
|
+
return client2;
|
|
48565
|
+
} finally {
|
|
48566
|
+
this.pendingConnections.delete(serverName);
|
|
48567
|
+
}
|
|
48568
|
+
}
|
|
48569
|
+
async createClient(serverName, config3) {
|
|
48570
|
+
const transportType = config3.transport ?? "stdio";
|
|
48571
|
+
this.registerProcessCleanup();
|
|
48572
|
+
const client2 = new Client({ name: `knowledge-mcp-${serverName}`, version: "1.0.0" }, { capabilities: {} });
|
|
48573
|
+
let transport;
|
|
48574
|
+
if (transportType === "sse") {
|
|
48575
|
+
transport = await this.createSseTransport(serverName, config3, client2);
|
|
48576
|
+
} else {
|
|
48577
|
+
transport = await this.createStdioTransport(serverName, config3, client2);
|
|
48578
|
+
}
|
|
48579
|
+
this.clients.set(serverName, {
|
|
48580
|
+
client: client2,
|
|
48581
|
+
transport,
|
|
48582
|
+
transportType,
|
|
48583
|
+
serverName,
|
|
48584
|
+
lastUsedAt: Date.now()
|
|
48585
|
+
});
|
|
48586
|
+
this.startCleanupTimer();
|
|
48587
|
+
return client2;
|
|
48588
|
+
}
|
|
48589
|
+
async createStdioTransport(serverName, config3, client2) {
|
|
48590
|
+
if (!config3.command) {
|
|
48591
|
+
throw new Error(`MCP server "${serverName}" is missing required 'command' field for stdio transport.
|
|
48592
|
+
|
|
48593
|
+
` + `Example:
|
|
48594
|
+
` + ` {
|
|
48595
|
+
` + ` "name": "${serverName}",
|
|
48596
|
+
` + ` "command": "npx",
|
|
48597
|
+
` + ` "args": ["-y", "@some/mcp-server"],
|
|
48598
|
+
` + ` "searchTool": "search"
|
|
48599
|
+
` + ` }`);
|
|
48600
|
+
}
|
|
48601
|
+
const command = config3.command;
|
|
48602
|
+
const args = config3.args || [];
|
|
48603
|
+
const mergedEnv = createCleanMcpEnvironment(config3.env);
|
|
48604
|
+
const transport = new StdioClientTransport({
|
|
48605
|
+
command,
|
|
48606
|
+
args,
|
|
48607
|
+
env: mergedEnv,
|
|
48608
|
+
stderr: "ignore"
|
|
48609
|
+
});
|
|
48610
|
+
try {
|
|
48611
|
+
await client2.connect(transport);
|
|
48612
|
+
} catch (error45) {
|
|
48613
|
+
try {
|
|
48614
|
+
await transport.close();
|
|
48615
|
+
} catch {}
|
|
48616
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
48617
|
+
throw new Error(`Failed to connect to MCP server "${serverName}" via stdio.
|
|
48618
|
+
|
|
48619
|
+
` + `Command: ${command} ${args.join(" ")}
|
|
48620
|
+
` + `Reason: ${errorMessage}
|
|
48621
|
+
|
|
48622
|
+
` + `Hints:
|
|
48623
|
+
` + ` - Ensure the command is installed and available in PATH
|
|
48624
|
+
` + ` - Check if the MCP server package exists
|
|
48625
|
+
` + ` - Verify the args are correct for this server`);
|
|
48626
|
+
}
|
|
48627
|
+
return transport;
|
|
48628
|
+
}
|
|
48629
|
+
async createSseTransport(serverName, config3, client2) {
|
|
48630
|
+
if (!config3.url) {
|
|
48631
|
+
throw new Error(`MCP server "${serverName}" is missing required 'url' field for SSE transport.
|
|
48632
|
+
|
|
48633
|
+
` + `Example:
|
|
48634
|
+
` + ` {
|
|
48635
|
+
` + ` "name": "${serverName}",
|
|
48636
|
+
` + ` "transport": "sse",
|
|
48637
|
+
` + ` "url": "http://localhost:60062/sse",
|
|
48638
|
+
` + ` "searchTool": "search"
|
|
48639
|
+
` + ` }
|
|
48640
|
+
|
|
48641
|
+
` + `Note: SSE servers must be started manually before connecting.`);
|
|
48642
|
+
}
|
|
48643
|
+
const transport = new SSEClientTransport(new URL(config3.url));
|
|
48644
|
+
try {
|
|
48645
|
+
await client2.connect(transport);
|
|
48646
|
+
} catch (error45) {
|
|
48647
|
+
try {
|
|
48648
|
+
await transport.close();
|
|
48649
|
+
} catch {}
|
|
48650
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
48651
|
+
throw new Error(`Failed to connect to MCP server "${serverName}" via SSE.
|
|
48652
|
+
|
|
48653
|
+
` + `URL: ${config3.url}
|
|
48654
|
+
` + `Reason: ${errorMessage}
|
|
48655
|
+
|
|
48656
|
+
` + `Hints:
|
|
48657
|
+
` + ` - Ensure the MCP server is running at ${config3.url}
|
|
48658
|
+
` + ` - SSE servers (like Dust CLI) must be started manually
|
|
48659
|
+
` + ` - Check if the port is correct (Dust uses dynamic ports)
|
|
48660
|
+
` + ` - Verify the URL ends with /sse if required by the server`);
|
|
48661
|
+
}
|
|
48662
|
+
return transport;
|
|
48663
|
+
}
|
|
48664
|
+
async disconnectServer(serverName) {
|
|
48665
|
+
const managed = this.clients.get(serverName);
|
|
48666
|
+
if (!managed) {
|
|
48667
|
+
return;
|
|
48668
|
+
}
|
|
48669
|
+
this.clients.delete(serverName);
|
|
48670
|
+
try {
|
|
48671
|
+
await managed.client.close();
|
|
48672
|
+
} catch {}
|
|
48673
|
+
try {
|
|
48674
|
+
await managed.transport.close();
|
|
48675
|
+
} catch {}
|
|
48676
|
+
}
|
|
48677
|
+
async disconnectAll() {
|
|
48678
|
+
this.stopCleanupTimer();
|
|
48679
|
+
const clients = Array.from(this.clients.values());
|
|
48680
|
+
this.clients.clear();
|
|
48681
|
+
for (const managed of clients) {
|
|
48682
|
+
try {
|
|
48683
|
+
await managed.client.close();
|
|
48684
|
+
} catch {}
|
|
48685
|
+
try {
|
|
48686
|
+
await managed.transport.close();
|
|
48687
|
+
} catch {}
|
|
48688
|
+
}
|
|
48689
|
+
}
|
|
48690
|
+
registerProcessCleanup() {
|
|
48691
|
+
if (this.cleanupRegistered)
|
|
48692
|
+
return;
|
|
48693
|
+
this.cleanupRegistered = true;
|
|
48694
|
+
const cleanup = async () => {
|
|
48695
|
+
for (const [, managed] of this.clients) {
|
|
48696
|
+
try {
|
|
48697
|
+
await managed.client.close();
|
|
48698
|
+
} catch {}
|
|
48699
|
+
try {
|
|
48700
|
+
await managed.transport.close();
|
|
48701
|
+
} catch {}
|
|
48702
|
+
}
|
|
48703
|
+
this.clients.clear();
|
|
48704
|
+
this.pendingConnections.clear();
|
|
48705
|
+
};
|
|
48706
|
+
process.on("SIGINT", async () => {
|
|
48707
|
+
await cleanup();
|
|
48708
|
+
process.exit(0);
|
|
48709
|
+
});
|
|
48710
|
+
process.on("SIGTERM", async () => {
|
|
48711
|
+
await cleanup();
|
|
48712
|
+
process.exit(0);
|
|
48713
|
+
});
|
|
48714
|
+
if (process.platform === "win32") {
|
|
48715
|
+
process.on("SIGBREAK", async () => {
|
|
48716
|
+
await cleanup();
|
|
48717
|
+
process.exit(0);
|
|
48718
|
+
});
|
|
48719
|
+
}
|
|
48720
|
+
}
|
|
48721
|
+
startCleanupTimer() {
|
|
48722
|
+
if (this.cleanupInterval)
|
|
48723
|
+
return;
|
|
48724
|
+
this.cleanupInterval = setInterval(() => {
|
|
48725
|
+
this.cleanupIdleClients();
|
|
48726
|
+
}, 60000);
|
|
48727
|
+
this.cleanupInterval.unref();
|
|
48728
|
+
}
|
|
48729
|
+
stopCleanupTimer() {
|
|
48730
|
+
if (this.cleanupInterval) {
|
|
48731
|
+
clearInterval(this.cleanupInterval);
|
|
48732
|
+
this.cleanupInterval = null;
|
|
48733
|
+
}
|
|
48734
|
+
}
|
|
48735
|
+
async cleanupIdleClients() {
|
|
48736
|
+
const now = Date.now();
|
|
48737
|
+
for (const [serverName, managed] of this.clients) {
|
|
48738
|
+
if (now - managed.lastUsedAt > this.IDLE_TIMEOUT) {
|
|
48739
|
+
this.clients.delete(serverName);
|
|
48740
|
+
try {
|
|
48741
|
+
await managed.client.close();
|
|
48742
|
+
} catch {}
|
|
48743
|
+
try {
|
|
48744
|
+
await managed.transport.close();
|
|
48745
|
+
} catch {}
|
|
48746
|
+
}
|
|
48747
|
+
}
|
|
48748
|
+
}
|
|
48749
|
+
getRegisteredServers() {
|
|
48750
|
+
return Array.from(this.serverConfigs.keys());
|
|
48751
|
+
}
|
|
48752
|
+
getConnectedServers() {
|
|
48753
|
+
return Array.from(this.clients.keys());
|
|
48754
|
+
}
|
|
48755
|
+
isConnected(serverName) {
|
|
48756
|
+
return this.clients.has(serverName);
|
|
48757
|
+
}
|
|
48758
|
+
}
|
|
48759
|
+
// src/features/background-agent/manager.ts
|
|
48760
|
+
import { existsSync as existsSync48, readdirSync as readdirSync17 } from "fs";
|
|
48761
|
+
import { join as join61 } from "path";
|
|
48762
|
+
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
48763
|
+
function getMessageDir11(sessionID) {
|
|
48764
|
+
if (!existsSync48(MESSAGE_STORAGE))
|
|
48765
|
+
return null;
|
|
48766
|
+
const directPath = join61(MESSAGE_STORAGE, sessionID);
|
|
48767
|
+
if (existsSync48(directPath))
|
|
48768
|
+
return directPath;
|
|
48769
|
+
for (const dir of readdirSync17(MESSAGE_STORAGE)) {
|
|
48770
|
+
const sessionPath = join61(MESSAGE_STORAGE, dir, sessionID);
|
|
48771
|
+
if (existsSync48(sessionPath))
|
|
48772
|
+
return sessionPath;
|
|
48773
|
+
}
|
|
48774
|
+
return null;
|
|
48775
|
+
}
|
|
48776
|
+
|
|
48777
|
+
class BackgroundManager {
|
|
48778
|
+
tasks;
|
|
48779
|
+
notifications;
|
|
48780
|
+
client;
|
|
48781
|
+
directory;
|
|
48782
|
+
pollingInterval;
|
|
48783
|
+
constructor(ctx) {
|
|
48784
|
+
this.tasks = new Map;
|
|
48785
|
+
this.notifications = new Map;
|
|
48786
|
+
this.client = ctx.client;
|
|
48787
|
+
this.directory = ctx.directory;
|
|
48788
|
+
}
|
|
48789
|
+
async launch(input) {
|
|
48790
|
+
if (!input.agent || input.agent.trim() === "") {
|
|
48791
|
+
throw new Error("Agent parameter is required");
|
|
48792
|
+
}
|
|
48793
|
+
const createResult = await this.client.session.create({
|
|
48794
|
+
body: {
|
|
48795
|
+
parentID: input.parentSessionID,
|
|
48796
|
+
title: `Background: ${input.description}`
|
|
48797
|
+
}
|
|
48798
|
+
});
|
|
48799
|
+
if (createResult.error) {
|
|
48800
|
+
throw new Error(`Failed to create background session: ${createResult.error}`);
|
|
48801
|
+
}
|
|
48802
|
+
const sessionID = createResult.data.id;
|
|
48803
|
+
subagentSessions.add(sessionID);
|
|
48804
|
+
const task = {
|
|
48805
|
+
id: `bg_${crypto.randomUUID().slice(0, 8)}`,
|
|
48806
|
+
sessionID,
|
|
48807
|
+
parentSessionID: input.parentSessionID,
|
|
48808
|
+
parentMessageID: input.parentMessageID,
|
|
48809
|
+
description: input.description,
|
|
48810
|
+
prompt: input.prompt,
|
|
48811
|
+
agent: input.agent,
|
|
48812
|
+
status: "running",
|
|
48813
|
+
startedAt: new Date,
|
|
48814
|
+
progress: {
|
|
48815
|
+
toolCalls: 0,
|
|
48816
|
+
lastUpdate: new Date
|
|
48817
|
+
},
|
|
48818
|
+
parentModel: input.parentModel
|
|
48819
|
+
};
|
|
48820
|
+
this.tasks.set(task.id, task);
|
|
48821
|
+
this.startPolling();
|
|
48822
|
+
log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent });
|
|
48823
|
+
this.client.session.promptAsync({
|
|
48824
|
+
path: { id: sessionID },
|
|
48825
|
+
body: {
|
|
48826
|
+
agent: input.agent,
|
|
48827
|
+
tools: {
|
|
48828
|
+
task: false,
|
|
48829
|
+
background_task: false
|
|
48830
|
+
},
|
|
48831
|
+
parts: [{ type: "text", text: input.prompt }]
|
|
48832
|
+
}
|
|
48833
|
+
}).catch((error45) => {
|
|
48834
|
+
log("[background-agent] promptAsync error:", error45);
|
|
48835
|
+
const existingTask = this.findBySession(sessionID);
|
|
48836
|
+
if (existingTask) {
|
|
48837
|
+
existingTask.status = "error";
|
|
48838
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
48839
|
+
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
48840
|
+
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`;
|
|
48841
|
+
} else {
|
|
48842
|
+
existingTask.error = errorMessage;
|
|
48843
|
+
}
|
|
48844
|
+
existingTask.completedAt = new Date;
|
|
48845
|
+
this.markForNotification(existingTask);
|
|
48846
|
+
this.notifyParentSession(existingTask);
|
|
48847
|
+
}
|
|
48848
|
+
});
|
|
48849
|
+
return task;
|
|
48850
|
+
}
|
|
48851
|
+
getTask(id) {
|
|
48852
|
+
return this.tasks.get(id);
|
|
48853
|
+
}
|
|
48854
|
+
getTasksByParentSession(sessionID) {
|
|
48855
|
+
const result = [];
|
|
48856
|
+
for (const task of this.tasks.values()) {
|
|
48857
|
+
if (task.parentSessionID === sessionID) {
|
|
48858
|
+
result.push(task);
|
|
46279
48859
|
}
|
|
46280
48860
|
}
|
|
48861
|
+
return result;
|
|
46281
48862
|
}
|
|
46282
|
-
|
|
46283
|
-
|
|
46284
|
-
|
|
46285
|
-
|
|
46286
|
-
|
|
46287
|
-
|
|
48863
|
+
getAllDescendantTasks(sessionID) {
|
|
48864
|
+
const result = [];
|
|
48865
|
+
const directChildren = this.getTasksByParentSession(sessionID);
|
|
48866
|
+
for (const child of directChildren) {
|
|
48867
|
+
result.push(child);
|
|
48868
|
+
const descendants = this.getAllDescendantTasks(child.sessionID);
|
|
48869
|
+
result.push(...descendants);
|
|
48870
|
+
}
|
|
46288
48871
|
return result;
|
|
46289
48872
|
}
|
|
46290
|
-
|
|
46291
|
-
const
|
|
46292
|
-
|
|
46293
|
-
|
|
48873
|
+
findBySession(sessionID) {
|
|
48874
|
+
for (const task of this.tasks.values()) {
|
|
48875
|
+
if (task.sessionID === sessionID) {
|
|
48876
|
+
return task;
|
|
48877
|
+
}
|
|
46294
48878
|
}
|
|
46295
|
-
|
|
46296
|
-
|
|
48879
|
+
return;
|
|
48880
|
+
}
|
|
48881
|
+
async checkSessionTodos(sessionID) {
|
|
48882
|
+
try {
|
|
48883
|
+
const response2 = await this.client.session.todo({
|
|
48884
|
+
path: { id: sessionID }
|
|
48885
|
+
});
|
|
48886
|
+
const todos = response2.data ?? response2;
|
|
48887
|
+
if (!todos || todos.length === 0)
|
|
48888
|
+
return false;
|
|
48889
|
+
const incomplete = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
|
|
48890
|
+
return incomplete.length > 0;
|
|
48891
|
+
} catch {
|
|
48892
|
+
return false;
|
|
46297
48893
|
}
|
|
46298
|
-
|
|
46299
|
-
|
|
46300
|
-
const
|
|
46301
|
-
|
|
46302
|
-
|
|
48894
|
+
}
|
|
48895
|
+
handleEvent(event) {
|
|
48896
|
+
const props = event.properties;
|
|
48897
|
+
if (event.type === "message.part.updated") {
|
|
48898
|
+
if (!props || typeof props !== "object" || !("sessionID" in props))
|
|
46303
48899
|
return;
|
|
48900
|
+
const partInfo = props;
|
|
48901
|
+
const sessionID = partInfo?.sessionID;
|
|
48902
|
+
if (!sessionID)
|
|
48903
|
+
return;
|
|
48904
|
+
const task = this.findBySession(sessionID);
|
|
48905
|
+
if (!task)
|
|
48906
|
+
return;
|
|
48907
|
+
if (partInfo?.type === "tool" || partInfo?.tool) {
|
|
48908
|
+
if (!task.progress) {
|
|
48909
|
+
task.progress = {
|
|
48910
|
+
toolCalls: 0,
|
|
48911
|
+
lastUpdate: new Date
|
|
48912
|
+
};
|
|
48913
|
+
}
|
|
48914
|
+
task.progress.toolCalls += 1;
|
|
48915
|
+
task.progress.lastTool = partInfo.tool;
|
|
48916
|
+
task.progress.lastUpdate = new Date;
|
|
46304
48917
|
}
|
|
46305
|
-
|
|
46306
|
-
|
|
46307
|
-
|
|
46308
|
-
|
|
46309
|
-
|
|
46310
|
-
|
|
46311
|
-
|
|
46312
|
-
|
|
46313
|
-
|
|
46314
|
-
|
|
46315
|
-
|
|
46316
|
-
|
|
46317
|
-
clearTimeout(existingTimer);
|
|
48918
|
+
}
|
|
48919
|
+
if (event.type === "session.idle") {
|
|
48920
|
+
const sessionID = props?.sessionID;
|
|
48921
|
+
if (!sessionID)
|
|
48922
|
+
return;
|
|
48923
|
+
const task = this.findBySession(sessionID);
|
|
48924
|
+
if (!task || task.status !== "running")
|
|
48925
|
+
return;
|
|
48926
|
+
this.checkSessionTodos(sessionID).then((hasIncompleteTodos2) => {
|
|
48927
|
+
if (hasIncompleteTodos2) {
|
|
48928
|
+
log("[background-agent] Task has incomplete todos, waiting for todo-continuation:", task.id);
|
|
48929
|
+
return;
|
|
46318
48930
|
}
|
|
46319
|
-
|
|
46320
|
-
|
|
46321
|
-
|
|
46322
|
-
|
|
48931
|
+
task.status = "completed";
|
|
48932
|
+
task.completedAt = new Date;
|
|
48933
|
+
this.markForNotification(task);
|
|
48934
|
+
this.notifyParentSession(task);
|
|
48935
|
+
log("[background-agent] Task completed via session.idle event:", task.id);
|
|
48936
|
+
});
|
|
48937
|
+
}
|
|
48938
|
+
if (event.type === "session.deleted") {
|
|
48939
|
+
const info = props?.info;
|
|
48940
|
+
if (!info || typeof info.id !== "string")
|
|
48941
|
+
return;
|
|
48942
|
+
const sessionID = info.id;
|
|
48943
|
+
const task = this.findBySession(sessionID);
|
|
48944
|
+
if (!task)
|
|
48945
|
+
return;
|
|
48946
|
+
if (task.status === "running") {
|
|
48947
|
+
task.status = "cancelled";
|
|
48948
|
+
task.completedAt = new Date;
|
|
48949
|
+
task.error = "Session deleted";
|
|
46323
48950
|
}
|
|
46324
|
-
|
|
46325
|
-
|
|
48951
|
+
this.tasks.delete(task.id);
|
|
48952
|
+
this.clearNotificationsForTask(task.id);
|
|
48953
|
+
subagentSessions.delete(sessionID);
|
|
48954
|
+
}
|
|
46326
48955
|
}
|
|
46327
|
-
|
|
46328
|
-
|
|
48956
|
+
markForNotification(task) {
|
|
48957
|
+
const queue = this.notifications.get(task.parentSessionID) ?? [];
|
|
48958
|
+
queue.push(task);
|
|
48959
|
+
this.notifications.set(task.parentSessionID, queue);
|
|
46329
48960
|
}
|
|
46330
|
-
|
|
46331
|
-
|
|
46332
|
-
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js
|
|
46333
|
-
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
46334
|
-
import process2 from "process";
|
|
46335
|
-
import { PassThrough } from "stream";
|
|
46336
|
-
|
|
46337
|
-
// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
46338
|
-
class ReadBuffer {
|
|
46339
|
-
append(chunk) {
|
|
46340
|
-
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
48961
|
+
getPendingNotifications(sessionID) {
|
|
48962
|
+
return this.notifications.get(sessionID) ?? [];
|
|
46341
48963
|
}
|
|
46342
|
-
|
|
46343
|
-
|
|
46344
|
-
|
|
46345
|
-
|
|
46346
|
-
const
|
|
46347
|
-
|
|
46348
|
-
|
|
46349
|
-
|
|
48964
|
+
clearNotifications(sessionID) {
|
|
48965
|
+
this.notifications.delete(sessionID);
|
|
48966
|
+
}
|
|
48967
|
+
clearNotificationsForTask(taskId) {
|
|
48968
|
+
for (const [sessionID, tasks] of this.notifications.entries()) {
|
|
48969
|
+
const filtered = tasks.filter((t) => t.id !== taskId);
|
|
48970
|
+
if (filtered.length === 0) {
|
|
48971
|
+
this.notifications.delete(sessionID);
|
|
48972
|
+
} else {
|
|
48973
|
+
this.notifications.set(sessionID, filtered);
|
|
48974
|
+
}
|
|
46350
48975
|
}
|
|
46351
|
-
const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
|
|
46352
|
-
this._buffer = this._buffer.subarray(index + 1);
|
|
46353
|
-
return deserializeMessage(line);
|
|
46354
48976
|
}
|
|
46355
|
-
|
|
46356
|
-
this.
|
|
48977
|
+
startPolling() {
|
|
48978
|
+
if (this.pollingInterval)
|
|
48979
|
+
return;
|
|
48980
|
+
this.pollingInterval = setInterval(() => {
|
|
48981
|
+
this.pollRunningTasks();
|
|
48982
|
+
}, 2000);
|
|
48983
|
+
this.pollingInterval.unref();
|
|
46357
48984
|
}
|
|
46358
|
-
|
|
46359
|
-
|
|
46360
|
-
|
|
46361
|
-
|
|
46362
|
-
function serializeMessage(message) {
|
|
46363
|
-
return JSON.stringify(message) + `
|
|
46364
|
-
`;
|
|
46365
|
-
}
|
|
46366
|
-
|
|
46367
|
-
// node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js
|
|
46368
|
-
var DEFAULT_INHERITED_ENV_VARS = process2.platform === "win32" ? [
|
|
46369
|
-
"APPDATA",
|
|
46370
|
-
"HOMEDRIVE",
|
|
46371
|
-
"HOMEPATH",
|
|
46372
|
-
"LOCALAPPDATA",
|
|
46373
|
-
"PATH",
|
|
46374
|
-
"PROCESSOR_ARCHITECTURE",
|
|
46375
|
-
"SYSTEMDRIVE",
|
|
46376
|
-
"SYSTEMROOT",
|
|
46377
|
-
"TEMP",
|
|
46378
|
-
"USERNAME",
|
|
46379
|
-
"USERPROFILE",
|
|
46380
|
-
"PROGRAMFILES"
|
|
46381
|
-
] : ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"];
|
|
46382
|
-
function getDefaultEnvironment() {
|
|
46383
|
-
const env = {};
|
|
46384
|
-
for (const key of DEFAULT_INHERITED_ENV_VARS) {
|
|
46385
|
-
const value = process2.env[key];
|
|
46386
|
-
if (value === undefined) {
|
|
46387
|
-
continue;
|
|
46388
|
-
}
|
|
46389
|
-
if (value.startsWith("()")) {
|
|
46390
|
-
continue;
|
|
48985
|
+
stopPolling() {
|
|
48986
|
+
if (this.pollingInterval) {
|
|
48987
|
+
clearInterval(this.pollingInterval);
|
|
48988
|
+
this.pollingInterval = undefined;
|
|
46391
48989
|
}
|
|
46392
|
-
env[key] = value;
|
|
46393
48990
|
}
|
|
46394
|
-
|
|
46395
|
-
|
|
46396
|
-
|
|
46397
|
-
|
|
46398
|
-
constructor(server) {
|
|
46399
|
-
this._readBuffer = new ReadBuffer;
|
|
46400
|
-
this._stderrStream = null;
|
|
46401
|
-
this._serverParams = server;
|
|
46402
|
-
if (server.stderr === "pipe" || server.stderr === "overlapped") {
|
|
46403
|
-
this._stderrStream = new PassThrough;
|
|
46404
|
-
}
|
|
48991
|
+
cleanup() {
|
|
48992
|
+
this.stopPolling();
|
|
48993
|
+
this.tasks.clear();
|
|
48994
|
+
this.notifications.clear();
|
|
46405
48995
|
}
|
|
46406
|
-
|
|
46407
|
-
|
|
46408
|
-
|
|
48996
|
+
notifyParentSession(task) {
|
|
48997
|
+
const duration3 = this.formatDuration(task.startedAt, task.completedAt);
|
|
48998
|
+
log("[background-agent] notifyParentSession called for task:", task.id);
|
|
48999
|
+
const tuiClient = this.client;
|
|
49000
|
+
if (tuiClient.tui?.showToast) {
|
|
49001
|
+
tuiClient.tui.showToast({
|
|
49002
|
+
body: {
|
|
49003
|
+
title: "Background Task Completed",
|
|
49004
|
+
message: `Task "${task.description}" finished in ${duration3}.`,
|
|
49005
|
+
variant: "success",
|
|
49006
|
+
duration: 5000
|
|
49007
|
+
}
|
|
49008
|
+
}).catch(() => {});
|
|
46409
49009
|
}
|
|
46410
|
-
|
|
46411
|
-
|
|
46412
|
-
|
|
46413
|
-
|
|
46414
|
-
|
|
46415
|
-
|
|
46416
|
-
|
|
46417
|
-
|
|
46418
|
-
|
|
46419
|
-
|
|
46420
|
-
|
|
46421
|
-
|
|
46422
|
-
|
|
46423
|
-
|
|
46424
|
-
|
|
46425
|
-
|
|
46426
|
-
|
|
46427
|
-
|
|
46428
|
-
|
|
46429
|
-
|
|
46430
|
-
|
|
46431
|
-
}
|
|
46432
|
-
|
|
46433
|
-
this.
|
|
46434
|
-
|
|
46435
|
-
this._process.stdout?.on("data", (chunk) => {
|
|
46436
|
-
this._readBuffer.append(chunk);
|
|
46437
|
-
this.processReadBuffer();
|
|
46438
|
-
});
|
|
46439
|
-
this._process.stdout?.on("error", (error45) => {
|
|
46440
|
-
this.onerror?.(error45);
|
|
46441
|
-
});
|
|
46442
|
-
if (this._stderrStream && this._process.stderr) {
|
|
46443
|
-
this._process.stderr.pipe(this._stderrStream);
|
|
49010
|
+
const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration3}. Use background_output with task_id="${task.id}" to get results.`;
|
|
49011
|
+
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
49012
|
+
const taskId = task.id;
|
|
49013
|
+
setTimeout(async () => {
|
|
49014
|
+
try {
|
|
49015
|
+
const messageDir = getMessageDir11(task.parentSessionID);
|
|
49016
|
+
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
49017
|
+
const modelContext = task.parentModel ?? prevMessage?.model;
|
|
49018
|
+
const modelField = modelContext?.providerID && modelContext?.modelID ? { providerID: modelContext.providerID, modelID: modelContext.modelID } : undefined;
|
|
49019
|
+
await this.client.session.prompt({
|
|
49020
|
+
path: { id: task.parentSessionID },
|
|
49021
|
+
body: {
|
|
49022
|
+
agent: prevMessage?.agent,
|
|
49023
|
+
model: modelField,
|
|
49024
|
+
parts: [{ type: "text", text: message }]
|
|
49025
|
+
},
|
|
49026
|
+
query: { directory: this.directory }
|
|
49027
|
+
});
|
|
49028
|
+
log("[background-agent] Successfully sent prompt to parent session:", { parentSessionID: task.parentSessionID });
|
|
49029
|
+
} catch (error45) {
|
|
49030
|
+
log("[background-agent] prompt failed:", String(error45));
|
|
49031
|
+
} finally {
|
|
49032
|
+
this.clearNotificationsForTask(taskId);
|
|
49033
|
+
this.tasks.delete(taskId);
|
|
49034
|
+
log("[background-agent] Removed completed task from memory:", taskId);
|
|
46444
49035
|
}
|
|
46445
|
-
});
|
|
49036
|
+
}, 200);
|
|
46446
49037
|
}
|
|
46447
|
-
|
|
46448
|
-
|
|
46449
|
-
|
|
49038
|
+
formatDuration(start, end) {
|
|
49039
|
+
const duration3 = (end ?? new Date).getTime() - start.getTime();
|
|
49040
|
+
const seconds = Math.floor(duration3 / 1000);
|
|
49041
|
+
const minutes = Math.floor(seconds / 60);
|
|
49042
|
+
const hours = Math.floor(minutes / 60);
|
|
49043
|
+
if (hours > 0) {
|
|
49044
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
49045
|
+
} else if (minutes > 0) {
|
|
49046
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
46450
49047
|
}
|
|
46451
|
-
return
|
|
49048
|
+
return `${seconds}s`;
|
|
46452
49049
|
}
|
|
46453
|
-
|
|
46454
|
-
|
|
49050
|
+
hasRunningTasks() {
|
|
49051
|
+
for (const task of this.tasks.values()) {
|
|
49052
|
+
if (task.status === "running")
|
|
49053
|
+
return true;
|
|
49054
|
+
}
|
|
49055
|
+
return false;
|
|
46455
49056
|
}
|
|
46456
|
-
|
|
46457
|
-
|
|
46458
|
-
|
|
46459
|
-
|
|
46460
|
-
|
|
46461
|
-
|
|
46462
|
-
|
|
46463
|
-
|
|
46464
|
-
|
|
46465
|
-
this.
|
|
49057
|
+
pruneStaleTasksAndNotifications() {
|
|
49058
|
+
const now = Date.now();
|
|
49059
|
+
for (const [taskId, task] of this.tasks.entries()) {
|
|
49060
|
+
const age = now - task.startedAt.getTime();
|
|
49061
|
+
if (age > TASK_TTL_MS) {
|
|
49062
|
+
log("[background-agent] Pruning stale task:", { taskId, age: Math.round(age / 1000) + "s" });
|
|
49063
|
+
task.status = "error";
|
|
49064
|
+
task.error = "Task timed out after 30 minutes";
|
|
49065
|
+
task.completedAt = new Date;
|
|
49066
|
+
this.clearNotificationsForTask(taskId);
|
|
49067
|
+
this.tasks.delete(taskId);
|
|
49068
|
+
subagentSessions.delete(task.sessionID);
|
|
46466
49069
|
}
|
|
46467
49070
|
}
|
|
46468
|
-
|
|
46469
|
-
|
|
46470
|
-
|
|
46471
|
-
|
|
46472
|
-
this._process = undefined;
|
|
46473
|
-
const closePromise = new Promise((resolve8) => {
|
|
46474
|
-
processToClose.once("close", () => {
|
|
46475
|
-
resolve8();
|
|
46476
|
-
});
|
|
46477
|
-
});
|
|
46478
|
-
try {
|
|
46479
|
-
processToClose.stdin?.end();
|
|
46480
|
-
} catch {}
|
|
46481
|
-
await Promise.race([closePromise, new Promise((resolve8) => setTimeout(resolve8, 2000).unref())]);
|
|
46482
|
-
if (processToClose.exitCode === null) {
|
|
46483
|
-
try {
|
|
46484
|
-
processToClose.kill("SIGTERM");
|
|
46485
|
-
} catch {}
|
|
46486
|
-
await Promise.race([closePromise, new Promise((resolve8) => setTimeout(resolve8, 2000).unref())]);
|
|
49071
|
+
for (const [sessionID, notifications] of this.notifications.entries()) {
|
|
49072
|
+
if (notifications.length === 0) {
|
|
49073
|
+
this.notifications.delete(sessionID);
|
|
49074
|
+
continue;
|
|
46487
49075
|
}
|
|
46488
|
-
|
|
46489
|
-
|
|
46490
|
-
|
|
46491
|
-
|
|
49076
|
+
const validNotifications = notifications.filter((task) => {
|
|
49077
|
+
const age = now - task.startedAt.getTime();
|
|
49078
|
+
return age <= TASK_TTL_MS;
|
|
49079
|
+
});
|
|
49080
|
+
if (validNotifications.length === 0) {
|
|
49081
|
+
this.notifications.delete(sessionID);
|
|
49082
|
+
} else if (validNotifications.length !== notifications.length) {
|
|
49083
|
+
this.notifications.set(sessionID, validNotifications);
|
|
46492
49084
|
}
|
|
46493
49085
|
}
|
|
46494
|
-
this._readBuffer.clear();
|
|
46495
49086
|
}
|
|
46496
|
-
|
|
46497
|
-
|
|
46498
|
-
|
|
46499
|
-
|
|
46500
|
-
|
|
46501
|
-
|
|
46502
|
-
|
|
46503
|
-
|
|
46504
|
-
|
|
46505
|
-
|
|
49087
|
+
async pollRunningTasks() {
|
|
49088
|
+
this.pruneStaleTasksAndNotifications();
|
|
49089
|
+
const statusResult = await this.client.session.status();
|
|
49090
|
+
const allStatuses = statusResult.data ?? {};
|
|
49091
|
+
for (const task of this.tasks.values()) {
|
|
49092
|
+
if (task.status !== "running")
|
|
49093
|
+
continue;
|
|
49094
|
+
try {
|
|
49095
|
+
const sessionStatus = allStatuses[task.sessionID];
|
|
49096
|
+
if (!sessionStatus) {
|
|
49097
|
+
log("[background-agent] Session not found in status:", task.sessionID);
|
|
49098
|
+
continue;
|
|
49099
|
+
}
|
|
49100
|
+
if (sessionStatus.type === "idle") {
|
|
49101
|
+
const hasIncompleteTodos2 = await this.checkSessionTodos(task.sessionID);
|
|
49102
|
+
if (hasIncompleteTodos2) {
|
|
49103
|
+
log("[background-agent] Task has incomplete todos via polling, waiting:", task.id);
|
|
49104
|
+
continue;
|
|
49105
|
+
}
|
|
49106
|
+
task.status = "completed";
|
|
49107
|
+
task.completedAt = new Date;
|
|
49108
|
+
this.markForNotification(task);
|
|
49109
|
+
this.notifyParentSession(task);
|
|
49110
|
+
log("[background-agent] Task completed via polling:", task.id);
|
|
49111
|
+
continue;
|
|
49112
|
+
}
|
|
49113
|
+
const messagesResult = await this.client.session.messages({
|
|
49114
|
+
path: { id: task.sessionID }
|
|
49115
|
+
});
|
|
49116
|
+
if (!messagesResult.error && messagesResult.data) {
|
|
49117
|
+
const messages = messagesResult.data;
|
|
49118
|
+
const assistantMsgs = messages.filter((m) => m.info?.role === "assistant");
|
|
49119
|
+
let toolCalls = 0;
|
|
49120
|
+
let lastTool;
|
|
49121
|
+
let lastMessage;
|
|
49122
|
+
for (const msg of assistantMsgs) {
|
|
49123
|
+
const parts = msg.parts ?? [];
|
|
49124
|
+
for (const part of parts) {
|
|
49125
|
+
if (part.type === "tool_use" || part.tool) {
|
|
49126
|
+
toolCalls++;
|
|
49127
|
+
lastTool = part.tool || part.name || "unknown";
|
|
49128
|
+
}
|
|
49129
|
+
if (part.type === "text" && part.text) {
|
|
49130
|
+
lastMessage = part.text;
|
|
49131
|
+
}
|
|
49132
|
+
}
|
|
49133
|
+
}
|
|
49134
|
+
if (!task.progress) {
|
|
49135
|
+
task.progress = { toolCalls: 0, lastUpdate: new Date };
|
|
49136
|
+
}
|
|
49137
|
+
task.progress.toolCalls = toolCalls;
|
|
49138
|
+
task.progress.lastTool = lastTool;
|
|
49139
|
+
task.progress.lastUpdate = new Date;
|
|
49140
|
+
if (lastMessage) {
|
|
49141
|
+
task.progress.lastMessage = lastMessage;
|
|
49142
|
+
task.progress.lastMessageAt = new Date;
|
|
49143
|
+
}
|
|
49144
|
+
}
|
|
49145
|
+
} catch (error45) {
|
|
49146
|
+
log("[background-agent] Poll error for task:", { taskId: task.id, error: error45 });
|
|
46506
49147
|
}
|
|
46507
|
-
}
|
|
46508
|
-
|
|
46509
|
-
|
|
46510
|
-
function isElectron() {
|
|
46511
|
-
return "type" in process2;
|
|
46512
|
-
}
|
|
46513
|
-
|
|
46514
|
-
// src/features/skill-mcp-manager/env-cleaner.ts
|
|
46515
|
-
var EXCLUDED_ENV_PATTERNS = [
|
|
46516
|
-
/^NPM_CONFIG_/i,
|
|
46517
|
-
/^npm_config_/,
|
|
46518
|
-
/^YARN_/,
|
|
46519
|
-
/^PNPM_/,
|
|
46520
|
-
/^NO_UPDATE_NOTIFIER$/
|
|
46521
|
-
];
|
|
46522
|
-
function createCleanMcpEnvironment(customEnv = {}) {
|
|
46523
|
-
const cleanEnv = {};
|
|
46524
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
46525
|
-
if (value === undefined)
|
|
46526
|
-
continue;
|
|
46527
|
-
const shouldExclude = EXCLUDED_ENV_PATTERNS.some((pattern) => pattern.test(key));
|
|
46528
|
-
if (!shouldExclude) {
|
|
46529
|
-
cleanEnv[key] = value;
|
|
49148
|
+
}
|
|
49149
|
+
if (!this.hasRunningTasks()) {
|
|
49150
|
+
this.stopPolling();
|
|
46530
49151
|
}
|
|
46531
49152
|
}
|
|
46532
|
-
Object.assign(cleanEnv, customEnv);
|
|
46533
|
-
return cleanEnv;
|
|
46534
49153
|
}
|
|
46535
|
-
|
|
46536
49154
|
// src/features/skill-mcp-manager/manager.ts
|
|
46537
49155
|
class SkillMcpManager {
|
|
46538
49156
|
clients = new Map;
|
|
@@ -47058,11 +49676,17 @@ var OpenSpecConfigSchema = exports_external.object({
|
|
|
47058
49676
|
]),
|
|
47059
49677
|
rootDir: exports_external.string().default(".opencode/openspec")
|
|
47060
49678
|
});
|
|
49679
|
+
var MCPTransportTypeSchema = exports_external.enum(["stdio", "sse"]);
|
|
47061
49680
|
var MCPKnowledgeProviderConfigSchema = exports_external.object({
|
|
47062
49681
|
name: exports_external.string(),
|
|
47063
|
-
|
|
47064
|
-
|
|
47065
|
-
|
|
49682
|
+
transport: MCPTransportTypeSchema.optional().describe("Transport type: 'stdio' (default) or 'sse'"),
|
|
49683
|
+
command: exports_external.string().optional().describe("Command to start the MCP server (required for stdio transport)"),
|
|
49684
|
+
args: exports_external.array(exports_external.string()).optional().describe("Arguments for the command (stdio transport)"),
|
|
49685
|
+
env: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Environment variables (stdio transport)"),
|
|
49686
|
+
url: exports_external.string().optional().describe("SSE endpoint URL (required for sse transport, e.g., 'http://localhost:60062/sse')"),
|
|
49687
|
+
searchTool: exports_external.string().describe("MCP tool name for search operations"),
|
|
49688
|
+
getTool: exports_external.string().optional().describe("MCP tool name for get-by-id operations"),
|
|
49689
|
+
config: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Additional config passed to MCP tools")
|
|
47066
49690
|
});
|
|
47067
49691
|
var NotebookLMProviderConfigSchema = exports_external.object({
|
|
47068
49692
|
enabled: exports_external.boolean().default(false),
|
|
@@ -47116,11 +49740,22 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
47116
49740
|
skills: SkillsConfigSchema.optional(),
|
|
47117
49741
|
ralph_loop: RalphLoopConfigSchema.optional(),
|
|
47118
49742
|
mem0: Mem0ConfigSchema.optional(),
|
|
49743
|
+
letta: Mem0ConfigSchema.optional(),
|
|
47119
49744
|
knowledge_repo: KnowledgeRepoConfigSchema.optional(),
|
|
47120
49745
|
knowledge_provider: KnowledgeProviderConfigSchema.optional(),
|
|
47121
49746
|
openspec: OpenSpecConfigSchema.optional()
|
|
47122
49747
|
});
|
|
47123
49748
|
// src/plugin-config.ts
|
|
49749
|
+
function normalizeLettaToMem0(config3) {
|
|
49750
|
+
if (!config3.letta) {
|
|
49751
|
+
return config3;
|
|
49752
|
+
}
|
|
49753
|
+
const { letta, ...rest } = config3;
|
|
49754
|
+
if (config3.mem0) {
|
|
49755
|
+
return { ...rest, mem0: deepMerge(letta, config3.mem0) };
|
|
49756
|
+
}
|
|
49757
|
+
return { ...rest, mem0: letta };
|
|
49758
|
+
}
|
|
47124
49759
|
function loadConfigFromPath2(configPath, ctx) {
|
|
47125
49760
|
try {
|
|
47126
49761
|
if (fs9.existsSync(configPath)) {
|
|
@@ -47137,8 +49772,9 @@ function loadConfigFromPath2(configPath, ctx) {
|
|
|
47137
49772
|
});
|
|
47138
49773
|
return null;
|
|
47139
49774
|
}
|
|
47140
|
-
|
|
47141
|
-
|
|
49775
|
+
const normalized = normalizeLettaToMem0(result.data);
|
|
49776
|
+
log(`Config loaded from ${configPath}`, { agents: normalized.agents });
|
|
49777
|
+
return normalized;
|
|
47142
49778
|
}
|
|
47143
49779
|
} catch (err) {
|
|
47144
49780
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -47152,6 +49788,7 @@ function mergeConfigs(base, override) {
|
|
|
47152
49788
|
...base,
|
|
47153
49789
|
...override,
|
|
47154
49790
|
agents: deepMerge(base.agents, override.agents),
|
|
49791
|
+
mem0: deepMerge(base.mem0, override.mem0),
|
|
47155
49792
|
disabled_agents: [
|
|
47156
49793
|
...new Set([
|
|
47157
49794
|
...base.disabled_agents ?? [],
|
|
@@ -52734,6 +55371,62 @@ function createConfigHandler(deps) {
|
|
|
52734
55371
|
};
|
|
52735
55372
|
}
|
|
52736
55373
|
// src/index.ts
|
|
55374
|
+
async function initializeKnowledgeProviderRegistry(pluginConfig, mem0Adapter) {
|
|
55375
|
+
const config3 = pluginConfig.knowledge_provider;
|
|
55376
|
+
const registry2 = new KnowledgeProviderRegistry({
|
|
55377
|
+
defaultLimit: config3.search?.defaultLimit ?? 10,
|
|
55378
|
+
defaultThreshold: config3.search?.defaultThreshold ?? 0.5,
|
|
55379
|
+
mergeStrategy: config3.search?.mergeStrategy ?? "score",
|
|
55380
|
+
providerPriority: config3.search?.providerPriority,
|
|
55381
|
+
deduplicate: config3.search?.deduplicate ?? true
|
|
55382
|
+
});
|
|
55383
|
+
if (config3.providers?.local?.enabled !== false) {
|
|
55384
|
+
const localProvider = new LocalKnowledgeProvider({
|
|
55385
|
+
enabled: true,
|
|
55386
|
+
rootDir: config3.providers?.local?.rootDir
|
|
55387
|
+
});
|
|
55388
|
+
await registry2.register(localProvider);
|
|
55389
|
+
}
|
|
55390
|
+
if (mem0Adapter && config3.providers?.mem0?.enabled !== false) {
|
|
55391
|
+
const mem0Provider = new Mem0KnowledgeProvider({
|
|
55392
|
+
enabled: true,
|
|
55393
|
+
indexKnowledgeRepo: config3.providers?.mem0?.indexKnowledgeRepo ?? false,
|
|
55394
|
+
searchLayers: config3.providers?.mem0?.searchLayers
|
|
55395
|
+
}, {
|
|
55396
|
+
enabled: true,
|
|
55397
|
+
apiKey: pluginConfig.mem0.apiKey,
|
|
55398
|
+
endpoint: pluginConfig.mem0.endpoint,
|
|
55399
|
+
userId: pluginConfig.mem0.userId,
|
|
55400
|
+
sessionId: pluginConfig.mem0.sessionId,
|
|
55401
|
+
projectId: pluginConfig.mem0.projectId,
|
|
55402
|
+
teamId: pluginConfig.mem0.teamId,
|
|
55403
|
+
orgId: pluginConfig.mem0.orgId,
|
|
55404
|
+
companyId: pluginConfig.mem0.companyId,
|
|
55405
|
+
agentId: pluginConfig.mem0.agentId
|
|
55406
|
+
});
|
|
55407
|
+
await registry2.register(mem0Provider);
|
|
55408
|
+
}
|
|
55409
|
+
const hasMcpServers = config3.providers?.mcp?.enabled && config3.providers?.mcp?.servers?.length;
|
|
55410
|
+
const hasNotebookLM = config3.providers?.notebooklm?.enabled;
|
|
55411
|
+
if (hasMcpServers || hasNotebookLM) {
|
|
55412
|
+
const mcpManager = new KnowledgeMcpManager;
|
|
55413
|
+
if (hasMcpServers) {
|
|
55414
|
+
for (const serverConfig of config3.providers.mcp.servers) {
|
|
55415
|
+
mcpManager.registerServer(serverConfig);
|
|
55416
|
+
const mcpProvider = new MCPKnowledgeProvider(serverConfig, mcpManager);
|
|
55417
|
+
await registry2.register(mcpProvider);
|
|
55418
|
+
}
|
|
55419
|
+
}
|
|
55420
|
+
if (hasNotebookLM) {
|
|
55421
|
+
const notebookLMProvider = new NotebookLMKnowledgeProvider({
|
|
55422
|
+
enabled: true,
|
|
55423
|
+
defaultNotebookId: config3.providers.notebooklm.defaultNotebookId
|
|
55424
|
+
}, mcpManager);
|
|
55425
|
+
await registry2.register(notebookLMProvider);
|
|
55426
|
+
}
|
|
55427
|
+
}
|
|
55428
|
+
return registry2;
|
|
55429
|
+
}
|
|
52737
55430
|
var OhMyOpenCodePlugin = async (ctx) => {
|
|
52738
55431
|
const pluginConfig = loadPluginConfig(ctx.directory, ctx);
|
|
52739
55432
|
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
@@ -52752,6 +55445,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
52752
55445
|
agentId: pluginConfig.mem0.agentId
|
|
52753
55446
|
}) : null;
|
|
52754
55447
|
const memoryTools = mem0Adapter ? createMemoryTools(mem0Adapter) : {};
|
|
55448
|
+
const knowledgeProviderRegistry = pluginConfig.knowledge_provider?.enabled ? await initializeKnowledgeProviderRegistry(pluginConfig, mem0Adapter) : null;
|
|
52755
55449
|
const knowledgeMonitor = isHookEnabled("knowledge-monitor") && pluginConfig.knowledge_repo?.enabled ? createKnowledgeMonitorHook(ctx.directory, {
|
|
52756
55450
|
enabled: true,
|
|
52757
55451
|
checkPreTool: true,
|
|
@@ -52855,12 +55549,14 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
52855
55549
|
pluginConfig,
|
|
52856
55550
|
modelCacheState
|
|
52857
55551
|
});
|
|
55552
|
+
const knowledgeProviderTools = knowledgeProviderRegistry ? createKnowledgeProviderTools(knowledgeProviderRegistry) : {};
|
|
52858
55553
|
return {
|
|
52859
55554
|
...googleAuthHooks ? { auth: googleAuthHooks.auth } : {},
|
|
52860
55555
|
tool: {
|
|
52861
55556
|
...builtinTools,
|
|
52862
55557
|
...backgroundTools,
|
|
52863
55558
|
...memoryTools,
|
|
55559
|
+
...knowledgeProviderTools,
|
|
52864
55560
|
call_omo_agent: callOmoAgent,
|
|
52865
55561
|
look_at: lookAt,
|
|
52866
55562
|
skill: skillTool,
|