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/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 types19 = getJSONTypes(schema2.type);
2906
- const hasNull = types19.includes("null");
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 (!types19.length && schema2.nullable !== undefined) {
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
- types19.push("null");
2915
+ types18.push("null");
2916
2916
  }
2917
- return types19;
2917
+ return types18;
2918
2918
  }
2919
2919
  exports.getSchemaTypes = getSchemaTypes;
2920
2920
  function getJSONTypes(ts) {
2921
- const types19 = Array.isArray(ts) ? ts : ts ? [ts] : [];
2922
- if (types19.every(rules_1.isJSONType))
2923
- return types19;
2924
- throw new Error("type must be JSONType or JSONType[]: " + types19.join(","));
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, types19) {
2927
+ function coerceAndCheckDataType(it, types18) {
2928
2928
  const { gen, data, opts } = it;
2929
- const coerceTo = coerceToTypes(types19, opts.coerceTypes);
2930
- const checkTypes = types19.length > 0 && !(coerceTo.length === 0 && types19.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types19[0]));
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(types19, data, opts.strictNumbers, DataType.Wrong);
2932
+ const wrongType = checkDataTypes(types18, data, opts.strictNumbers, DataType.Wrong);
2933
2933
  gen.if(wrongType, () => {
2934
2934
  if (coerceTo.length)
2935
- coerceData(it, types19, coerceTo);
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(types19, coerceTypes) {
2945
- return coerceTypes ? types19.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
2944
+ function coerceToTypes(types18, coerceTypes) {
2945
+ return coerceTypes ? types18.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
2946
2946
  }
2947
- function coerceData(it, types19, coerceTo) {
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(types19, data, opts.strictNumbers), () => gen.assign(coerced, data)));
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 types19 = (0, util_1.toHash)(dataTypes);
3029
- if (types19.array && types19.object) {
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 = types19.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`;
3032
- delete types19.null;
3033
- delete types19.array;
3034
- delete types19.object;
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 (types19.number)
3039
- delete types19.integer;
3040
- for (const t in types19)
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 types19 = (0, dataType_1.getSchemaTypes)(it.schema);
3829
- const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it, types19);
3830
- schemaKeywords(it, types19, !checkedTypes, errsCount);
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, types19, typeErrors, errsCount) {
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, types19);
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 (types19.length === 1 && types19[0] === group.type && typeErrors) {
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, types19) {
3924
+ function checkStrictTypes(it, types18) {
3925
3925
  if (it.schemaEnv.meta || !it.opts.strictTypes)
3926
3926
  return;
3927
- checkContextTypes(it, types19);
3927
+ checkContextTypes(it, types18);
3928
3928
  if (!it.opts.allowUnionTypes)
3929
- checkMultipleTypes(it, types19);
3929
+ checkMultipleTypes(it, types18);
3930
3930
  checkKeywordTypes(it, it.dataTypes);
3931
3931
  }
3932
- function checkContextTypes(it, types19) {
3933
- if (!types19.length)
3932
+ function checkContextTypes(it, types18) {
3933
+ if (!types18.length)
3934
3934
  return;
3935
3935
  if (!it.dataTypes.length) {
3936
- it.dataTypes = types19;
3936
+ it.dataTypes = types18;
3937
3937
  return;
3938
3938
  }
3939
- types19.forEach((t) => {
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, types19);
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/background-agent/manager.ts
43584
- import { existsSync as existsSync48, readdirSync as readdirSync17 } from "fs";
43585
- import { join as join61 } from "path";
43586
- var TASK_TTL_MS = 30 * 60 * 1000;
43587
- function getMessageDir11(sessionID) {
43588
- if (!existsSync48(MESSAGE_STORAGE))
43589
- return null;
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 BackgroundManager {
43602
- tasks;
43603
- notifications;
43604
- client;
43605
- directory;
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 launch(input) {
43614
- if (!input.agent || input.agent.trim() === "") {
43615
- throw new Error("Agent parameter is required");
43794
+ async register(provider) {
43795
+ if (this.providers.has(provider.name)) {
43796
+ throw new Error(`Provider "${provider.name}" is already registered`);
43616
43797
  }
43617
- const createResult = await this.client.session.create({
43618
- body: {
43619
- parentID: input.parentSessionID,
43620
- title: `Background: ${input.description}`
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
- if (createResult.error) {
43624
- throw new Error(`Failed to create background session: ${createResult.error}`);
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
- const sessionID = createResult.data.id;
43627
- subagentSessions.add(sessionID);
43628
- const task = {
43629
- id: `bg_${crypto.randomUUID().slice(0, 8)}`,
43630
- sessionID,
43631
- parentSessionID: input.parentSessionID,
43632
- parentMessageID: input.parentMessageID,
43633
- description: input.description,
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
- }).catch((error45) => {
43658
- log("[background-agent] promptAsync error:", error45);
43659
- const existingTask = this.findBySession(sessionID);
43660
- if (existingTask) {
43661
- existingTask.status = "error";
43662
- const errorMessage = error45 instanceof Error ? error45.message : String(error45);
43663
- if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
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 task;
43853
+ }
43854
+ return null;
43674
43855
  }
43675
- getTask(id) {
43676
- return this.tasks.get(id);
43856
+ getProviders() {
43857
+ return Array.from(this.providers.values());
43677
43858
  }
43678
- getTasksByParentSession(sessionID) {
43679
- const result = [];
43680
- for (const task of this.tasks.values()) {
43681
- if (task.parentSessionID === sessionID) {
43682
- result.push(task);
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 result;
43926
+ return merged;
43686
43927
  }
43687
- getAllDescendantTasks(sessionID) {
43688
- const result = [];
43689
- const directChildren = this.getTasksByParentSession(sessionID);
43690
- for (const child of directChildren) {
43691
- result.push(child);
43692
- const descendants = this.getAllDescendantTasks(child.sessionID);
43693
- result.push(...descendants);
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 result;
43963
+ return [id.slice(0, index), id.slice(index + separator.length)];
43696
43964
  }
43697
- findBySession(sessionID) {
43698
- for (const task of this.tasks.values()) {
43699
- if (task.sessionID === sessionID) {
43700
- return task;
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
- return;
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 checkSessionTodos(sessionID) {
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 response2 = await this.client.session.todo({
43708
- path: { id: sessionID }
43709
- });
43710
- const todos = response2.data ?? response2;
43711
- if (!todos || todos.length === 0)
43712
- return false;
43713
- const incomplete = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled");
43714
- return incomplete.length > 0;
43715
- } catch {
43716
- return false;
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
- handleEvent(event) {
43720
- const props = event.properties;
43721
- if (event.type === "message.part.updated") {
43722
- if (!props || typeof props !== "object" || !("sessionID" in props))
43723
- return;
43724
- const partInfo = props;
43725
- const sessionID = partInfo?.sessionID;
43726
- if (!sessionID)
43727
- return;
43728
- const task = this.findBySession(sessionID);
43729
- if (!task)
43730
- return;
43731
- if (partInfo?.type === "tool" || partInfo?.tool) {
43732
- if (!task.progress) {
43733
- task.progress = {
43734
- toolCalls: 0,
43735
- lastUpdate: new Date
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
- task.progress.toolCalls += 1;
43739
- task.progress.lastTool = partInfo.tool;
43740
- task.progress.lastUpdate = new Date;
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
- if (event.type === "session.idle") {
43744
- const sessionID = props?.sessionID;
43745
- if (!sessionID)
43746
- return;
43747
- const task = this.findBySession(sessionID);
43748
- if (!task || task.status !== "running")
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
- if (event.type === "session.deleted") {
43763
- const info = props?.info;
43764
- if (!info || typeof info.id !== "string")
43765
- return;
43766
- const sessionID = info.id;
43767
- const task = this.findBySession(sessionID);
43768
- if (!task)
43769
- return;
43770
- if (task.status === "running") {
43771
- task.status = "cancelled";
43772
- task.completedAt = new Date;
43773
- task.error = "Session deleted";
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
- this.tasks.delete(task.id);
43776
- this.clearNotificationsForTask(task.id);
43777
- subagentSessions.delete(sessionID);
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
- markForNotification(task) {
43781
- const queue = this.notifications.get(task.parentSessionID) ?? [];
43782
- queue.push(task);
43783
- this.notifications.set(task.parentSessionID, queue);
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
- getPendingNotifications(sessionID) {
43786
- return this.notifications.get(sessionID) ?? [];
44246
+ extractTitle(content) {
44247
+ const firstLine = content.split(`
44248
+ `)[0] ?? "";
44249
+ return firstLine.slice(0, 100).trim() || "Untitled Memory";
43787
44250
  }
43788
- clearNotifications(sessionID) {
43789
- this.notifications.delete(sessionID);
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
- clearNotificationsForTask(taskId) {
43792
- for (const [sessionID, tasks] of this.notifications.entries()) {
43793
- const filtered = tasks.filter((t) => t.id !== taskId);
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
- startPolling() {
43802
- if (this.pollingInterval)
43803
- return;
43804
- this.pollingInterval = setInterval(() => {
43805
- this.pollRunningTasks();
43806
- }, 2000);
43807
- this.pollingInterval.unref();
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
- stopPolling() {
43810
- if (this.pollingInterval) {
43811
- clearInterval(this.pollingInterval);
43812
- this.pollingInterval = undefined;
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
- cleanup() {
43816
- this.stopPolling();
43817
- this.tasks.clear();
43818
- this.notifications.clear();
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
- notifyParentSession(task) {
43821
- const duration3 = this.formatDuration(task.startedAt, task.completedAt);
43822
- log("[background-agent] notifyParentSession called for task:", task.id);
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
- const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished in ${duration3}. Use background_output with task_id="${task.id}" to get results.`;
43835
- log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
43836
- const taskId = task.id;
43837
- setTimeout(async () => {
43838
- try {
43839
- const messageDir = getMessageDir11(task.parentSessionID);
43840
- const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
43841
- const modelContext = task.parentModel ?? prevMessage?.model;
43842
- const modelField = modelContext?.providerID && modelContext?.modelID ? { providerID: modelContext.providerID, modelID: modelContext.modelID } : undefined;
43843
- await this.client.session.prompt({
43844
- path: { id: task.parentSessionID },
43845
- body: {
43846
- agent: prevMessage?.agent,
43847
- model: modelField,
43848
- parts: [{ type: "text", text: message }]
43849
- },
43850
- query: { directory: this.directory }
43851
- });
43852
- log("[background-agent] Successfully sent prompt to parent session:", { parentSessionID: task.parentSessionID });
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
- }, 200);
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
- formatDuration(start, end) {
43863
- const duration3 = (end ?? new Date).getTime() - start.getTime();
43864
- const seconds = Math.floor(duration3 / 1000);
43865
- const minutes = Math.floor(seconds / 60);
43866
- const hours = Math.floor(minutes / 60);
43867
- if (hours > 0) {
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
- return `${seconds}s`;
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
- hasRunningTasks() {
43875
- for (const task of this.tasks.values()) {
43876
- if (task.status === "running")
43877
- return true;
44401
+ parseGetResponse(response2) {
44402
+ if (!response2) {
44403
+ return null;
43878
44404
  }
43879
- return false;
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
- pruneStaleTasksAndNotifications() {
43882
- const now = Date.now();
43883
- for (const [taskId, task] of this.tasks.entries()) {
43884
- const age = now - task.startedAt.getTime();
43885
- if (age > TASK_TTL_MS) {
43886
- log("[background-agent] Pruning stale task:", { taskId, age: Math.round(age / 1000) + "s" });
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
- for (const [sessionID, notifications] of this.notifications.entries()) {
43896
- if (notifications.length === 0) {
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
- async pollRunningTasks() {
43912
- this.pruneStaleTasksAndNotifications();
43913
- const statusResult = await this.client.session.status();
43914
- const allStatuses = statusResult.data ?? {};
43915
- for (const task of this.tasks.values()) {
43916
- if (task.status !== "running")
43917
- continue;
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 sessionStatus = allStatuses[task.sessionID];
43920
- if (!sessionStatus) {
43921
- log("[background-agent] Session not found in status:", task.sessionID);
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 (!messagesResult.error && messagesResult.data) {
43941
- const messages = messagesResult.data;
43942
- const assistantMsgs = messages.filter((m) => m.info?.role === "assistant");
43943
- let toolCalls = 0;
43944
- let lastTool;
43945
- let lastMessage;
43946
- for (const msg of assistantMsgs) {
43947
- const parts = msg.parts ?? [];
43948
- for (const part of parts) {
43949
- if (part.type === "tool_use" || part.tool) {
43950
- toolCalls++;
43951
- lastTool = part.tool || part.name || "unknown";
43952
- }
43953
- if (part.type === "text" && part.text) {
43954
- lastMessage = part.text;
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
- if (!task.progress) {
43959
- task.progress = { toolCalls: 0, lastUpdate: new Date };
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
- task.progress.toolCalls = toolCalls;
43962
- task.progress.lastTool = lastTool;
43963
- task.progress.lastUpdate = new Date;
43964
- if (lastMessage) {
43965
- task.progress.lastMessage = lastMessage;
43966
- task.progress.lastMessageAt = new Date;
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
- if (!this.hasRunningTasks()) {
43974
- this.stopPolling();
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
- getToolOutputValidator(toolName) {
46283
- return this._cachedToolOutputValidators.get(toolName);
46284
- }
46285
- async listTools(params, options) {
46286
- const result = await this.request({ method: "tools/list", params }, ListToolsResultSchema, options);
46287
- this.cacheToolMetadata(result.tools);
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
- _setupListChangedHandler(listType, notificationSchema, options, fetcher) {
46291
- const parseResult = ListChangedOptionsBaseSchema.safeParse(options);
46292
- if (!parseResult.success) {
46293
- throw new Error(`Invalid ${listType} listChanged options: ${parseResult.error.message}`);
48873
+ findBySession(sessionID) {
48874
+ for (const task of this.tasks.values()) {
48875
+ if (task.sessionID === sessionID) {
48876
+ return task;
48877
+ }
46294
48878
  }
46295
- if (typeof options.onChanged !== "function") {
46296
- throw new Error(`Invalid ${listType} listChanged options: onChanged must be a function`);
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
- const { autoRefresh, debounceMs } = parseResult.data;
46299
- const { onChanged } = options;
46300
- const refresh = async () => {
46301
- if (!autoRefresh) {
46302
- onChanged(null, null);
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
- try {
46306
- const items = await fetcher();
46307
- onChanged(null, items);
46308
- } catch (e) {
46309
- const error45 = e instanceof Error ? e : new Error(String(e));
46310
- onChanged(error45, null);
46311
- }
46312
- };
46313
- const handler = () => {
46314
- if (debounceMs) {
46315
- const existingTimer = this._listChangedDebounceTimers.get(listType);
46316
- if (existingTimer) {
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
- const timer = setTimeout(refresh, debounceMs);
46320
- this._listChangedDebounceTimers.set(listType, timer);
46321
- } else {
46322
- refresh();
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
- this.setNotificationHandler(notificationSchema, handler);
48951
+ this.tasks.delete(task.id);
48952
+ this.clearNotificationsForTask(task.id);
48953
+ subagentSessions.delete(sessionID);
48954
+ }
46326
48955
  }
46327
- async sendRootsListChanged() {
46328
- return this.notification({ method: "notifications/roots/list_changed" });
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
- readMessage() {
46343
- if (!this._buffer) {
46344
- return null;
46345
- }
46346
- const index = this._buffer.indexOf(`
46347
- `);
46348
- if (index === -1) {
46349
- return null;
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
- clear() {
46356
- this._buffer = undefined;
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
- function deserializeMessage(line) {
46360
- return JSONRPCMessageSchema.parse(JSON.parse(line));
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
- return env;
46395
- }
46396
-
46397
- class StdioClientTransport {
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
- async start() {
46407
- if (this._process) {
46408
- throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
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
- return new Promise((resolve8, reject) => {
46411
- this._process = import_cross_spawn.default(this._serverParams.command, this._serverParams.args ?? [], {
46412
- env: {
46413
- ...getDefaultEnvironment(),
46414
- ...this._serverParams.env
46415
- },
46416
- stdio: ["pipe", "pipe", this._serverParams.stderr ?? "inherit"],
46417
- shell: false,
46418
- windowsHide: process2.platform === "win32" && isElectron(),
46419
- cwd: this._serverParams.cwd
46420
- });
46421
- this._process.on("error", (error45) => {
46422
- reject(error45);
46423
- this.onerror?.(error45);
46424
- });
46425
- this._process.on("spawn", () => {
46426
- resolve8();
46427
- });
46428
- this._process.on("close", (_code) => {
46429
- this._process = undefined;
46430
- this.onclose?.();
46431
- });
46432
- this._process.stdin?.on("error", (error45) => {
46433
- this.onerror?.(error45);
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
- get stderr() {
46448
- if (this._stderrStream) {
46449
- return this._stderrStream;
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 this._process?.stderr ?? null;
49048
+ return `${seconds}s`;
46452
49049
  }
46453
- get pid() {
46454
- return this._process?.pid ?? null;
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
- processReadBuffer() {
46457
- while (true) {
46458
- try {
46459
- const message = this._readBuffer.readMessage();
46460
- if (message === null) {
46461
- break;
46462
- }
46463
- this.onmessage?.(message);
46464
- } catch (error45) {
46465
- this.onerror?.(error45);
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
- async close() {
46470
- if (this._process) {
46471
- const processToClose = this._process;
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
- if (processToClose.exitCode === null) {
46489
- try {
46490
- processToClose.kill("SIGKILL");
46491
- } catch {}
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
- send(message) {
46497
- return new Promise((resolve8) => {
46498
- if (!this._process?.stdin) {
46499
- throw new Error("Not connected");
46500
- }
46501
- const json3 = serializeMessage(message);
46502
- if (this._process.stdin.write(json3)) {
46503
- resolve8();
46504
- } else {
46505
- this._process.stdin.once("drain", resolve8);
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
- searchTool: exports_external.string(),
47064
- getTool: exports_external.string().optional(),
47065
- config: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
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
- log(`Config loaded from ${configPath}`, { agents: result.data.agents });
47141
- return result.data;
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,