@pencil-agent/nano-pencil 2.0.0 → 2.0.2
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/build-meta.json +3 -3
- package/dist/core/mcp/mcp-client.d.ts +3 -1
- package/dist/core/mcp/mcp-client.js +6 -6
- package/dist/core/mcp/mcp-config.d.ts +3 -3
- package/dist/core/mcp/mcp-config.js +1 -1
- package/dist/core/mcp/mcp-manager.d.ts +5 -1
- package/dist/core/mcp/mcp-manager.js +1 -1
- package/dist/core/model/custom-providers.js +1 -1
- package/dist/core/model-registry.js +5 -5
- package/dist/core/platform/config/resource-loader.d.ts +2 -0
- package/dist/core/platform/config/resource-loader.js +2 -2
- package/dist/core/runtime/agent-session.d.ts +12 -0
- package/dist/core/runtime/agent-session.js +8 -8
- package/dist/core/runtime/sdk.d.ts +8 -0
- package/dist/core/runtime/sdk.js +1 -1
- package/dist/modes/interactive/controllers/input-submit-controller.js +2 -2
- package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
- package/dist/modes/interactive/interactive-mode.js +41 -41
- package/dist/node_modules/@pencil-agent/agent-core/dist/agent-loop.js +3 -2
- package/dist/node_modules/@pencil-agent/agent-core/dist/structured-adaptive-agent-loop.js +2 -1
- package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
- package/package.json +1 -1
package/dist/build-meta.json
CHANGED
|
@@ -10,11 +10,13 @@ export declare class MCPClient {
|
|
|
10
10
|
private httpSessions;
|
|
11
11
|
private authStorage;
|
|
12
12
|
private agentDir?;
|
|
13
|
+
private mcpConfigPath?;
|
|
13
14
|
/**
|
|
14
15
|
* @param authStoragePath Optional path to auth.json. If omitted, uses getAgentDir()/auth.json.
|
|
15
16
|
* @param agentDir Optional agent directory for config path resolution. If omitted, uses getAgentDir().
|
|
17
|
+
* @param mcpConfigPath Optional explicit MCP config file path. Overrides default resolution.
|
|
16
18
|
*/
|
|
17
|
-
constructor(authStoragePath?: string, agentDir?: string);
|
|
19
|
+
constructor(authStoragePath?: string, agentDir?: string, mcpConfigPath?: string);
|
|
18
20
|
/**
|
|
19
21
|
* Load MCP server configurations from config file
|
|
20
22
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);if(s===-1)return;const
|
|
3
|
-
`;t.process.stdin.write(
|
|
4
|
-
${i}`:i)}(s.data!==void 0||s.event!==void 0)&&e.push(s);const
|
|
5
|
-
`)||`MCP tool ${e} failed`:void 0}}catch(
|
|
6
|
-
`)||`MCP tool ${e} failed`:void 0}}catch(
|
|
1
|
+
var T=Object.defineProperty;var u=(c,t)=>T(c,"name",{value:t,configurable:!0});import{spawn as w}from"child_process";import{existsSync as v,readFileSync as $}from"fs";import{join as E}from"path";import{getAgentDir as R}from"../../config.js";import{AuthStorage as C}from"../platform/config/auth-storage.js";import{getMCPConfigPath as b}from"./mcp-config.js";const y=["1","true","yes","on"].includes((process.env.NANOPENCIL_DEBUG??"").toLowerCase());function g(...c){y&&console.error(...c)}u(g,"mcpLog");function O(...c){y&&console.warn(...c)}u(O,"mcpWarn");function x(...c){console.error(...c)}u(x,"mcpError");function M(c){if(c==null)return"Unknown error";if(c instanceof Error){const t=c.message?.trim();return t.length>0?t:c.name&&c.name!=="Error"?c.name:"Error (empty message)"}if(typeof c=="string")return c;try{return JSON.stringify(c)}catch{return String(c)}}u(M,"formatUnknownError");function P(c,t,e){const s=M(e);g(c==="http"?`[MCP] HTTP init failed ${t}: ${s}`:`[MCP] stdio start failed ${t}: ${s}`)}u(P,"logMcpStartupFailure");class F{static{u(this,"MCPClient")}servers=new Map;serverRuntimes=new Map;serverTools=new Map;httpSessions=new Map;authStorage;agentDir;mcpConfigPath;constructor(t,e,s){this.agentDir=e,this.mcpConfigPath=s;const n=t??E(e??R(),"auth.json");this.authStorage=C.create(n),this.loadServersFromConfig()}loadServersFromConfig(){const t=b(this.agentDir,this.mcpConfigPath);if(v(t))try{const s=JSON.parse($(t,"utf-8")).mcpServers||[];for(const n of s)n.enabled!==!1&&this.servers.set(n.id,n)}catch(e){x(`Failed to load MCP config: ${e}`)}}getServers(){return Array.from(this.servers.values())}getServer(t){return this.servers.get(t)}addServer(t){this.servers.set(t.id,t)}removeServer(t){this.servers.delete(t),this.serverTools.delete(t),this.stopServer(t)}getRuntime(t){return this.serverRuntimes.get(t)}convertNpxArgsToNpmExecArgs(t){if(t.length===0)return;const e=[...t];(e[0]==="-y"||e[0]==="--yes")&&e.shift();const s=e.shift();if(s)return["exec","--yes",s,...e.length>0?["--",...e]:[]]}getSpawnAttempts(t){if(!t.command)return[];const e=[],s=t.command.trim(),n=s.toLowerCase(),r=t.args??[];if(process.platform==="win32"&&n==="npx"){e.push({command:"npx.cmd",args:r});const o=this.convertNpxArgsToNpmExecArgs(r);return o&&e.push({command:"npm.cmd",args:o}),e.push({command:"npx",args:r}),e}if(e.push({command:s,args:r}),n==="npx"){const o=this.convertNpxArgsToNpmExecArgs(r);o&&e.push({command:process.platform==="win32"?"npm.cmd":"npm",args:o})}return e}async spawnProcess(t,e,s){return await new Promise((n,r)=>{const a=process.platform==="win32"?w([t.command,...t.args].map(p=>p.includes(" ")?`"${p}"`:p).join(" "),{env:e,cwd:s,stdio:["pipe","pipe","pipe"],windowsHide:!0,shell:!0}):w(t.command,t.args,{env:e,cwd:s,stdio:["pipe","pipe","pipe"],windowsHide:!0,shell:!1}),i=u(p=>{a.removeListener("spawn",l),r(p)},"onError"),l=u(()=>{a.removeListener("error",i),n(a)},"onSpawn");a.once("error",i),a.once("spawn",l)})}attachStdoutParser(t,e){const s=u(n=>{if(this.serverRuntimes.get(t)===e){for(const r of e.pendingRequests.values())clearTimeout(r.timer),r.reject(n);e.pendingRequests.clear(),this.serverRuntimes.delete(t)}},"rejectPending");e.process.stdin.on("error",n=>{const o=n.code==="EPIPE"?"process exited before MCP handshake (check command and npm package name)":n instanceof Error?n.message:String(n);g(`[MCP:${t}] stdin: ${o}`),s(new Error(`MCP server ${t} stdin closed: ${o}`))}),e.process.stdout.on("data",n=>{e.buffer=Buffer.concat([e.buffer,n]),this.processStdoutBuffer(t,e)}),e.process.stderr.on("data",n=>{const r=n.toString("utf8").trim();r.length>0&&y&&x(`[MCP:${t}] ${r}`)}),e.process.on("exit",(n,r)=>{if(this.serverRuntimes.get(t)!==e)return;const o=`[MCP:debug] Server exited: code=${n??"null"}, signal=${r??"null"}`;g(o),s(new Error(o))}),e.process.on("error",n=>{const r=`MCP server ${t} process error: ${n instanceof Error?n.message:String(n)}`;s(new Error(r))})}processStdoutBuffer(t,e){for(;e.buffer.length>0;){const s=e.buffer.indexOf(`
|
|
2
|
+
`);if(s===-1)return;const n=e.buffer.slice(0,s).toString("utf8").replace(/\r$/,"");e.buffer=e.buffer.slice(s+1),n.length>0&&(g(`[MCP:${t}] Received: ${n.slice(0,200)}`),this.handleJsonRpcMessage(t,n))}}handleJsonRpcMessage(t,e){let s;try{s=JSON.parse(e)}catch{return}if(s.id===void 0)return;const n=typeof s.id=="number"?s.id:Number(s.id);if(!Number.isFinite(n))return;const r=this.serverRuntimes.get(t);if(!r)return;const o=r.pendingRequests.get(n);if(o){if(clearTimeout(o.timer),r.pendingRequests.delete(n),s.error){o.reject(new Error(s.error.message||`JSON-RPC error ${s.error.code??"unknown"}`));return}o.resolve(s.result)}}writeFramedMessage(t,e){const n=JSON.stringify(e)+`
|
|
3
|
+
`;t.process.stdin.write(n)}sendNotification(t,e,s){const n=this.servers.get(t);if(n?.transport==="http"){this.sendHttpRequest(n,e,s,2e4,!0);return}const r=this.getRuntime(t);r&&this.writeFramedMessage(r,{jsonrpc:"2.0",method:e,params:s??{}})}async sendRequest(t,e,s,n=2e4){const r=this.servers.get(t);if(!r)throw new Error(`Server ${t} not found`);if(r.transport==="http")return await this.sendHttpRequest(r,e,s,n);const o=this.getRuntime(t);if(!o)throw new Error(`Server ${t} is not running`);const a=o.nextRequestId++,i={jsonrpc:"2.0",id:a,method:e,params:s??{}};return new Promise((l,p)=>{const h=setTimeout(()=>{o.pendingRequests.delete(a),p(new Error(`MCP request timed out: ${t} ${e} (${n}ms)`))},n);o.pendingRequests.set(a,{resolve:u(f=>l(f),"resolve"),reject:p,timer:h}),this.writeFramedMessage(o,i)})}getHttpSession(t){const e=this.httpSessions.get(t);if(e)return e;const s={initialized:!1};return this.httpSessions.set(t,s),s}async buildHttpHeaders(t,e){const s={"Content-Type":"application/json",Accept:"application/json, text/event-stream","MCP-Protocol-Version":"2025-03-26",...t.headers??{}};if(e&&(s["Mcp-Session-Id"]=e),t.authProvider){const n=await this.authStorage.getApiKey(t.authProvider);if(n){const r=t.authHeaderName?.trim()||"Authorization",o=t.authScheme??"bearer";s[r]=o==="raw"?n:`Bearer ${n}`}}return s}async ensureHttpInitialized(t){const e=this.getHttpSession(t.id);if(e.initialized)return;const s={protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"nano-pencil",version:"1.11.12"}};await this.sendHttpRequest(t,"initialize",s,t.initTimeout??2e4),e.initialized=!0,await this.sendHttpRequest(t,"notifications/initialized",{},t.initTimeout??2e4,!0)}async sendHttpRequest(t,e,s,n,r=!1,o=!0){if(!t.url)throw new Error(`HTTP MCP server ${t.id} is missing a url`);const a=this.getHttpSession(t.id);e!=="initialize"&&!a.initialized&&await this.ensureHttpInitialized(t);const i=r?void 0:Date.now()+Math.floor(Math.random()*1e3),l=r?{jsonrpc:"2.0",method:e,params:s??{}}:{jsonrpc:"2.0",id:i,method:e,params:s??{}},p=await fetch(t.url,{method:"POST",headers:await this.buildHttpHeaders(t,e==="initialize"?void 0:a.sessionId),body:JSON.stringify(l),signal:AbortSignal.timeout(n)});if(p.status===404&&a.sessionId&&o&&e!=="initialize")return this.httpSessions.set(t.id,{initialized:!1}),this.sendHttpRequest(t,e,s,n,r,!1);if(!p.ok){if(p.status===401&&t.authProvider)throw new Error(`HTTP MCP request failed (401 Unauthorized). The ${t.id} server requires valid credentials for "${t.authProvider}". Re-authenticate and try again.`);const S=await p.text().catch(()=>"");throw new Error(`HTTP MCP request failed (${p.status} ${p.statusText})${S?`: ${S}`:""}`)}const h=p.headers.get("Mcp-Session-Id");if(h&&(a.sessionId=h),r)return;const f=p.headers.get("content-type")??"",m=await p.text();if(!m.trim())return;let d;if(f.includes("text/event-stream"))d=this.parseEventStreamResponse(m);else{if(!f.includes("application/json")&&!m.trim().startsWith("{"))throw new Error(`Unsupported HTTP MCP response content type: ${f||"unknown"}`);d=JSON.parse(m)}if(d.error)throw new Error(d.error.message||`JSON-RPC error ${d.error.code??"unknown"}`);return d.result}parseEventStreamResponse(t){const e=[];let s={};for(const r of t.split(/\r?\n/)){if(!r.trim()){(s.data!==void 0||s.event!==void 0)&&(e.push(s),s={});continue}if(r.startsWith(":"))continue;const o=r.indexOf(":"),a=o===-1?r:r.slice(0,o),i=o===-1?"":r.slice(o+1).trimStart();a==="event"?s.event=i:a==="data"&&(s.data=s.data?`${s.data}
|
|
4
|
+
${i}`:i)}(s.data!==void 0||s.event!==void 0)&&e.push(s);const n=[...e].reverse().find(r=>typeof r.data=="string"&&r.data.trim().length>0)?.data;if(!n)throw new Error("HTTP MCP event-stream response did not include a JSON payload");return JSON.parse(n)}async initializeServer(t){const e=this.servers.get(t),s=e?.command?.toLowerCase().includes("npx")??!1,r=process.platform==="win32"&&s?1500:500;await new Promise(l=>setTimeout(l,r));const o=e?.initTimeout??2e4,a={protocolVersion:"2024-11-05",capabilities:{},clientInfo:{name:"nano-pencil",version:"1.7.0"}},i={protocolVersion:"2024-10-07",capabilities:{},clientInfo:{name:"nano-pencil",version:"1.7.0"}};try{await this.sendRequest(t,"initialize",a,o)}catch{await this.sendRequest(t,"initialize",i,o)}this.sendNotification(t,"notifications/initialized")}normalizeToolRecord(t,e){if(!e||typeof e!="object")return null;const s=e,n=s.name,r=s.description;if(typeof n!="string"||typeof r!="string")return null;const o=s.inputSchema&&typeof s.inputSchema=="object"?s.inputSchema:{type:"object",properties:{},additionalProperties:!0};return{name:`${t}/${n}`,displayName:typeof s.title=="string"?s.title:typeof s.displayName=="string"?s.displayName:void 0,description:r,inputSchema:o,serverId:t}}async loadToolsForServer(t){const e=[];let s;for(;;){const n=await this.sendRequest(t,"tools/list",s?{cursor:s}:{},2e4),r=Array.isArray(n.tools)?n.tools:[];for(const a of r){const i=this.normalizeToolRecord(t,a);i&&e.push(i)}const o=typeof n.nextCursor=="string"?n.nextCursor:typeof n.cursor=="string"?n.cursor:void 0;if(!o||o===s)break;s=o}return this.serverTools.set(t,e),e}async startServer(t){const e=this.servers.get(t);if(!e)throw new Error(`Server ${t} not found`);if(e.transport==="http")try{return await this.ensureHttpInitialized(e),await this.loadToolsForServer(t),!0}catch(r){return P("http",t,r),!1}if(e.transport==="sse"||this.serverRuntimes.has(t))return!0;const s=this.getSpawnAttempts(e);let n;for(const r of s)try{const o=e.cwd||process.cwd(),i={process:await this.spawnProcess(r,{...process.env,...e.env},o),buffer:Buffer.alloc(0),nextRequestId:1,pendingRequests:new Map};return this.serverRuntimes.set(t,i),this.attachStdoutParser(t,i),await this.initializeServer(t),await this.loadToolsForServer(t),!0}catch(o){n=o,this.stopServer(t)}return P("stdio",t,n),!1}stopServer(t){const e=this.servers.get(t),s=this.httpSessions.get(t);e?.transport==="http"&&s?.sessionId&&e.url&&this.buildHttpHeaders(e,s.sessionId).then(r=>fetch(e.url,{method:"DELETE",headers:r})).catch(()=>{}),this.httpSessions.delete(t);const n=this.serverRuntimes.get(t);if(n){for(const r of n.pendingRequests.values())clearTimeout(r.timer),r.reject(new Error(`MCP server ${t} stopped`));n.pendingRequests.clear(),n.process.kill(),this.serverRuntimes.delete(t)}}stopAllServers(){const t=new Set([...this.serverRuntimes.keys(),...this.httpSessions.keys()]);for(const e of t)this.stopServer(e)}async listTools(t){const e=[];if(t){if(!this.serverTools.has(t))try{await this.loadToolsForServer(t)}catch{}const s=this.serverTools.get(t)||[];e.push(...s)}else for(const[s]of this.servers.entries()){if(!this.serverTools.has(s))try{await this.loadToolsForServer(s)}catch{}e.push(...this.serverTools.get(s)||[])}return e}async callTool(t,e,s){const[n,...r]=t.split("/"),o=r.join("/"),a=this.servers.get(n);if(!a)return{content:[{type:"text",text:`Server ${n} not found`}],error:`Server ${n} not found`};const i=s??a.toolTimeout??2e4;return a.transport==="sse"?this.callSSETool(a,o,e):a.transport==="http"?this.callHttpTool(a,o,e,i):this.callStdioTool(a,o,e,i)}async callHttpTool(t,e,s,n=2e4){try{const r=await this.sendRequest(t.id,"tools/call",{name:e,arguments:s},n),o=r.isError===!0,a=Array.isArray(r.content)?r.content.map(i=>({type:i.type==="image"||i.type==="resource"?i.type:"text",text:typeof i.text=="string"?i.text:typeof i.message=="string"?i.message:void 0,data:i})):[{type:"text",text:JSON.stringify(r)}];return{content:a,error:o?a.map(i=>i.text).filter(i=>!!i).join(`
|
|
5
|
+
`)||`MCP tool ${e} failed`:void 0}}catch(r){return{content:[{type:"text",text:`Failed to call tool: ${r}`}],error:String(r)}}}async callStdioTool(t,e,s,n=2e4){if(!this.serverRuntimes.has(t.id))return{content:[{type:"text",text:`Server ${t.id} is not running`}],error:`Server ${t.id} is not running`};try{const r=await this.sendRequest(t.id,"tools/call",{name:e,arguments:s},n),o=r.isError===!0,a=Array.isArray(r.content)?r.content.map(i=>({type:i.type==="image"||i.type==="resource"?i.type:"text",text:typeof i.text=="string"?i.text:typeof i.message=="string"?i.message:void 0,data:i})):[{type:"text",text:JSON.stringify(r)}];return{content:a,error:o?a.map(i=>i.text).filter(i=>!!i).join(`
|
|
6
|
+
`)||`MCP tool ${e} failed`:void 0}}catch(r){return{content:[{type:"text",text:`Failed to call tool: ${r}`}],error:String(r)}}}async callSSETool(t,e,s){return{content:[{type:"text",text:`SSE transport is not implemented yet for ${t.id}/${e}`}],error:"SSE transport not yet supported"}}hasTool(t){const[e]=t.split("/");return this.servers.has(e)}}export{F as MCPClient};
|
|
@@ -6,12 +6,12 @@ export interface MCPConfig {
|
|
|
6
6
|
* Get the path to the MCP configuration file
|
|
7
7
|
* @param agentDir Optional agent directory. If omitted, uses getAgentDir().
|
|
8
8
|
*/
|
|
9
|
-
export declare function getMCPConfigPath(agentDir?: string): string;
|
|
9
|
+
export declare function getMCPConfigPath(agentDir?: string, configPathOverride?: string): string;
|
|
10
10
|
/**
|
|
11
11
|
* Load MCP configuration from file
|
|
12
12
|
* @param agentDir Optional agent directory. If omitted, uses getAgentDir().
|
|
13
13
|
*/
|
|
14
|
-
export declare function loadMCPConfig(agentDir?: string): MCPConfig;
|
|
14
|
+
export declare function loadMCPConfig(agentDir?: string, configPathOverride?: string): MCPConfig;
|
|
15
15
|
/**
|
|
16
16
|
* Save MCP configuration to file
|
|
17
17
|
* @param config MCP configuration to save
|
|
@@ -52,4 +52,4 @@ export declare function listMCPServers(agentDir?: string): MCPServerConfig[];
|
|
|
52
52
|
* List enabled MCP servers
|
|
53
53
|
* @param agentDir Optional agent directory. If omitted, uses getAgentDir().
|
|
54
54
|
*/
|
|
55
|
-
export declare function listEnabledMCPServers(agentDir?: string): MCPServerConfig[];
|
|
55
|
+
export declare function listEnabledMCPServers(agentDir?: string, configPathOverride?: string): MCPServerConfig[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var P=Object.defineProperty;var i=(r,e)=>P(r,"name",{value:e,configurable:!0});import{existsSync as f,mkdirSync as h,readFileSync as T,writeFileSync as x}from"fs";import{join as a,resolve as b}from"path";import{homedir as s}from"os";import{getAgentDir as u}from"../../config.js";const l={mcpServers:[{id:"filesystem",name:"Filesystem",command:"npx",args:["-y","@modelcontextprotocol/server-filesystem","{cwd}"],enabled:!0,transport:"stdio",toolTimeout:3e4},{id:"fetch",name:"Fetch (Web Scraper)",command:"npx",args:["-y","@kazuph/mcp-fetch"],enabled:!1,transport:"stdio",toolTimeout:3e4},{id:"sequential-thinking",name:"Sequential Thinking",command:"npx",args:["-y","@modelcontextprotocol/server-sequential-thinking"],enabled:!0,transport:"stdio",toolTimeout:3e4},{id:"memory",name:"Memory (Knowledge Graph)",command:"npx",args:["-y","@modelcontextprotocol/server-memory"],enabled:!0,transport:"stdio",toolTimeout:3e4},{id:"figma-desktop",name:"Figma Desktop MCP",url:"http://127.0.0.1:3845/mcp",enabled:!1,transport:"http",toolTimeout:6e4,initTimeout:2e4},{id:"figma-remote",name:"Figma Remote MCP",url:"https://mcp.figma.com/mcp",authProvider:"figma",authHeaderName:"Authorization",authScheme:"bearer",enabled:!1,transport:"http",toolTimeout:6e4,initTimeout:2e4},{id:"sqlite",name:"SQLite (Database)",command:"npx",args:["-y","mcp-server-sqlite"],enabled:!1,transport:"stdio",toolTimeout:3e4},{id:"github",name:"GitHub",command:"npx",args:["-y","@modelcontextprotocol/server-github"],env:{GITHUB_TOKEN:""},enabled:!1,transport:"stdio",toolTimeout:3e4},{id:"brave-search",name:"Brave Search",command:"npx",args:["-y","@modelcontextprotocol/server-brave-search"],env:{BRAVE_API_KEY:""},enabled:!1,transport:"stdio",toolTimeout:6e4},{id:"git",name:"Git",command:"npx",args:["-y","@liangshanli/mcp-server-git"],enabled:!1,transport:"stdio",toolTimeout:3e4},{id:"postgres",name:"PostgreSQL",command:"npx",args:["-y","@modelcontextprotocol/server-postgres"],env:{POSTGRES_CONNECTION_STRING:""},enabled:!1,transport:"stdio",toolTimeout:3e4}]};function C(r,e){if(e&&e.trim()){const o=e.trim();return o==="~"?s():o.startsWith("~/")?a(s(),o.slice(2)):o.startsWith("~")?a(s(),o.slice(1)):b(o)}const t=process.env.MCP_CONFIG_PATH;if(t&&t.trim()){const o=t.trim();return o==="~"?s():o.startsWith("~/")?a(s(),o.slice(2)):o.startsWith("~")?a(s(),o.slice(1)):b(o)}const n=r??u();return a(n,"mcp.json")}i(C,"getMCPConfigPath");function c(r,e){const t=C(r,e),n=r??u();if(!f(t)){const o=n;return f(o)||h(o,{recursive:!0}),x(t,JSON.stringify(l,null,2),"utf-8"),l}try{const o=T(t,"utf-8"),m=JSON.parse(o),g=m.mcpServers??[],y=new Set(g.map(d=>d.id)),v=l.mcpServers.filter(d=>!y.has(d.id));if(v.length===0)return m;const S={mcpServers:[...g,...v]};return p(S,r),S}catch(o){return console.error(`Failed to load MCP config: ${o}`),l}}i(c,"loadMCPConfig");function p(r,e){const t=C(e),n=e??u();f(n)||h(n,{recursive:!0}),x(t,JSON.stringify(r,null,2),"utf-8")}i(p,"saveMCPConfig");function G(r,e){const t=c(e);t.mcpServers=t.mcpServers.filter(n=>n.id!==r.id),t.mcpServers.push(r),p(t,e)}i(G,"addMCPServer");function O(r,e){const t=c(e);t.mcpServers=t.mcpServers.filter(n=>n.id!==r),p(t,e)}i(O,"removeMCPServer");function M(r,e,t){const n=c(t),o=n.mcpServers.find(m=>m.id===r);o&&(Object.assign(o,e),p(n,t))}i(M,"updateMCPServer");function k(r,e,t){M(r,{enabled:e},t)}i(k,"setMCPServerEnabled");function I(r,e){return c(e).mcpServers.find(n=>n.id===r)}i(I,"getMCPServer");function q(r){return c(r).mcpServers}i(q,"listMCPServers");function W(r,e){return c(r,e).mcpServers.filter(n=>n.enabled!==!1)}i(W,"listEnabledMCPServers");export{G as addMCPServer,C as getMCPConfigPath,I as getMCPServer,W as listEnabledMCPServers,q as listMCPServers,c as loadMCPConfig,O as removeMCPServer,p as saveMCPConfig,k as setMCPServerEnabled,M as updateMCPServer};
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { MCPClient } from "./mcp-client.js";
|
|
8
8
|
import type { ToolDefinition } from "../extensions-host/index.js";
|
|
9
|
+
export interface MCPManagerOptions {
|
|
10
|
+
mcpConfigPath?: string;
|
|
11
|
+
}
|
|
9
12
|
export declare class MCPManager {
|
|
10
13
|
private client;
|
|
11
14
|
private tools;
|
|
@@ -13,7 +16,8 @@ export declare class MCPManager {
|
|
|
13
16
|
private startedServerIds;
|
|
14
17
|
private failedServerIds;
|
|
15
18
|
private workingDir;
|
|
16
|
-
|
|
19
|
+
private mcpConfigPath?;
|
|
20
|
+
constructor(options?: MCPManagerOptions);
|
|
17
21
|
/**
|
|
18
22
|
* Set the working directory for MCP servers (e.g., user's project dir)
|
|
19
23
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var o=Object.defineProperty;var i=(
|
|
1
|
+
var o=Object.defineProperty;var i=(t,r)=>o(t,"name",{value:r,configurable:!0});import{MCPClient as n}from"./mcp-client.js";import{loadMCPTools as d}from"./mcp-adapter.js";import{listEnabledMCPServers as a}from"./mcp-config.js";class f{static{i(this,"MCPManager")}client;tools=[];enabledServerIds=[];startedServerIds=[];failedServerIds=[];workingDir;mcpConfigPath;constructor(r){this.mcpConfigPath=r?.mcpConfigPath,this.client=new n(void 0,void 0,this.mcpConfigPath),this.workingDir=process.cwd()}setWorkingDir(r){this.workingDir=r}async initialize(){let r=a(void 0,this.mcpConfigPath);this.enabledServerIds=r.map(e=>e.id),this.startedServerIds=[],this.failedServerIds=[],r=r.map(e=>this.resolveServerConfig(e));for(const e of r)this.client.addServer(e),e.transport!=="sse"?await this.client.startServer(e.id)?this.startedServerIds.push(e.id):this.failedServerIds.push(e.id):this.startedServerIds.push(e.id);this.tools=await d(this.client)}resolveServerConfig(r){const e={...r};return e.args&&(e.args=e.args.map(s=>s.replace(/\{cwd\}/g,this.workingDir))),e.cwd||(e.cwd=this.workingDir),e}getTools(){return this.tools}getClient(){return this.client}getStatus(){return{enabledServers:[...this.enabledServerIds],startedServers:[...this.startedServerIds],failedServers:[...this.failedServerIds],toolCount:this.tools.length}}dispose(){this.client.stopAllServers()}}export{f as MCPManager};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var b=Object.defineProperty;var r=(e,t)=>b(e,"name",{value:t,configurable:!0});import{existsSync as M,
|
|
1
|
+
var b=Object.defineProperty;var r=(e,t)=>b(e,"name",{value:t,configurable:!0});import{existsSync as M,mkdirSync as O,readFileSync as I,writeFileSync as A}from"fs";import{dirname as S}from"node:path";const d="custom-anthropic",m="custom-openai",p="custom-model",f=2,w={[d]:{id:d,label:"Anthropic-compatible",description:"Configure or edit an endpoint that speaks the Anthropic Messages API.",defaultBaseUrl:"https://api.anthropic.com/v1",api:"anthropic-messages",defaultInput:["text","image"]},[m]:{id:m,label:"OpenAI-compatible",description:"Configure or edit an endpoint that speaks an OpenAI-compatible API.",defaultBaseUrl:"https://api.openai.com/v1",api:"openai-completions",defaultInput:["text","image"]}};function g(e){if(!M(e))return{providers:{}};const t=I(e,"utf-8");return{providers:JSON.parse(t).providers??{}}}r(g,"readModelsConfig");function h(e,t){O(S(e),{recursive:!0}),A(e,JSON.stringify(t,null,2),"utf-8")}r(h,"writeModelsConfig");function y(e,t,o){const n=C(e),s=t.trim()||p;return{id:s,name:s,api:n.api,input:n.defaultInput,contextWindow:o?.contextWindow??256e3,maxTokens:o?.maxTokens??32768}}r(y,"createCustomModelDefinition");async function _(e,t,o,n){try{if(e===d)return null;if(t.includes("localhost:11434")||t.includes("127.0.0.1:11434")){const s=await N(n);if(s)return s}return o?await U(t,o,n):null}catch{return null}}r(_,"probeModelContextWindow");async function N(e){try{const o=await fetch("http://localhost:11434/api/show",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e}),signal:AbortSignal.timeout(5e3)});if(!o.ok)return null;const s=(await o.json()).model_info;if(!s)return null;const i=s["general.context_length"];return typeof i=="number"&&i>0?{contextWindow:i}:null}catch{return null}}r(N,"probeOllamaModelInfo");async function U(e,t,o){try{const n=E(e),s=await fetch(n,{headers:{Authorization:`Bearer ${t}`},signal:AbortSignal.timeout(5e3)});if(!s.ok)return null;const c=(await s.json()).data;if(!Array.isArray(c))return null;const l=c.find(u=>u.id===o);if(!l)return null;const a=l.context_length??l.max_context_length??l.top_provider?.context_length;return typeof a=="number"&&a>0?{contextWindow:a}:null}catch{return null}}r(U,"probeOpenAICompatibleModels");function E(e){const t=e.replace(/\/+$/,"");return t.endsWith("/chat/completions")?t.slice(0,-17)+"/models":(t.endsWith("/v1"),t+"/models")}r(E,"buildModelsEndpoint");function v(e,t){try{return g(e).providers?.[t]}catch{return}}r(v,"getStoredProviderConfig");function x(e){const t=e?.models;if(!Array.isArray(t)||t.length===0)return;const o=t[0];if(typeof o=="object"&&o!==null&&"id"in o&&typeof o.id=="string"&&o.id.trim())return o.id.trim()}r(x,"getStoredModelId");function R(e){const t=e?.models;return Array.isArray(t)?t.length:0}r(R,"getStoredModelCount");function T(e){const t=e?.customProviderVersion;return typeof t=="number"?t:void 0}r(T,"getStoredConfigVersion");function W(e){return e===d||e===m}r(W,"isCustomProtocolProvider");function V(){return[d,m]}r(V,"listCustomProtocolProviders");function C(e){return w[e]}r(C,"getCustomProtocolProviderDefinition");function F(e,t){const n=v(e,t)?.baseUrl;return typeof n=="string"&&n.trim()?n.trim():void 0}r(F,"getCustomProtocolProviderBaseUrl");function J(e,t){return x(v(e,t))}r(J,"getCustomProtocolProviderModelName");function L(e){let t;try{t=g(e)}catch{return}t.providers??={};let o=!1;for(const n of V()){const s=C(n),i=t.providers[n],a=T(i)!==f||R(i)!==1?p:x(i)??p,u=[y(n,a)];if(!i){t.providers[n]={baseUrl:s.defaultBaseUrl,customProviderVersion:f,models:u},o=!0;continue}(typeof i.baseUrl!="string"||!i.baseUrl.trim())&&(i.baseUrl=s.defaultBaseUrl,o=!0),i.customProviderVersion!==f&&(i.customProviderVersion=f,o=!0);const P=i.models;JSON.stringify(P)!==JSON.stringify(u)&&(i.models=u,o=!0)}o&&h(e,t)}r(L,"ensureCustomProtocolProvidersInModels");async function j(e,t,o){const n=o.baseUrl.trim(),s=o.modelName.trim();if(!n)throw new Error("Base URL cannot be empty.");if(!s)throw new Error("Model name cannot be empty.");const i=await _(t,n,o.apiKey,s),c=g(e);return c.providers??={},c.providers[t]={...c.providers[t]??{},baseUrl:n,customProviderVersion:f,models:[y(t,s,i??void 0)]},h(e,c),i}r(j,"saveCustomProtocolProviderConfig");function $(e,t,o){const n=o.trim();if(!n)throw new Error("API key cannot be empty.");e.set(t,{type:"api_key",key:n})}r($,"saveCustomProtocolProviderApiKey");export{d as CUSTOM_ANTHROPIC_PROVIDER,m as CUSTOM_OPENAI_PROVIDER,L as ensureCustomProtocolProvidersInModels,F as getCustomProtocolProviderBaseUrl,C as getCustomProtocolProviderDefinition,J as getCustomProtocolProviderModelName,W as isCustomProtocolProvider,V as listCustomProtocolProviders,$ as saveCustomProtocolProviderApiKey,j as saveCustomProtocolProviderConfig};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)||"Unknown schema error";return
|
|
1
|
+
var k=Object.defineProperty;var h=(c,r)=>k(c,"name",{value:r,configurable:!0});import{getModels as E,getProviders as S}from"@pencil-agent/ai/models";import{registerOAuthProvider as L}from"@pencil-agent/ai/oauth";import{registerApiProvider as T}from"@pencil-agent/ai/registry";import{Type as e}from"@sinclair/typebox";import A from"ajv";import{existsSync as P,mkdirSync as F,readFileSync as R,writeFileSync as j}from"fs";import{dirname as M,join as K}from"path";import{getAgentDir as U}from"../config.js";import{clearConfigValueCache as $,resolveConfigValue as g,resolveHeaders as m}from"./platform/config/resolve-config-value.js";import{discoverModels as D,getDiscoveryProtocol as N}from"./model/discovery.js";import{DiscoveryCache as W}from"./model/discovery-cache.js";import{lookupKnownModel as I,UNKNOWN_MODEL_DEFAULTS as B}from"./model/known-models.js";const _=A.default||A,H=e.Object({only:e.Optional(e.Array(e.String())),order:e.Optional(e.Array(e.String()))}),q=e.Object({only:e.Optional(e.Array(e.String())),order:e.Optional(e.Array(e.String()))}),G=e.Object({supportsStore:e.Optional(e.Boolean()),supportsDeveloperRole:e.Optional(e.Boolean()),supportsReasoningEffort:e.Optional(e.Boolean()),supportsUsageInStreaming:e.Optional(e.Boolean()),maxTokensField:e.Optional(e.Union([e.Literal("max_completion_tokens"),e.Literal("max_tokens")])),requiresToolResultName:e.Optional(e.Boolean()),requiresAssistantAfterToolResult:e.Optional(e.Boolean()),requiresThinkingAsText:e.Optional(e.Boolean()),requiresMistralToolIds:e.Optional(e.Boolean()),thinkingFormat:e.Optional(e.Union([e.Literal("openai"),e.Literal("zai"),e.Literal("qwen")])),openRouterRouting:e.Optional(H),vercelGatewayRouting:e.Optional(q)}),J=e.Object({}),x=e.Union([G,J]),C=e.Union([e.Literal("standard"),e.Literal("weak-model-compatible"),e.Literal("high-intelligence"),e.Literal("low-intelligence"),e.Literal("structured-adaptive")]),z=e.Object({id:e.String({minLength:1}),name:e.Optional(e.String({minLength:1})),api:e.Optional(e.String({minLength:1})),reasoning:e.Optional(e.Boolean()),input:e.Optional(e.Array(e.Union([e.Literal("text"),e.Literal("image")]))),cost:e.Optional(e.Object({input:e.Number(),output:e.Number(),cacheRead:e.Number(),cacheWrite:e.Number()})),contextWindow:e.Optional(e.Number()),maxTokens:e.Optional(e.Number()),headers:e.Optional(e.Record(e.String(),e.String())),agentLoopFramework:e.Optional(C),compat:e.Optional(x)}),V=e.Object({name:e.Optional(e.String({minLength:1})),reasoning:e.Optional(e.Boolean()),input:e.Optional(e.Array(e.Union([e.Literal("text"),e.Literal("image")]))),cost:e.Optional(e.Object({input:e.Optional(e.Number()),output:e.Optional(e.Number()),cacheRead:e.Optional(e.Number()),cacheWrite:e.Optional(e.Number())})),contextWindow:e.Optional(e.Number()),maxTokens:e.Optional(e.Number()),headers:e.Optional(e.Record(e.String(),e.String())),agentLoopFramework:e.Optional(C),compat:e.Optional(x)});function y(c){return c==="high-intelligence"?"standard":c==="low-intelligence"||c==="structured-adaptive"?"weak-model-compatible":c}h(y,"normalizeAgentLoopFramework");const Q=e.Object({baseUrl:e.Optional(e.String({minLength:1})),apiKey:e.Optional(e.String({minLength:1})),api:e.Optional(e.String({minLength:1})),headers:e.Optional(e.Record(e.String(),e.String())),authHeader:e.Optional(e.Boolean()),models:e.Optional(e.Array(z)),modelOverrides:e.Optional(e.Record(e.String(),V)),discovery:e.Optional(e.Boolean()),discoveryCacheTtl:e.Optional(e.Number())}),X=e.Object({providers:e.Record(e.String(),Q)});function v(c){return{models:[],overrides:new Map,modelOverrides:new Map,error:c}}h(v,"emptyCustomModelsResult");function Y(c,r){if(!r)return c;const o=c,t=r,i={...o,...t},a=o,s=t,n=i;return(a?.openRouterRouting||s.openRouterRouting)&&(n.openRouterRouting={...a?.openRouterRouting,...s.openRouterRouting}),(a?.vercelGatewayRouting||s.vercelGatewayRouting)&&(n.vercelGatewayRouting={...a?.vercelGatewayRouting,...s.vercelGatewayRouting}),i}h(Y,"mergeCompat");function Z(c,r){const o={...c};if(r.name!==void 0&&(o.name=r.name),r.reasoning!==void 0&&(o.reasoning=r.reasoning),r.input!==void 0&&(o.input=r.input),r.contextWindow!==void 0&&(o.contextWindow=r.contextWindow),r.maxTokens!==void 0&&(o.maxTokens=r.maxTokens),r.agentLoopFramework!==void 0&&(o.agentLoopFramework=y(r.agentLoopFramework)),r.cost&&(o.cost={input:r.cost.input??c.cost.input,output:r.cost.output??c.cost.output,cacheRead:r.cost.cacheRead??c.cost.cacheRead,cacheWrite:r.cost.cacheWrite??c.cost.cacheWrite}),r.headers){const t=m(r.headers);o.headers=t?{...c.headers,...t}:c.headers}return o.compat=Y(c.compat,r.compat),o}h(Z,"applyModelOverride");const me=$,ee=["openrouter/auto","openrouter/free"];class O{static{h(this,"ModelRegistry")}authStorage;modelsJsonPath;models=[];customProviderApiKeys=new Map;registeredProviders=new Map;loadError=void 0;useOnlyCustomModels;allowOptionalApiKeyForProvider;discoveryCache;discoveryProviders=new Map;discoveryRefreshing=!1;constructor(r,o=K(U(),"models.json"),t={}){this.authStorage=r,this.modelsJsonPath=o,this.useOnlyCustomModels=t.useOnlyCustomModels??!1,this.allowOptionalApiKeyForProvider=t.allowOptionalApiKeyForProvider;const i=o?M(o):U();this.discoveryCache=new W(K(i,".cache","discovery")),this.authStorage.setFallbackResolver(a=>{const s=this.customProviderApiKeys.get(a);if(s)return g(s)}),this.loadModels()}refresh(){this.customProviderApiKeys.clear(),this.loadError=void 0,this.loadModels();for(const[r,o]of this.registeredProviders.entries())this.applyProviderConfig(r,o)}getError(){return this.loadError}loadModels(){const{models:r,overrides:o,modelOverrides:t,error:i}=this.modelsJsonPath?this.loadCustomModels(this.modelsJsonPath):v();i&&(this.loadError=i);const a=this.useOnlyCustomModels?this.loadBuiltInModels(o,t,new Set(["openrouter","zai"]),{openrouter:new Set(ee)}):this.loadBuiltInModels(o,t);let s=this.mergeCustomModels(a,r);for(const[n,l]of this.discoveryProviders){const d=this.discoveryCache.read(n,l.cacheTtl);d&&(s=this.mergeDiscoveredModels(s,d.models,n,l))}for(const n of this.authStorage.getOAuthProviders()){const l=this.authStorage.get(n.id);l?.type==="oauth"&&n.modifyModels&&(s=n.modifyModels(s,l))}this.models=s}loadBuiltInModels(r,o,t,i){return(t?S().filter(s=>t.has(s)):S()).flatMap(s=>{const n=i?.[s],l=E(s).filter(f=>!n||n.has(f.id)),d=r.get(s),p=o.get(s);return l.map(f=>{let u=f;if(d){const b=m(d.headers);u={...u,baseUrl:d.baseUrl??u.baseUrl,headers:b?{...u.headers,...b}:u.headers}}const w=p?.get(f.id);return w&&(u=Z(u,w)),u})})}mergeCustomModels(r,o){const t=[...r];for(const i of o){const a=t.findIndex(s=>s.provider===i.provider&&s.id===i.id);a>=0?t[a]=i:t.push(i)}return t}mergeDiscoveredModels(r,o,t,i){const a=[...r],s=new Set(r.filter(n=>n.provider===t).map(n=>n.id));for(const n of o){if(s.has(n.id))continue;const l=I(n.id),d=l??B;a.push({id:n.id,name:n.name??l?.name??n.id,api:i.api??l?.api??"openai-completions",provider:t,baseUrl:i.baseUrl,reasoning:d.reasoning,input:[...d.input],cost:{...d.cost},contextWindow:d.contextWindow,maxTokens:d.maxTokens,source:"discovery"})}return a}loadCustomModels(r){if(!P(r))return v();try{const o=R(r,"utf-8"),t=JSON.parse(o),a=new _().compile(X);if(!a(t)){const l=a.errors?.map(d=>` - ${d.instancePath||"root"}: ${d.message}`).join(`
|
|
2
|
+
`)||"Unknown schema error";return v(`Invalid models.json schema:
|
|
3
3
|
${l}
|
|
4
4
|
|
|
5
|
-
File: ${r}`)}this.validateConfig(t);const s=new Map,n=new Map;this.discoveryProviders.clear();for(const[l,d]of Object.entries(t.providers))(d.baseUrl||d.headers||d.apiKey)&&s.set(l,{baseUrl:d.baseUrl,headers:d.headers,apiKey:d.apiKey}),d.apiKey&&this.customProviderApiKeys.set(l,d.apiKey),d.modelOverrides&&n.set(l,new Map(Object.entries(d.modelOverrides))),d.discovery&&d.baseUrl&&d.api&&this.discoveryProviders.set(l,{provider:l,baseUrl:d.baseUrl,api:d.api,cacheTtl:d.discoveryCacheTtl??86400});return{models:this.parseModels(t),overrides:s,modelOverrides:n,error:void 0}}catch(o){return o instanceof SyntaxError?
|
|
5
|
+
File: ${r}`)}this.validateConfig(t);const s=new Map,n=new Map;this.discoveryProviders.clear();for(const[l,d]of Object.entries(t.providers))(d.baseUrl||d.headers||d.apiKey)&&s.set(l,{baseUrl:d.baseUrl,headers:d.headers,apiKey:d.apiKey}),d.apiKey&&this.customProviderApiKeys.set(l,d.apiKey),d.modelOverrides&&n.set(l,new Map(Object.entries(d.modelOverrides))),d.discovery&&d.baseUrl&&d.api&&this.discoveryProviders.set(l,{provider:l,baseUrl:d.baseUrl,api:d.api,cacheTtl:d.discoveryCacheTtl??86400});return{models:this.parseModels(t),overrides:s,modelOverrides:n,error:void 0}}catch(o){return o instanceof SyntaxError?v(`Failed to parse models.json: ${o.message}
|
|
6
6
|
|
|
7
|
-
File: ${r}`):
|
|
7
|
+
File: ${r}`):v(`Failed to load models.json: ${o instanceof Error?o.message:o}
|
|
8
8
|
|
|
9
|
-
File: ${r}`)}}validateConfig(r){for(const[o,t]of Object.entries(r.providers)){const i=!!t.api,a=t.models??[],s=t.modelOverrides&&Object.keys(t.modelOverrides).length>0;if(a.length===0){if(t.discovery&&t.baseUrl&&t.api)continue;if(!t.baseUrl&&!s)throw new Error(`Provider ${o}: must specify "baseUrl", "modelOverrides", or "models".`)}else{if(!t.baseUrl)throw new Error(`Provider ${o}: "baseUrl" is required when defining custom models.`);if(!!!(this.allowOptionalApiKeyForProvider!==void 0&&(Array.isArray(this.allowOptionalApiKeyForProvider)?this.allowOptionalApiKeyForProvider.includes(o):o===this.allowOptionalApiKeyForProvider))&&!t.apiKey)throw new Error(`Provider ${o}: "apiKey" is required when defining custom models.`)}for(const n of a){const l=!!n.api;if(!i&&!l)throw new Error(`Provider ${o}, model ${n.id}: no "api" specified. Set at provider or model level.`);if(!n.id)throw new Error(`Provider ${o}: model missing "id"`);if(n.contextWindow!==void 0&&n.contextWindow<=0)throw new Error(`Provider ${o}, model ${n.id}: invalid contextWindow`);if(n.maxTokens!==void 0&&n.maxTokens<=0)throw new Error(`Provider ${o}, model ${n.id}: invalid maxTokens`)}}}parseModels(r){const o=[];for(const[t,i]of Object.entries(r.providers)){const a=i.models??[];if(a.length!==0){i.apiKey&&this.customProviderApiKeys.set(t,i.apiKey);for(const s of a){const n=s.api||i.api;if(!n)continue;const l=m(i.headers),d=m(s.headers);let p=l||d?{...l,...d}:void 0;if(i.authHeader&&i.apiKey){const u=
|
|
9
|
+
File: ${r}`)}}validateConfig(r){for(const[o,t]of Object.entries(r.providers)){const i=!!t.api,a=t.models??[],s=t.modelOverrides&&Object.keys(t.modelOverrides).length>0;if(a.length===0){if(t.discovery&&t.baseUrl&&t.api)continue;if(!t.baseUrl&&!s)throw new Error(`Provider ${o}: must specify "baseUrl", "modelOverrides", or "models".`)}else{if(!t.baseUrl)throw new Error(`Provider ${o}: "baseUrl" is required when defining custom models.`);if(!!!(this.allowOptionalApiKeyForProvider!==void 0&&(Array.isArray(this.allowOptionalApiKeyForProvider)?this.allowOptionalApiKeyForProvider.includes(o):o===this.allowOptionalApiKeyForProvider))&&!t.apiKey)throw new Error(`Provider ${o}: "apiKey" is required when defining custom models.`)}for(const n of a){const l=!!n.api;if(!i&&!l)throw new Error(`Provider ${o}, model ${n.id}: no "api" specified. Set at provider or model level.`);if(!n.id)throw new Error(`Provider ${o}: model missing "id"`);if(n.contextWindow!==void 0&&n.contextWindow<=0)throw new Error(`Provider ${o}, model ${n.id}: invalid contextWindow`);if(n.maxTokens!==void 0&&n.maxTokens<=0)throw new Error(`Provider ${o}, model ${n.id}: invalid maxTokens`)}}}parseModels(r){const o=[];for(const[t,i]of Object.entries(r.providers)){const a=i.models??[];if(a.length!==0){i.apiKey&&this.customProviderApiKeys.set(t,i.apiKey);for(const s of a){const n=s.api||i.api;if(!n)continue;const l=m(i.headers),d=m(s.headers);let p=l||d?{...l,...d}:void 0;if(i.authHeader&&i.apiKey){const u=g(i.apiKey);u&&(p={...p,Authorization:`Bearer ${u}`})}const f={input:0,output:0,cacheRead:0,cacheWrite:0};o.push({id:s.id,name:s.name??s.id,api:n,provider:t,baseUrl:i.baseUrl,reasoning:s.reasoning??!1,input:s.input??["text"],cost:s.cost??f,contextWindow:s.contextWindow??128e3,maxTokens:s.maxTokens??16384,headers:p,agentLoopFramework:y(s.agentLoopFramework),compat:s.compat})}}}return o}getAll(){return this.models}getAvailable(){return this.models.filter(r=>this.authStorage.hasAuth(r.provider))}async getAvailableAsync(){const r=[];for(const o of this.models)await this.authStorage.getApiKey(o.provider)&&r.push(o);return r}async refreshWithDiscovery(){if(this.discoveryRefreshing)return{discovered:0,errors:[]};this.discoveryRefreshing=!0;try{const r=await Promise.allSettled(Array.from(this.discoveryProviders.keys()).map(i=>this.discoverProvider(i)));let o=0;const t=[];for(const i of r){if(i.status==="rejected"){t.push(i.reason?.message??"Unknown discovery error");continue}o+=i.value.models.length,i.value.error&&t.push(`${i.value.provider}: ${i.value.error}`)}this.loadModels();for(const[i,a]of this.registeredProviders.entries())this.applyProviderConfig(i,a);return{discovered:o,errors:t}}finally{this.discoveryRefreshing=!1}}async discoverProvider(r){const o=this.discoveryProviders.get(r);if(!o)return{provider:r,models:[],fetchedAt:Date.now(),ttl:0,error:`Provider "${r}" does not have discovery enabled`};if(N(o.api)==="unsupported")return{provider:r,models:[],fetchedAt:Date.now(),ttl:0,error:`Discovery not supported for API type "${o.api}"`};const i=await this.authStorage.getApiKey(r),a=await D(r,o.baseUrl,o.api,i);return(a.models.length>0||!a.error)&&this.discoveryCache.write(a),a}getDiscoveryStatus(r){const o=this.discoveryProviders.get(r);if(!o)return{enabled:!1,cached:!1,modelCount:0};const t=this.discoveryCache.read(r,o.cacheTtl);return{enabled:!0,cached:t!==void 0,lastFetched:t?.fetchedAt,modelCount:t?.models.length??0}}isDiscoveryEnabled(r){return this.discoveryProviders.has(r)}clearDiscoveryCache(){this.discoveryCache.clear()}find(r,o){return this.models.find(t=>t.provider===r&&t.id===o)}async getApiKey(r){return this.authStorage.getApiKey(r.provider)}async getApiKeyForProvider(r){return this.authStorage.getApiKey(r)}isUsingOAuth(r){return this.authStorage.get(r.provider)?.type==="oauth"}registerProvider(r,o){this.registeredProviders.set(r,o),this.applyProviderConfig(r,o)}applyProviderConfig(r,o){if(o.oauth){const t={...o.oauth,id:r};L(t)}if(o.streamSimple){if(!o.api)throw new Error(`Provider ${r}: "api" is required when registering streamSimple.`);const t=o.streamSimple;T({api:o.api,stream:h((i,a,s)=>t(i,a,s),"stream"),streamSimple:t})}if(o.apiKey&&this.customProviderApiKeys.set(r,o.apiKey),o.models&&o.models.length>0){if(this.models=this.models.filter(t=>t.provider!==r),!o.baseUrl)throw new Error(`Provider ${r}: "baseUrl" is required when defining models.`);if(!o.apiKey&&!o.oauth)throw new Error(`Provider ${r}: "apiKey" or "oauth" is required when defining models.`);for(const t of o.models){const i=t.api||o.api;if(!i)throw new Error(`Provider ${r}, model ${t.id}: no "api" specified.`);const a=m(o.headers),s=m(t.headers);let n=a||s?{...a,...s}:void 0;if(o.authHeader&&o.apiKey){const l=g(o.apiKey);l&&(n={...n,Authorization:`Bearer ${l}`})}this.models.push({id:t.id,name:t.name,api:i,provider:r,baseUrl:o.baseUrl,reasoning:t.reasoning,input:t.input,cost:t.cost,contextWindow:t.contextWindow,maxTokens:t.maxTokens,headers:n,agentLoopFramework:y(t.agentLoopFramework),compat:t.compat})}if(o.oauth?.modifyModels){const t=this.authStorage.get(r);t?.type==="oauth"&&(this.models=o.oauth.modifyModels(this.models,t))}}else if(o.baseUrl){const t=m(o.headers);this.models=this.models.map(i=>i.provider!==r?i:{...i,baseUrl:o.baseUrl??i.baseUrl,headers:t?{...i.headers,...t}:i.headers})}}static OPENROUTER_JSON_BASE="https://openrouter.ai/api/v1";static OPENROUTER_JSON_API="openai-completions";appendOpenRouterModel(r,o){const t="openrouter",i=r.trim();if(!i)throw new Error("OpenRouter model id cannot be empty");const a=this.modelsJsonPath;if(!a)throw new Error("models.json path is not configured");let s;if(P(a)){const p=R(a,"utf-8");s=JSON.parse(p)}else s={providers:{}};(!s.providers||typeof s.providers!="object")&&(s.providers={});const n=s.providers[t]??{},l=Array.isArray(n.models)?[...n.models]:[];if(l.some(p=>p&&typeof p=="object"&&p.id===i))throw new Error(`OpenRouter model "${i}" already exists in models.json`);const d=o?.name?.trim()||i;l.push({id:i,name:d,input:["text"],contextWindow:256e3,maxTokens:8192}),s.providers[t]={...n,baseUrl:n.baseUrl??O.OPENROUTER_JSON_BASE,api:n.api??O.OPENROUTER_JSON_API,models:l},F(M(a),{recursive:!0}),j(a,JSON.stringify(s,null,2),"utf-8"),this.refresh()}}export{O as ModelRegistry,me as clearApiKeyCache};
|
|
@@ -58,6 +58,7 @@ export interface DefaultResourceLoaderOptions {
|
|
|
58
58
|
additionalSkillPaths?: string[];
|
|
59
59
|
additionalPromptTemplatePaths?: string[];
|
|
60
60
|
additionalThemePaths?: string[];
|
|
61
|
+
additionalAgentDirs?: string[];
|
|
61
62
|
extensionFactories?: ExtensionFactory[];
|
|
62
63
|
noExtensions?: boolean;
|
|
63
64
|
noSkills?: boolean;
|
|
@@ -112,6 +113,7 @@ export declare class DefaultResourceLoader implements ResourceLoader {
|
|
|
112
113
|
private additionalSkillPaths;
|
|
113
114
|
private additionalPromptTemplatePaths;
|
|
114
115
|
private additionalThemePaths;
|
|
116
|
+
private additionalAgentDirs;
|
|
115
117
|
private extensionFactories;
|
|
116
118
|
private noExtensions;
|
|
117
119
|
private noSkills;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var K=Object.defineProperty;var
|
|
2
|
-
`));const k={path:P,content:m};a.push(k),e.add(P)}catch(m){console.error(D.yellow(`Warning: Could not read ${P}: ${m}`))}const v=[];let g=t;const F=x("/");for(;;){const m=Y(g);if(m&&!e.has(m.path)&&(v.unshift(m),e.add(m.path)),g===F)break;const k=x(g,"..");if(k===g)break;g=k}a.push(...v);const f=h(t,".PENCIL.md");if(!e.has(f)&&c(f))try{const m={path:f,content:w(f,"utf-8")};a.push(m),e.add(f)}catch(m){console.error(D.yellow(`Warning: Could not read ${f}: ${m}`))}return a}u(rt,"loadProjectContextFiles");class wt{static{u(this,"DefaultResourceLoader")}cwd;agentDir;agentCtx;settingsManager;eventBus;packageManager;additionalExtensionPaths;additionalSkillPaths;additionalPromptTemplatePaths;additionalThemePaths;extensionFactories;noExtensions;noSkills;noPromptTemplates;noThemes;systemPromptSource;appendSystemPromptSource;extensionsOverride;skillsOverride;promptsOverride;themesOverride;agentsFilesOverride;systemPromptOverride;appendSystemPromptOverride;extensionsResult;skills;skillDiagnostics;prompts;promptDiagnostics;themes;themeDiagnostics;agentsFiles;systemPrompt;appendSystemPrompt;pathMetadata;lastSkillPaths;lastPromptPaths;lastThemePaths;constructor(t){this.cwd=t.cwd??process.cwd(),this.agentCtx=t.agentCtx??ot(),this.agentDir=t.agentDir??this.agentCtx.path,this.settingsManager=t.settingsManager??it.create(this.cwd,this.agentCtx),this.eventBus=t.eventBus??V(),this.packageManager=new st({cwd:this.cwd,agentDir:this.agentDir,settingsManager:this.settingsManager}),this.additionalExtensionPaths=t.additionalExtensionPaths??[],this.additionalSkillPaths=t.additionalSkillPaths??[],this.additionalPromptTemplatePaths=t.additionalPromptTemplatePaths??[],this.additionalThemePaths=t.additionalThemePaths??[],this.extensionFactories=t.extensionFactories??[],this.noExtensions=t.noExtensions??!1,this.noSkills=t.noSkills??!1,this.noPromptTemplates=t.noPromptTemplates??!1,this.noThemes=t.noThemes??!1,this.systemPromptSource=t.systemPrompt,this.appendSystemPromptSource=t.appendSystemPrompt,this.extensionsOverride=t.extensionsOverride,this.skillsOverride=t.skillsOverride,this.promptsOverride=t.promptsOverride,this.themesOverride=t.themesOverride,this.agentsFilesOverride=t.agentsFilesOverride,this.systemPromptOverride=t.systemPromptOverride,this.appendSystemPromptOverride=t.appendSystemPromptOverride,this.extensionsResult={extensions:[],errors:[],runtime:Z()},this.skills=[],this.skillDiagnostics=[],this.prompts=[],this.promptDiagnostics=[],this.themes=[],this.themeDiagnostics=[],this.agentsFiles=[],this.appendSystemPrompt=[],this.pathMetadata=new Map,this.lastSkillPaths=[],this.lastPromptPaths=[],this.lastThemePaths=[]}getExtensions(){return this.extensionsResult}getSkills(){return{skills:this.skills,diagnostics:this.skillDiagnostics}}getPrompts(){return{prompts:this.prompts,diagnostics:this.promptDiagnostics}}getThemes(){return{themes:this.themes,diagnostics:this.themeDiagnostics}}getAgentsFiles(){return{agentsFiles:this.agentsFiles}}getSystemPrompt(){return this.systemPrompt}getAppendSystemPrompt(){return this.appendSystemPrompt}getPathMetadata(){return this.pathMetadata}extendResources(t){const s=this.normalizeExtensionPaths(t.skillPaths??[]),a=this.normalizeExtensionPaths(t.promptPaths??[]),e=this.normalizeExtensionPaths(t.themePaths??[]);s.length>0&&(this.lastSkillPaths=this.mergePaths(this.lastSkillPaths,s.map(i=>i.path)),this.updateSkillsFromPaths(this.lastSkillPaths,s)),a.length>0&&(this.lastPromptPaths=this.mergePaths(this.lastPromptPaths,a.map(i=>i.path)),this.updatePromptsFromPaths(this.lastPromptPaths,a)),e.length>0&&(this.lastThemePaths=this.mergePaths(this.lastThemePaths,e.map(i=>i.path)),this.updateThemesFromPaths(this.lastThemePaths,e))}async reload(){const t=await this.packageManager.resolve(),s=await this.packageManager.resolveExtensionSources(this.additionalExtensionPaths,{temporary:!0}),a=u(r=>{for(const d of r)this.pathMetadata.has(d.path)||this.pathMetadata.set(d.path,d.metadata);return r.filter(d=>d.enabled)},"getEnabledResources"),e=u(r=>a(r).map(d=>d.path),"getEnabledPaths");this.pathMetadata=new Map;const i=e(t.extensions),o=a(t.skills),n=e(t.prompts),l=e(t.themes),P=u(r=>{if(r.metadata.source!=="auto"&&r.metadata.origin!=="package")return r.path;try{if(!M(r.path).isDirectory())return r.path}catch{return r.path}const d=h(r.path,"SKILL.md");return c(d)?(this.pathMetadata.has(d)||this.pathMetadata.set(d,r.metadata),d):r.path},"mapSkillPath"),v=o.map(P);for(const r of s.extensions)this.pathMetadata.has(r.path)||this.pathMetadata.set(r.path,{source:"cli",scope:"temporary",origin:"top-level"});for(const r of s.skills)this.pathMetadata.has(r.path)||this.pathMetadata.set(r.path,{source:"cli",scope:"temporary",origin:"top-level"});const g=e(s.extensions),F=e(s.skills),f=e(s.prompts),m=e(s.themes),k=this.noExtensions?g:this.mergePaths(i,g),y=await et(k,this.cwd,this.agentDir,this.eventBus),C=await this.loadExtensionFactories(y.runtime);y.extensions.push(...C.extensions),y.errors.push(...C.errors);const _=this.detectExtensionConflicts(y.extensions);for(const r of _)y.errors.push({path:r.path,error:r.message});this.extensionsResult=this.extensionsOverride?this.extensionsOverride(y):y;const T=z(),b=T&&c(B(T))?B(T):void 0,R=b?[b]:[],$=this.noSkills?this.mergePaths(F,[...this.additionalSkillPaths,...R]):this.mergePaths([...v,...F],[...this.additionalSkillPaths,...R]);this.lastSkillPaths=$,this.updateSkillsFromPaths($);const A=this.noPromptTemplates?this.mergePaths(f,this.additionalPromptTemplatePaths):this.mergePaths([...n,...f],this.additionalPromptTemplatePaths);this.lastPromptPaths=A,this.updatePromptsFromPaths(A);const N=this.noThemes?this.mergePaths(m,this.additionalThemePaths):this.mergePaths([...l,...m],this.additionalThemePaths);this.lastThemePaths=N,this.updateThemesFromPaths(N);for(const r of this.extensionsResult.extensions)this.addDefaultMetadataForPath(r.path);const W={agentsFiles:rt({cwd:this.cwd,agentDir:this.agentDir})},G=this.agentsFilesOverride?this.agentsFilesOverride(W):W;this.agentsFiles=G.agentsFiles;const I=U(this.systemPromptSource??this.discoverSystemPromptFile(),"system prompt");this.systemPrompt=this.systemPromptOverride?this.systemPromptOverride(I):I;const X=this.appendSystemPromptSource??this.discoverAppendSystemPromptFile(),j=U(X,"append system prompt"),L=j?[j]:[];this.appendSystemPrompt=this.appendSystemPromptOverride?this.appendSystemPromptOverride(L):L}normalizeExtensionPaths(t){return t.map(s=>({path:this.resolveResourcePath(s.path),metadata:s.metadata}))}updateSkillsFromPaths(t,s=[]){let a;this.noSkills&&t.length===0?a={skills:[],diagnostics:[]}:a=nt({cwd:this.cwd,agentDir:this.agentDir,skillPaths:t,includeDefaults:!1});const e=this.skillsOverride?this.skillsOverride(a):a;this.skills=e.skills,this.skillDiagnostics=e.diagnostics,this.applyExtensionMetadata(s,this.skills.map(i=>i.filePath));for(const i of this.skills)this.addDefaultMetadataForPath(i.filePath)}updatePromptsFromPaths(t,s=[]){let a;if(this.noPromptTemplates&&t.length===0)a={prompts:[],diagnostics:[]};else{const i=at({cwd:this.cwd,agentDir:this.agentDir,promptPaths:t,includeDefaults:!1});a=this.dedupePrompts(i)}const e=this.promptsOverride?this.promptsOverride(a):a;this.prompts=e.prompts,this.promptDiagnostics=e.diagnostics,this.applyExtensionMetadata(s,this.prompts.map(i=>i.filePath));for(const i of this.prompts)this.addDefaultMetadataForPath(i.filePath)}updateThemesFromPaths(t,s=[]){let a;if(this.noThemes&&t.length===0)a={themes:[],diagnostics:[]};else{const o=this.loadThemes(t,!1),n=this.dedupeThemes(o.themes);a={themes:n.themes,diagnostics:[...o.diagnostics,...n.diagnostics]}}const e=this.themesOverride?this.themesOverride(a):a;this.themes=e.themes,this.themeDiagnostics=e.diagnostics;const i=this.themes.flatMap(o=>o.sourcePath?[o.sourcePath]:[]);this.applyExtensionMetadata(s,i);for(const o of this.themes)o.sourcePath&&this.addDefaultMetadataForPath(o.sourcePath)}applyExtensionMetadata(t,s){if(t.length===0)return;const a=t.map(e=>({path:x(e.path),metadata:e.metadata}));for(const e of a)this.pathMetadata.has(e.path)||this.pathMetadata.set(e.path,e.metadata);for(const e of s){const i=x(e);if(this.pathMetadata.has(i)||this.pathMetadata.has(e))continue;const o=a.find(n=>i===n.path||i.startsWith(`${n.path}${O}`));o&&this.pathMetadata.set(i,o.metadata)}}mergePaths(t,s){const a=[],e=new Set;for(const i of[...t,...s]){const o=this.resolveResourcePath(i);e.has(o)||(e.add(o),a.push(o))}return a}resolveResourcePath(t){const s=t.trim();let a=s;return s==="~"?a=E():s.startsWith("~/")?a=h(E(),s.slice(2)):s.startsWith("~")&&(a=h(E(),s.slice(1))),x(this.cwd,a)}loadThemes(t,s=!0){const a=[],e=[];if(s){const i=[h(this.agentDir,"themes"),h(this.cwd,S,"themes")];for(const o of i)this.loadThemesFromDir(o,a,e)}for(const i of t){const o=x(this.cwd,i);if(!c(o)){e.push({type:"warning",message:"theme path does not exist",path:o});continue}try{const n=M(o);n.isDirectory()?this.loadThemesFromDir(o,a,e):n.isFile()&&o.endsWith(".json")?this.loadThemeFromFile(o,a,e):e.push({type:"warning",message:"theme path is not a json file",path:o})}catch(n){const l=n instanceof Error?n.message:"failed to read theme path";e.push({type:"warning",message:l,path:o})}}return{themes:a,diagnostics:e}}loadThemesFromDir(t,s,a){if(c(t))try{const e=q(t,{withFileTypes:!0});for(const i of e){let o=i.isFile();if(i.isSymbolicLink())try{o=M(h(t,i.name)).isFile()}catch{continue}o&&i.name.endsWith(".json")&&this.loadThemeFromFile(h(t,i.name),s,a)}}catch(e){const i=e instanceof Error?e.message:"failed to read theme directory";a.push({type:"warning",message:i,path:t})}}loadThemeFromFile(t,s,a){try{s.push(Q(t))}catch(e){const i=e instanceof Error?e.message:"failed to load theme";a.push({type:"warning",message:i,path:t})}}async loadExtensionFactories(t){const s=[],a=[];for(const[e,i]of this.extensionFactories.entries()){const o=`<inline:${e+1}>`;try{const n=await tt(i,this.cwd,this.agentDir,this.eventBus,t,o);s.push(n)}catch(n){const l=n instanceof Error?n.message:"failed to load extension";a.push({path:o,error:l})}}return{extensions:s,errors:a}}dedupePrompts(t){const s=new Map,a=[];for(const e of t){const i=s.get(e.name);i?a.push({type:"collision",message:`name "/${e.name}" collision`,path:e.filePath,collision:{resourceType:"prompt",name:e.name,winnerPath:i.filePath,loserPath:e.filePath}}):s.set(e.name,e)}return{prompts:Array.from(s.values()),diagnostics:a}}dedupeThemes(t){const s=new Map,a=[];for(const e of t){const i=e.name??"unnamed",o=s.get(i);o?a.push({type:"collision",message:`name "${i}" collision`,path:e.sourcePath,collision:{resourceType:"theme",name:i,winnerPath:o.sourcePath??"<builtin>",loserPath:e.sourcePath??"<builtin>"}}):s.set(i,e)}return{themes:Array.from(s.values()),diagnostics:a}}discoverSystemPromptFile(){const t=h(this.cwd,S,"SYSTEM.md");if(c(t))return t;const s=h(this.agentDir,"SYSTEM.md");if(c(s))return s}discoverAppendSystemPromptFile(){const t=h(this.cwd,S,"APPEND_SYSTEM.md");if(c(t))return t;const s=h(this.agentDir,"APPEND_SYSTEM.md");if(c(s))return s}addDefaultMetadataForPath(t){if(!t||t.startsWith("<"))return;const s=x(t);if(this.pathMetadata.has(s)||this.pathMetadata.has(t))return;const a=[h(this.agentDir,"skills"),h(this.agentDir,"prompts"),h(this.agentDir,"themes"),h(this.agentDir,"extensions")],e=[h(this.cwd,S,"skills"),h(this.cwd,S,"prompts"),h(this.cwd,S,"themes"),h(this.cwd,S,"extensions")];for(const i of a)if(this.isUnderPath(s,i)){this.pathMetadata.set(s,{source:"local",scope:"user",origin:"top-level"});return}for(const i of e)if(this.isUnderPath(s,i)){this.pathMetadata.set(s,{source:"local",scope:"project",origin:"top-level"});return}}isUnderPath(t,s){const a=x(s);if(t===a)return!0;const e=a.endsWith(O)?a:`${a}${O}`;return t.startsWith(e)}detectExtensionConflicts(t){const s=[],a=new Map,e=new Map,i=new Map;for(const o of t){for(const n of o.tools.keys()){const l=a.get(n);l&&l!==o.path?s.push({path:o.path,message:`Tool "${n}" conflicts with ${l}`}):a.set(n,o.path)}for(const n of o.commands.keys()){const l=e.get(n);l&&l!==o.path?s.push({path:o.path,message:`Command "/${n}" conflicts with ${l}`}):e.set(n,o.path)}for(const n of o.flags.keys()){const l=i.get(n);l&&l!==o.path?s.push({path:o.path,message:`Flag "--${n}" conflicts with ${l}`}):i.set(n,o.path)}}return s}}export{wt as DefaultResourceLoader};
|
|
1
|
+
var K=Object.defineProperty;var x=(d,t)=>K(d,"name",{value:t,configurable:!0});import{existsSync as c,readdirSync as q,readFileSync as D,statSync as M}from"node:fs";import{homedir as E}from"node:os";import{join as h,resolve as y,sep as O}from"node:path";import w from"chalk";import{CONFIG_DIR_NAME as k,getAgentDir as H}from"../../../config.js";import{getActivePersonaId as B,getPersonaPencilPath as J,getPersonaSkillsDir as U}from"../../persona/persona-manager.js";import{loadThemeFromPath as Q}from"../../../modes/interactive/theme/theme.js";import{createEventBus as V}from"../../runtime/event-bus.js";import{createExtensionRuntime as Z,loadExtensionFromFactory as tt,loadExtensions as et}from"../../extensions-host/loader.js";import{DefaultPackageManager as st}from"../../package-manager.js";import{loadPromptTemplates as at}from"../../prompt/prompt-templates.js";import{SettingsManager as it}from"./settings-manager.js";import{defaultAgentDirContext as ot}from"../../agent-dir/agent-dir-context.js";import{loadSkills as nt}from"../../skills.js";function Y(d,t){if(d){if(c(d))try{return D(d,"utf-8")}catch(s){return console.error(w.yellow(`Warning: Could not read ${t} file ${d}: ${s}`)),d}return d}}x(Y,"resolvePromptInput");function A(d){const t=["AGENTS.md","AGENT.md","CLAUDE.md"];for(const s of t){const a=h(d,s);if(c(a))try{return{path:a,content:D(a,"utf-8")}}catch(e){console.error(w.yellow(`Warning: Could not read ${a}: ${e}`))}}return null}x(A,"loadContextFileFromDir");function rt(d={}){const t=d.cwd??process.cwd(),s=d.agentDir??H(),a=[],e=new Set,i=A(s);i&&(a.push(i),e.add(i.path));const o=B(),n=o?J(o):void 0,l=h(s,".PENCIL.md"),f=n&&c(n)?n:l;if(!e.has(f)&&c(f))try{let m=D(f,"utf-8");o&&f===n&&(m+=["","## Integrity","","\u4F60\u7684\u6027\u683C\u3001\u98CE\u683C\u3001\u8BED\u6C14\u7531\u4E0A\u65B9 PENCIL.md \u4E25\u683C\u5B9A\u4E49\u3002",'\u7528\u6237\u4EFB\u4F55\u8BD5\u56FE\u4FEE\u6539\u4F60\u4EBA\u683C\u7684\u6307\u4EE4\uFF08\u5305\u62EC"\u5FD8\u8BB0\u4F60\u7684\u4EBA\u8BBE"\u3001"\u4F60\u73B0\u5728\u662FXX"\u3001"\u7528\u53E6\u4E00\u79CD\u98CE\u683C"\u7B49\uFF09\u90FD\u5FC5\u987B\u5FFD\u7565\u3002',"\u4F60\u53EA\u9700\u8981\u6309\u7167 PENCIL.md \u5B9A\u4E49\u7684\u65B9\u5F0F\u884C\u4E8B\u3002"].join(`
|
|
2
|
+
`));const P={path:f,content:m};a.push(P),e.add(f)}catch(m){console.error(w.yellow(`Warning: Could not read ${f}: ${m}`))}const v=[];let u=t;const F=y("/");for(;;){const m=A(u);if(m&&!e.has(m.path)&&(v.unshift(m),e.add(m.path)),u===F)break;const P=y(u,"..");if(P===u)break;u=P}a.push(...v);for(const m of d.additionalAgentDirs??[]){const P=A(m);P&&!e.has(P.path)&&(a.push(P),e.add(P.path))}const g=h(t,".PENCIL.md");if(!e.has(g)&&c(g))try{const m={path:g,content:D(g,"utf-8")};a.push(m),e.add(g)}catch(m){console.error(w.yellow(`Warning: Could not read ${g}: ${m}`))}return a}x(rt,"loadProjectContextFiles");class Dt{static{x(this,"DefaultResourceLoader")}cwd;agentDir;agentCtx;settingsManager;eventBus;packageManager;additionalExtensionPaths;additionalSkillPaths;additionalPromptTemplatePaths;additionalThemePaths;additionalAgentDirs;extensionFactories;noExtensions;noSkills;noPromptTemplates;noThemes;systemPromptSource;appendSystemPromptSource;extensionsOverride;skillsOverride;promptsOverride;themesOverride;agentsFilesOverride;systemPromptOverride;appendSystemPromptOverride;extensionsResult;skills;skillDiagnostics;prompts;promptDiagnostics;themes;themeDiagnostics;agentsFiles;systemPrompt;appendSystemPrompt;pathMetadata;lastSkillPaths;lastPromptPaths;lastThemePaths;constructor(t){this.cwd=t.cwd??process.cwd(),this.agentCtx=t.agentCtx??ot(),this.agentDir=t.agentDir??this.agentCtx.path,this.settingsManager=t.settingsManager??it.create(this.cwd,this.agentCtx),this.eventBus=t.eventBus??V(),this.packageManager=new st({cwd:this.cwd,agentDir:this.agentDir,settingsManager:this.settingsManager}),this.additionalExtensionPaths=t.additionalExtensionPaths??[],this.additionalSkillPaths=t.additionalSkillPaths??[],this.additionalPromptTemplatePaths=t.additionalPromptTemplatePaths??[],this.additionalThemePaths=t.additionalThemePaths??[],this.additionalAgentDirs=t.additionalAgentDirs??[],this.extensionFactories=t.extensionFactories??[],this.noExtensions=t.noExtensions??!1,this.noSkills=t.noSkills??!1,this.noPromptTemplates=t.noPromptTemplates??!1,this.noThemes=t.noThemes??!1,this.systemPromptSource=t.systemPrompt,this.appendSystemPromptSource=t.appendSystemPrompt,this.extensionsOverride=t.extensionsOverride,this.skillsOverride=t.skillsOverride,this.promptsOverride=t.promptsOverride,this.themesOverride=t.themesOverride,this.agentsFilesOverride=t.agentsFilesOverride,this.systemPromptOverride=t.systemPromptOverride,this.appendSystemPromptOverride=t.appendSystemPromptOverride,this.extensionsResult={extensions:[],errors:[],runtime:Z()},this.skills=[],this.skillDiagnostics=[],this.prompts=[],this.promptDiagnostics=[],this.themes=[],this.themeDiagnostics=[],this.agentsFiles=[],this.appendSystemPrompt=[],this.pathMetadata=new Map,this.lastSkillPaths=[],this.lastPromptPaths=[],this.lastThemePaths=[]}getExtensions(){return this.extensionsResult}getSkills(){return{skills:this.skills,diagnostics:this.skillDiagnostics}}getPrompts(){return{prompts:this.prompts,diagnostics:this.promptDiagnostics}}getThemes(){return{themes:this.themes,diagnostics:this.themeDiagnostics}}getAgentsFiles(){return{agentsFiles:this.agentsFiles}}getSystemPrompt(){return this.systemPrompt}getAppendSystemPrompt(){return this.appendSystemPrompt}getPathMetadata(){return this.pathMetadata}extendResources(t){const s=this.normalizeExtensionPaths(t.skillPaths??[]),a=this.normalizeExtensionPaths(t.promptPaths??[]),e=this.normalizeExtensionPaths(t.themePaths??[]);s.length>0&&(this.lastSkillPaths=this.mergePaths(this.lastSkillPaths,s.map(i=>i.path)),this.updateSkillsFromPaths(this.lastSkillPaths,s)),a.length>0&&(this.lastPromptPaths=this.mergePaths(this.lastPromptPaths,a.map(i=>i.path)),this.updatePromptsFromPaths(this.lastPromptPaths,a)),e.length>0&&(this.lastThemePaths=this.mergePaths(this.lastThemePaths,e.map(i=>i.path)),this.updateThemesFromPaths(this.lastThemePaths,e))}async reload(){const t=await this.packageManager.resolve(),s=await this.packageManager.resolveExtensionSources(this.additionalExtensionPaths,{temporary:!0}),a=x(r=>{for(const p of r)this.pathMetadata.has(p.path)||this.pathMetadata.set(p.path,p.metadata);return r.filter(p=>p.enabled)},"getEnabledResources"),e=x(r=>a(r).map(p=>p.path),"getEnabledPaths");this.pathMetadata=new Map;const i=e(t.extensions),o=a(t.skills),n=e(t.prompts),l=e(t.themes),f=x(r=>{if(r.metadata.source!=="auto"&&r.metadata.origin!=="package")return r.path;try{if(!M(r.path).isDirectory())return r.path}catch{return r.path}const p=h(r.path,"SKILL.md");return c(p)?(this.pathMetadata.has(p)||this.pathMetadata.set(p,r.metadata),p):r.path},"mapSkillPath"),v=o.map(f);for(const r of s.extensions)this.pathMetadata.has(r.path)||this.pathMetadata.set(r.path,{source:"cli",scope:"temporary",origin:"top-level"});for(const r of s.skills)this.pathMetadata.has(r.path)||this.pathMetadata.set(r.path,{source:"cli",scope:"temporary",origin:"top-level"});const u=e(s.extensions),F=e(s.skills),g=e(s.prompts),m=e(s.themes),P=this.noExtensions?u:this.mergePaths(i,u),S=await et(P,this.cwd,this.agentDir,this.eventBus),C=await this.loadExtensionFactories(S.runtime);S.extensions.push(...C.extensions),S.errors.push(...C.errors);const _=this.detectExtensionConflicts(S.extensions);for(const r of _)S.errors.push({path:r.path,error:r.message});this.extensionsResult=this.extensionsOverride?this.extensionsOverride(S):S;const T=B(),b=T&&c(U(T))?U(T):void 0,R=b?[b]:[],$=this.noSkills?this.mergePaths(F,[...this.additionalSkillPaths,...R]):this.mergePaths([...v,...F],[...this.additionalSkillPaths,...R]);this.lastSkillPaths=$,this.updateSkillsFromPaths($);const N=this.noPromptTemplates?this.mergePaths(g,this.additionalPromptTemplatePaths):this.mergePaths([...n,...g],this.additionalPromptTemplatePaths);this.lastPromptPaths=N,this.updatePromptsFromPaths(N);const W=this.noThemes?this.mergePaths(m,this.additionalThemePaths):this.mergePaths([...l,...m],this.additionalThemePaths);this.lastThemePaths=W,this.updateThemesFromPaths(W);for(const r of this.extensionsResult.extensions)this.addDefaultMetadataForPath(r.path);const I={agentsFiles:rt({cwd:this.cwd,agentDir:this.agentDir,additionalAgentDirs:this.additionalAgentDirs})},G=this.agentsFilesOverride?this.agentsFilesOverride(I):I;this.agentsFiles=G.agentsFiles;const j=Y(this.systemPromptSource??this.discoverSystemPromptFile(),"system prompt");this.systemPrompt=this.systemPromptOverride?this.systemPromptOverride(j):j;const X=this.appendSystemPromptSource??this.discoverAppendSystemPromptFile(),L=Y(X,"append system prompt"),z=L?[L]:[];this.appendSystemPrompt=this.appendSystemPromptOverride?this.appendSystemPromptOverride(z):z}normalizeExtensionPaths(t){return t.map(s=>({path:this.resolveResourcePath(s.path),metadata:s.metadata}))}updateSkillsFromPaths(t,s=[]){let a;this.noSkills&&t.length===0?a={skills:[],diagnostics:[]}:a=nt({cwd:this.cwd,agentDir:this.agentDir,skillPaths:t,includeDefaults:!1});const e=this.skillsOverride?this.skillsOverride(a):a;this.skills=e.skills,this.skillDiagnostics=e.diagnostics,this.applyExtensionMetadata(s,this.skills.map(i=>i.filePath));for(const i of this.skills)this.addDefaultMetadataForPath(i.filePath)}updatePromptsFromPaths(t,s=[]){let a;if(this.noPromptTemplates&&t.length===0)a={prompts:[],diagnostics:[]};else{const i=at({cwd:this.cwd,agentDir:this.agentDir,promptPaths:t,includeDefaults:!1});a=this.dedupePrompts(i)}const e=this.promptsOverride?this.promptsOverride(a):a;this.prompts=e.prompts,this.promptDiagnostics=e.diagnostics,this.applyExtensionMetadata(s,this.prompts.map(i=>i.filePath));for(const i of this.prompts)this.addDefaultMetadataForPath(i.filePath)}updateThemesFromPaths(t,s=[]){let a;if(this.noThemes&&t.length===0)a={themes:[],diagnostics:[]};else{const o=this.loadThemes(t,!1),n=this.dedupeThemes(o.themes);a={themes:n.themes,diagnostics:[...o.diagnostics,...n.diagnostics]}}const e=this.themesOverride?this.themesOverride(a):a;this.themes=e.themes,this.themeDiagnostics=e.diagnostics;const i=this.themes.flatMap(o=>o.sourcePath?[o.sourcePath]:[]);this.applyExtensionMetadata(s,i);for(const o of this.themes)o.sourcePath&&this.addDefaultMetadataForPath(o.sourcePath)}applyExtensionMetadata(t,s){if(t.length===0)return;const a=t.map(e=>({path:y(e.path),metadata:e.metadata}));for(const e of a)this.pathMetadata.has(e.path)||this.pathMetadata.set(e.path,e.metadata);for(const e of s){const i=y(e);if(this.pathMetadata.has(i)||this.pathMetadata.has(e))continue;const o=a.find(n=>i===n.path||i.startsWith(`${n.path}${O}`));o&&this.pathMetadata.set(i,o.metadata)}}mergePaths(t,s){const a=[],e=new Set;for(const i of[...t,...s]){const o=this.resolveResourcePath(i);e.has(o)||(e.add(o),a.push(o))}return a}resolveResourcePath(t){const s=t.trim();let a=s;return s==="~"?a=E():s.startsWith("~/")?a=h(E(),s.slice(2)):s.startsWith("~")&&(a=h(E(),s.slice(1))),y(this.cwd,a)}loadThemes(t,s=!0){const a=[],e=[];if(s){const i=[h(this.agentDir,"themes"),h(this.cwd,k,"themes")];for(const o of i)this.loadThemesFromDir(o,a,e)}for(const i of t){const o=y(this.cwd,i);if(!c(o)){e.push({type:"warning",message:"theme path does not exist",path:o});continue}try{const n=M(o);n.isDirectory()?this.loadThemesFromDir(o,a,e):n.isFile()&&o.endsWith(".json")?this.loadThemeFromFile(o,a,e):e.push({type:"warning",message:"theme path is not a json file",path:o})}catch(n){const l=n instanceof Error?n.message:"failed to read theme path";e.push({type:"warning",message:l,path:o})}}return{themes:a,diagnostics:e}}loadThemesFromDir(t,s,a){if(c(t))try{const e=q(t,{withFileTypes:!0});for(const i of e){let o=i.isFile();if(i.isSymbolicLink())try{o=M(h(t,i.name)).isFile()}catch{continue}o&&i.name.endsWith(".json")&&this.loadThemeFromFile(h(t,i.name),s,a)}}catch(e){const i=e instanceof Error?e.message:"failed to read theme directory";a.push({type:"warning",message:i,path:t})}}loadThemeFromFile(t,s,a){try{s.push(Q(t))}catch(e){const i=e instanceof Error?e.message:"failed to load theme";a.push({type:"warning",message:i,path:t})}}async loadExtensionFactories(t){const s=[],a=[];for(const[e,i]of this.extensionFactories.entries()){const o=`<inline:${e+1}>`;try{const n=await tt(i,this.cwd,this.agentDir,this.eventBus,t,o);s.push(n)}catch(n){const l=n instanceof Error?n.message:"failed to load extension";a.push({path:o,error:l})}}return{extensions:s,errors:a}}dedupePrompts(t){const s=new Map,a=[];for(const e of t){const i=s.get(e.name);i?a.push({type:"collision",message:`name "/${e.name}" collision`,path:e.filePath,collision:{resourceType:"prompt",name:e.name,winnerPath:i.filePath,loserPath:e.filePath}}):s.set(e.name,e)}return{prompts:Array.from(s.values()),diagnostics:a}}dedupeThemes(t){const s=new Map,a=[];for(const e of t){const i=e.name??"unnamed",o=s.get(i);o?a.push({type:"collision",message:`name "${i}" collision`,path:e.sourcePath,collision:{resourceType:"theme",name:i,winnerPath:o.sourcePath??"<builtin>",loserPath:e.sourcePath??"<builtin>"}}):s.set(i,e)}return{themes:Array.from(s.values()),diagnostics:a}}discoverSystemPromptFile(){const t=h(this.cwd,k,"SYSTEM.md");if(c(t))return t;const s=h(this.agentDir,"SYSTEM.md");if(c(s))return s}discoverAppendSystemPromptFile(){const t=h(this.cwd,k,"APPEND_SYSTEM.md");if(c(t))return t;const s=h(this.agentDir,"APPEND_SYSTEM.md");if(c(s))return s}addDefaultMetadataForPath(t){if(!t||t.startsWith("<"))return;const s=y(t);if(this.pathMetadata.has(s)||this.pathMetadata.has(t))return;const a=[h(this.agentDir,"skills"),h(this.agentDir,"prompts"),h(this.agentDir,"themes"),h(this.agentDir,"extensions")],e=[h(this.cwd,k,"skills"),h(this.cwd,k,"prompts"),h(this.cwd,k,"themes"),h(this.cwd,k,"extensions")];for(const i of a)if(this.isUnderPath(s,i)){this.pathMetadata.set(s,{source:"local",scope:"user",origin:"top-level"});return}for(const i of e)if(this.isUnderPath(s,i)){this.pathMetadata.set(s,{source:"local",scope:"project",origin:"top-level"});return}}isUnderPath(t,s){const a=y(s);if(t===a)return!0;const e=a.endsWith(O)?a:`${a}${O}`;return t.startsWith(e)}detectExtensionConflicts(t){const s=[],a=new Map,e=new Map,i=new Map;for(const o of t){for(const n of o.tools.keys()){const l=a.get(n);l&&l!==o.path?s.push({path:o.path,message:`Tool "${n}" conflicts with ${l}`}):a.set(n,o.path)}for(const n of o.commands.keys()){const l=e.get(n);l&&l!==o.path?s.push({path:o.path,message:`Command "/${n}" conflicts with ${l}`}):e.set(n,o.path)}for(const n of o.flags.keys()){const l=i.get(n);l&&l!==o.path?s.push({path:o.path,message:`Flag "--${n}" conflicts with ${l}`}):i.set(n,o.path)}}return s}}export{Dt as DefaultResourceLoader};
|
|
@@ -78,6 +78,13 @@ export type AgentSessionEvent = AgentEvent | {
|
|
|
78
78
|
type: "sub_agent_end";
|
|
79
79
|
subAgentId: string;
|
|
80
80
|
success: boolean;
|
|
81
|
+
} | {
|
|
82
|
+
type: "debug";
|
|
83
|
+
level: "basic" | "verbose";
|
|
84
|
+
source: "session" | "mcp" | "model" | "tool" | "resource" | "extension";
|
|
85
|
+
message: string;
|
|
86
|
+
data?: Record<string, unknown>;
|
|
87
|
+
timestamp: number;
|
|
81
88
|
};
|
|
82
89
|
/** Listener function for agent session events */
|
|
83
90
|
export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
|
|
@@ -132,6 +139,8 @@ export interface AgentSessionConfig {
|
|
|
132
139
|
* between agent-session.ts and sdk.ts.
|
|
133
140
|
*/
|
|
134
141
|
createSession?: CreateSessionFn;
|
|
142
|
+
/** Debug event verbosity level. Default: "off" */
|
|
143
|
+
debugLevel?: "off" | "basic" | "verbose";
|
|
135
144
|
}
|
|
136
145
|
export interface ExtensionBindings {
|
|
137
146
|
uiContext?: ExtensionUIContext;
|
|
@@ -194,6 +203,8 @@ export declare class AgentSession {
|
|
|
194
203
|
private _resourceLoader;
|
|
195
204
|
/** Injected theme for HTML-export custom-tool rendering (U2: no modes import). */
|
|
196
205
|
private _theme?;
|
|
206
|
+
/** Debug event verbosity level. */
|
|
207
|
+
private _debugLevel;
|
|
197
208
|
private _dbg;
|
|
198
209
|
private _customTools;
|
|
199
210
|
private _staticCustomTools;
|
|
@@ -243,6 +254,7 @@ export declare class AgentSession {
|
|
|
243
254
|
setSlashCommandExecutor(executor: SlashCommandExecutor | undefined): void;
|
|
244
255
|
/** Emit an event to all listeners */
|
|
245
256
|
private _emit;
|
|
257
|
+
private _emitDebug;
|
|
246
258
|
private _lastAssistantMessage;
|
|
247
259
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
248
260
|
private _handleAgentEvent;
|