shennian 0.4.30 → 0.4.31

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.
@@ -16,8 +16,8 @@
16
16
  },
17
17
  {
18
18
  "file": "src/agents/acp/adapter.js",
19
- "beforeBytes": 24351,
20
- "afterBytes": 12664
19
+ "beforeBytes": 24691,
20
+ "afterBytes": 12888
21
21
  },
22
22
  {
23
23
  "file": "src/agents/acp/auth-state.js",
@@ -201,8 +201,8 @@
201
201
  },
202
202
  {
203
203
  "file": "src/agents/model-registry/service.js",
204
- "beforeBytes": 5902,
205
- "afterBytes": 2563
204
+ "beforeBytes": 5997,
205
+ "afterBytes": 2572
206
206
  },
207
207
  {
208
208
  "file": "src/agents/model-registry/types.js",
@@ -1,7 +1,7 @@
1
1
  import type { AcpAgentType, ChatAttachmentMeta } from '@shennian/wire';
2
2
  import { AgentAdapter, type ImportedAgentMessage } from '../adapter.js';
3
3
  import { type AcpAgentManifest } from './types.js';
4
- import type { CompiledManagedAgentPolicy } from '../../security/managed-agent-policy.js';
4
+ import { type CompiledManagedAgentPolicy } from '../../security/managed-agent-policy.js';
5
5
  export declare class AcpAgentAdapter extends AgentAdapter {
6
6
  private readonly manifest;
7
7
  private readonly options;
@@ -1,3 +1,3 @@
1
- import{randomUUID as p}from"node:crypto";import{createHash as w}from"node:crypto";import{realpathSync as m}from"node:fs";import{pathToFileURL as I}from"node:url";import*as l from"@agentclientprotocol/sdk";import{RequestError as S}from"@agentclientprotocol/sdk";import{AgentAdapter as v,registerAgent as C}from"../adapter.js";import{mapAcpSessionUpdate as A}from"./events.js";import{AcpProcessClient as f}from"./process-client.js";import{acpAgentType as g}from"./types.js";import{runAcpTerminalAuthentication as P}from"./auth.js";import{setAcpRuntimeAuthState as u}from"./auth-state.js";import{recordAcpAuthFailure as q,recordAcpImportResult as y}from"./metrics.js";class b extends v{manifest;options;type;get recoveryCapabilities(){const e=this.client?.capabilities;return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!!(e?.sessionCapabilities?.resume||e?.loadSession)}}client=null;workDir="";shennianSessionId="";requestedSessionId=null;agentSessionId=null;runId="";seq=0;active=!1;extraEnv={};pendingPermissions=new Map;configOptions=[];modes=null;importCapture=null;emittedToolStates=new Map;pendingAuthRetry=null;retryingAfterAuth=!1;managedAgentPolicy=null;constructor(e,i={}){super(),this.manifest=e,this.options=i,this.type=g(e.id)}configure(e){this.extraEnv=e.env??{},this.managedAgentPolicy=e.managedAgentPolicy??null}async start(e,i,s){this.shennianSessionId=e;try{this.workDir=m.native(i)}catch{this.workDir=i}this.requestedSessionId=s??null,this.agentSessionId=null,this.client=new f(this.manifest.launch,{onSessionUpdate:t=>this.handleSessionUpdate(t),onPermission:t=>this.handlePermission(t)},this.buildEnvironment(),{...this.buildClientCapabilities(),terminalAuth:!0}),await this.client.start(this.workDir)}async send(e,i,s,t,n){const o=this.requireClient();this.runId=p(),this.seq=0,this.emittedToolStates.clear();try{await this.ensureSession(),await this.applyDynamicConfig(i,s),this.active=!0,this.emitEvent({state:"start",runPhase:"thinking",agentSessionId:this.agentSessionId??void 0});const r=[],a=[];n?.trim()&&a.push(n.trim()),a.push(e),r.push({type:"text",text:a.join(`
1
+ import{randomUUID as p}from"node:crypto";import{createHash as I}from"node:crypto";import{realpathSync as m}from"node:fs";import{pathToFileURL as v}from"node:url";import*as l from"@agentclientprotocol/sdk";import{RequestError as S}from"@agentclientprotocol/sdk";import{AgentAdapter as C,registerAgent as A}from"../adapter.js";import{mapAcpSessionUpdate as P}from"./events.js";import{AcpProcessClient as g}from"./process-client.js";import{acpAgentType as f}from"./types.js";import{runAcpTerminalAuthentication as q}from"./auth.js";import{setAcpRuntimeAuthState as u}from"./auth-state.js";import{recordAcpAuthFailure as E,recordAcpImportResult as y}from"./metrics.js";import{isManagedAgentNativeFull as w}from"../../security/managed-agent-policy.js";import{buildManagedNativeFullProcessEnv as b}from"../../agent-env.js";class x extends C{manifest;options;type;get recoveryCapabilities(){const e=this.client?.capabilities;return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!!(e?.sessionCapabilities?.resume||e?.loadSession)}}client=null;workDir="";shennianSessionId="";requestedSessionId=null;agentSessionId=null;runId="";seq=0;active=!1;extraEnv={};pendingPermissions=new Map;configOptions=[];modes=null;importCapture=null;emittedToolStates=new Map;pendingAuthRetry=null;retryingAfterAuth=!1;managedAgentPolicy=null;constructor(e,i={}){super(),this.manifest=e,this.options=i,this.type=f(e.id)}configure(e){this.extraEnv=e.env??{},this.managedAgentPolicy=e.managedAgentPolicy??null}async start(e,i,s){this.shennianSessionId=e;try{this.workDir=m.native(i)}catch{this.workDir=i}this.requestedSessionId=s??null,this.agentSessionId=null,this.client=new g(this.manifest.launch,{onSessionUpdate:t=>this.handleSessionUpdate(t),onPermission:t=>this.handlePermission(t)},this.buildEnvironment(),{...this.buildClientCapabilities(),terminalAuth:!0}),await this.client.start(this.workDir)}async send(e,i,s,t,n){const o=this.requireClient();this.runId=p(),this.seq=0,this.emittedToolStates.clear();try{await this.ensureSession(),await this.applyDynamicConfig(i,s),this.active=!0,this.emitEvent({state:"start",runPhase:"thinking",agentSessionId:this.agentSessionId??void 0});const r=[],a=[];n?.trim()&&a.push(n.trim()),a.push(e),r.push({type:"text",text:a.join(`
2
2
 
3
- `)});for(const h of t??[])r.push({type:"resource_link",uri:I(h.path).href,name:h.name,mimeType:h.mimeType,size:h.byteSize});const c=await this.promptWithTimeout(o,r);this.active=!1,c.stopReason==="cancelled"?this.emitEvent({state:"aborted",agentSessionId:this.agentSessionId??void 0}):this.emitEvent({state:"final",agentSessionId:this.agentSessionId??void 0})}catch(r){this.active=!1,this.cancelPendingPermissions();const a=r instanceof Error?r.message:String(r),c=r instanceof S&&r.code===-32e3;throw c&&(u(this.type,"required"),this.retryingAfterAuth||(this.pendingAuthRetry={text:e,...i?{modelId:i}:{},...s?{reasoningEffort:s}:{},...t?{attachments:[...t]}:{},...n!==void 0?{systemPrompt:n}:{}})),this.emitEvent({state:"error",message:a,...c?{errorCode:"acp_auth_required"}:{},agentSessionId:this.agentSessionId??void 0}),r}}async resume(e){this.requestedSessionId=e,this.agentSessionId=null,await this.ensureSession()}async stop(){this.active&&this.agentSessionId&&this.client&&await this.client.agent.notify(l.methods.agent.session.cancel,{sessionId:this.agentSessionId}),this.cancelPendingPermissions(),this.active=!1,await this.client?.close(),this.client=null}async getStatus(){return{active:this.active,runId:this.active?this.runId:null,canStop:this.active}}async respondToPermission(e,i){const s=this.pendingPermissions.get(e);if(!s)throw new Error("ACP permission request is no longer pending");if(!s.optionIds.has(i))throw new Error("Invalid ACP permission option");this.pendingPermissions.delete(e),clearTimeout(s.timer),s.resolve({outcome:{outcome:"selected",optionId:i}})}getAuthMethods(){return[...this.requireClient().initializeResponse?.authMethods??[]]}hasPendingAuthenticationRetry(){return this.pendingAuthRetry!==null}getSessionOptions(){const e=this.configOptions.find(o=>o.category==="model"&&o.type==="select"),i=this.configOptions.find(o=>o.category==="thought_level"&&o.type==="select"),s=this.modes?.availableModes.map(o=>({id:o.id,name:o.name,...o.description?{description:o.description}:{}}))??[],t=o=>(o?.options??[]).flatMap(r=>"options"in r?r.options:[r]),n=(i?t(i):s).map(o=>({id:"value"in o?o.value:o.id,name:o.name,...o.description?{description:o.description}:{}}));return{models:t(e).map(o=>({id:o.value,name:o.name,...o.description?{description:o.description}:{},...o.value===e?.currentValue?{isDefault:!0}:{},...n.length?{supportedReasoningEfforts:n}:{}})),modes:s}}async authenticate(e,i={}){const s=this.requireClient(),t=s.initializeResponse?.authMethods?.find(n=>n.id===e);if(!t)throw new Error("ACP authentication method is unavailable");try{if("type"in t&&t.type==="terminal"){if(!i.interactiveTerminal)throw new Error("ACP terminal authentication must be completed on the target machine with `shennian agent auth`");await s.close(),await P({...this.manifest.authLaunch??this.manifest.launch,env:Object.fromEntries(Object.entries({...this.extraEnv,...this.manifest.authLaunch?.env??this.manifest.launch.env}).filter(n=>typeof n[1]=="string"))},t,this.workDir)}else await s.authenticate(e);await this.resumeAfterExternalAuthentication()}catch(n){throw q(),n}}async logout(){await this.requireClient().logout(),u(this.type,"required")}async resumeAfterExternalAuthentication(){await this.client?.close(),this.client=new f(this.manifest.launch,{onSessionUpdate:i=>this.handleSessionUpdate(i),onPermission:i=>this.handlePermission(i)},this.buildEnvironment(),{...this.buildClientCapabilities(),terminalAuth:!0}),await this.client.start(this.workDir),u(this.type,"ready");const e=this.pendingAuthRetry;this.pendingAuthRetry=null,e&&this.retryAfterAuthentication(e)}async listExternalSessions(e={}){const i=this.requireClient();if(!i.capabilities.sessionCapabilities?.list)return[];const s=[];let t;for(let n=0;n<10&&s.length<500;n+=1){let o=e.cwd;if(o)try{o=m.native(o)}catch{}const r=await i.listSessions(o,t);if(s.push(...r.sessions.slice(0,500-s.length).map(a=>({agentSessionId:a.sessionId,cwd:a.cwd,...a.title?{title:a.title.slice(0,200)}:{},...a.updatedAt?{updatedAt:a.updatedAt}:{}}))),t=r.nextCursor,!t)break}return s}async loadExternalSession(e){const i=this.requireClient();if(!i.capabilities.loadSession)throw new Error("ACP session history import is unsupported");this.requestedSessionId=e,this.agentSessionId=e;const s=[];this.importCapture=s;try{const t=await i.agent.request(l.methods.agent.session.load,{sessionId:e,cwd:this.workDir,mcpServers:[]});this.configOptions=t.configOptions??[],this.modes=t.modes??null,y(!0)}catch(t){throw y(!1),t}finally{this.importCapture=null}return s.map((t,n)=>({sourceId:t.sourceHint??w("sha256").update(`${e}\0${n}\0${t.role}\0${t.payload}`).digest("hex"),role:t.role,payload:t.payload}))}async closeExternalSession(e){if(!this.requireClient().capabilities.sessionCapabilities?.close)throw new Error("ACP session close is unsupported");await this.requireClient().closeSession(e)}async deleteExternalSession(e){if(!this.requireClient().capabilities.sessionCapabilities?.delete)throw new Error("ACP session delete is unsupported");await this.requireClient().deleteSession(e)}async ensureSession(){if(this.agentSessionId)return;const e=this.requireClient();if(this.requestedSessionId){const s=e.capabilities;if(s.sessionCapabilities?.resume){const t=await e.agent.request(l.methods.agent.session.resume,{sessionId:this.requestedSessionId,cwd:this.workDir,mcpServers:[]});this.configOptions=t.configOptions??[],this.modes=t.modes??null}else if(s.loadSession){this.agentSessionId=this.requestedSessionId;const t=await e.agent.request(l.methods.agent.session.load,{sessionId:this.requestedSessionId,cwd:this.workDir,mcpServers:[]});this.configOptions=t.configOptions??[],this.modes=t.modes??null}else throw new Error(`${this.manifest.name} cannot resume ACP sessions`);this.agentSessionId=this.requestedSessionId;return}const i=await e.agent.request(l.methods.agent.session.new,{cwd:this.workDir,mcpServers:[]});this.agentSessionId=i.sessionId,this.configOptions=i.configOptions??[],this.modes=i.modes??null}async applyDynamicConfig(e,i){if(!this.agentSessionId)return;i&&this.modes?.availableModes.some(t=>t.id===i)&&(this.modes.currentModeId!==i&&(await this.requireClient().agent.request(l.methods.agent.session.setMode,{sessionId:this.agentSessionId,modeId:i}),this.modes.currentModeId=i),i=void 0);const s=[{category:"model",value:e},{category:"thought_level",value:i}];for(const t of s){if(!t.value)continue;const n=this.configOptions.find(o=>o.category===t.category&&o.type==="select");!n||n.currentValue===t.value||(await this.requireClient().agent.request(l.methods.agent.session.setConfigOption,{sessionId:this.agentSessionId,configId:n.id,value:t.value}),n.currentValue=t.value)}}handleSessionUpdate(e){if(e.sessionId===this.agentSessionId){if(this.importCapture){this.captureImportedUpdate(e.update);return}if(this.active){if(e.update.sessionUpdate==="tool_call"||e.update.sessionUpdate==="tool_call_update"){const i=JSON.stringify(e.update);if(this.emittedToolStates.get(e.update.toolCallId)===i)return;this.emittedToolStates.set(e.update.toolCallId,i)}for(const i of A(e.update,s=>this.makeEvent(s)))this.emit("agentEvent",i)}}}captureImportedUpdate(e){if(this.importCapture){if(e.sessionUpdate==="user_message_chunk"||e.sessionUpdate==="agent_message_chunk"){const i=e.content.type==="text"?e.content.text:e.content.type==="resource_link"?e.content.name||e.content.uri:`[${e.content.type}]`,s=e.sessionUpdate==="user_message_chunk"?"user":"agent",t=e.messageId?`message:${e.messageId}`:void 0,n=t?this.importCapture.find(o=>o.sourceHint===t):this.importCapture.at(-1);n?.role===s&&n.sourceHint===t?n.payload+=i:this.importCapture.push({role:s,payload:i,...t?{sourceHint:t}:{}});return}if(e.sessionUpdate==="tool_call"){const i=`tool:${e.toolCallId}`,s=this.importCapture.find(n=>n.sourceHint===i),t={role:"agent",sourceHint:i,payload:JSON.stringify({v:1,type:"tool_use",name:e.title,toolCallId:e.toolCallId,status:e.status??"running"})};s?s.payload=t.payload:this.importCapture.push(t);return}if(e.sessionUpdate==="tool_call_update"&&(e.status==="completed"||e.status==="failed")){const i=this.importCapture.find(t=>t.sourceHint===`tool:${e.toolCallId}`),s=JSON.stringify({v:1,type:"tool_result",name:e.title??"tool",toolCallId:e.toolCallId,status:e.status});i?i.payload=s:this.importCapture.push({role:"agent",sourceHint:`tool:${e.toolCallId}`,payload:s})}}}handlePermission(e){if(!this.active||e.sessionId!==this.agentSessionId)return Promise.resolve({outcome:{outcome:"cancelled"}});const i=p();return new Promise(s=>{const t=setTimeout(()=>{this.pendingPermissions.delete(i),s({outcome:{outcome:"cancelled"}})},this.options.permissionTimeoutMs??3e5);t.unref?.(),this.pendingPermissions.set(i,{optionIds:new Set(e.options.map(n=>n.optionId)),resolve:s,timer:t}),this.emitEvent({state:"approval-pending",runPhase:"waiting_approval",approval:{title:e.toolCall.title??void 0,source:this.manifest.name},permission:{id:i,sessionId:this.shennianSessionId,agentSessionId:this.agentSessionId??void 0,runId:this.runId,toolCallId:e.toolCall.toolCallId,options:e.options.map(n=>({id:n.optionId,name:n.name,kind:n.kind}))}})})}requireClient(){if(!this.client)throw new Error("ACP adapter has not started");return this.client}buildEnvironment(){return this.managedAgentPolicy?{...this.extraEnv}:{...process.env,...this.extraEnv}}buildClientCapabilities(){const e=this.manifest.clientCapabilities??{},i=this.managedAgentPolicy?.adapterPolicy;return i?{readTextFile:!!e.readTextFile,writeTextFile:!!(e.writeTextFile&&i.fileAccess!=="workspace-read"),terminal:!1}:e}async retryAfterAuthentication(e){this.retryingAfterAuth=!0;try{await this.send(e.text,e.modelId,e.reasoningEffort,e.attachments,e.systemPrompt)}catch{}finally{this.retryingAfterAuth=!1}}async promptWithTimeout(e,i){let s;try{return await Promise.race([e.agent.request(l.methods.agent.session.prompt,{sessionId:this.agentSessionId,prompt:i}),new Promise((t,n)=>{s=setTimeout(()=>n(new Error("ACP prompt timed out")),this.options.promptTimeoutMs??720*6e4),s.unref?.()})])}catch(t){throw t instanceof Error&&t.message==="ACP prompt timed out"&&(await e.close(),this.client===e&&(this.client=null)),t}finally{s&&clearTimeout(s)}}cancelPendingPermissions(){for(const e of this.pendingPermissions.values())clearTimeout(e.timer),e.resolve({outcome:{outcome:"cancelled"}});this.pendingPermissions.clear()}makeEvent(e){return{...e,runId:this.runId,seq:this.seq++}}emitEvent(e){this.emit("agentEvent",this.makeEvent(e))}}function F(d){C(g(d.id),()=>new b(d))}export{b as AcpAgentAdapter,F as registerAcpAgent};
3
+ `)});for(const h of t??[])r.push({type:"resource_link",uri:v(h.path).href,name:h.name,mimeType:h.mimeType,size:h.byteSize});const c=await this.promptWithTimeout(o,r);this.active=!1,c.stopReason==="cancelled"?this.emitEvent({state:"aborted",agentSessionId:this.agentSessionId??void 0}):this.emitEvent({state:"final",agentSessionId:this.agentSessionId??void 0})}catch(r){this.active=!1,this.cancelPendingPermissions();const a=r instanceof Error?r.message:String(r),c=r instanceof S&&r.code===-32e3;throw c&&(u(this.type,"required"),this.retryingAfterAuth||(this.pendingAuthRetry={text:e,...i?{modelId:i}:{},...s?{reasoningEffort:s}:{},...t?{attachments:[...t]}:{},...n!==void 0?{systemPrompt:n}:{}})),this.emitEvent({state:"error",message:a,...c?{errorCode:"acp_auth_required"}:{},agentSessionId:this.agentSessionId??void 0}),r}}async resume(e){this.requestedSessionId=e,this.agentSessionId=null,await this.ensureSession()}async stop(){this.active&&this.agentSessionId&&this.client&&await this.client.agent.notify(l.methods.agent.session.cancel,{sessionId:this.agentSessionId}),this.cancelPendingPermissions(),this.active=!1,await this.client?.close(),this.client=null}async getStatus(){return{active:this.active,runId:this.active?this.runId:null,canStop:this.active}}async respondToPermission(e,i){const s=this.pendingPermissions.get(e);if(!s)throw new Error("ACP permission request is no longer pending");if(!s.optionIds.has(i))throw new Error("Invalid ACP permission option");this.pendingPermissions.delete(e),clearTimeout(s.timer),s.resolve({outcome:{outcome:"selected",optionId:i}})}getAuthMethods(){return[...this.requireClient().initializeResponse?.authMethods??[]]}hasPendingAuthenticationRetry(){return this.pendingAuthRetry!==null}getSessionOptions(){const e=this.configOptions.find(o=>o.category==="model"&&o.type==="select"),i=this.configOptions.find(o=>o.category==="thought_level"&&o.type==="select"),s=this.modes?.availableModes.map(o=>({id:o.id,name:o.name,...o.description?{description:o.description}:{}}))??[],t=o=>(o?.options??[]).flatMap(r=>"options"in r?r.options:[r]),n=(i?t(i):s).map(o=>({id:"value"in o?o.value:o.id,name:o.name,...o.description?{description:o.description}:{}}));return{models:t(e).map(o=>({id:o.value,name:o.name,...o.description?{description:o.description}:{},...o.value===e?.currentValue?{isDefault:!0}:{},...n.length?{supportedReasoningEfforts:n}:{}})),modes:s}}async authenticate(e,i={}){const s=this.requireClient(),t=s.initializeResponse?.authMethods?.find(n=>n.id===e);if(!t)throw new Error("ACP authentication method is unavailable");try{if("type"in t&&t.type==="terminal"){if(!i.interactiveTerminal)throw new Error("ACP terminal authentication must be completed on the target machine with `shennian agent auth`");await s.close(),await q({...this.manifest.authLaunch??this.manifest.launch,env:Object.fromEntries(Object.entries({...this.extraEnv,...this.manifest.authLaunch?.env??this.manifest.launch.env}).filter(n=>typeof n[1]=="string"))},t,this.workDir)}else await s.authenticate(e);await this.resumeAfterExternalAuthentication()}catch(n){throw E(),n}}async logout(){await this.requireClient().logout(),u(this.type,"required")}async resumeAfterExternalAuthentication(){await this.client?.close(),this.client=new g(this.manifest.launch,{onSessionUpdate:i=>this.handleSessionUpdate(i),onPermission:i=>this.handlePermission(i)},this.buildEnvironment(),{...this.buildClientCapabilities(),terminalAuth:!0}),await this.client.start(this.workDir),u(this.type,"ready");const e=this.pendingAuthRetry;this.pendingAuthRetry=null,e&&this.retryAfterAuthentication(e)}async listExternalSessions(e={}){const i=this.requireClient();if(!i.capabilities.sessionCapabilities?.list)return[];const s=[];let t;for(let n=0;n<10&&s.length<500;n+=1){let o=e.cwd;if(o)try{o=m.native(o)}catch{}const r=await i.listSessions(o,t);if(s.push(...r.sessions.slice(0,500-s.length).map(a=>({agentSessionId:a.sessionId,cwd:a.cwd,...a.title?{title:a.title.slice(0,200)}:{},...a.updatedAt?{updatedAt:a.updatedAt}:{}}))),t=r.nextCursor,!t)break}return s}async loadExternalSession(e){const i=this.requireClient();if(!i.capabilities.loadSession)throw new Error("ACP session history import is unsupported");this.requestedSessionId=e,this.agentSessionId=e;const s=[];this.importCapture=s;try{const t=await i.agent.request(l.methods.agent.session.load,{sessionId:e,cwd:this.workDir,mcpServers:[]});this.configOptions=t.configOptions??[],this.modes=t.modes??null,y(!0)}catch(t){throw y(!1),t}finally{this.importCapture=null}return s.map((t,n)=>({sourceId:t.sourceHint??I("sha256").update(`${e}\0${n}\0${t.role}\0${t.payload}`).digest("hex"),role:t.role,payload:t.payload}))}async closeExternalSession(e){if(!this.requireClient().capabilities.sessionCapabilities?.close)throw new Error("ACP session close is unsupported");await this.requireClient().closeSession(e)}async deleteExternalSession(e){if(!this.requireClient().capabilities.sessionCapabilities?.delete)throw new Error("ACP session delete is unsupported");await this.requireClient().deleteSession(e)}async ensureSession(){if(this.agentSessionId)return;const e=this.requireClient();if(this.requestedSessionId){const s=e.capabilities;if(s.sessionCapabilities?.resume){const t=await e.agent.request(l.methods.agent.session.resume,{sessionId:this.requestedSessionId,cwd:this.workDir,mcpServers:[]});this.configOptions=t.configOptions??[],this.modes=t.modes??null}else if(s.loadSession){this.agentSessionId=this.requestedSessionId;const t=await e.agent.request(l.methods.agent.session.load,{sessionId:this.requestedSessionId,cwd:this.workDir,mcpServers:[]});this.configOptions=t.configOptions??[],this.modes=t.modes??null}else throw new Error(`${this.manifest.name} cannot resume ACP sessions`);this.agentSessionId=this.requestedSessionId;return}const i=await e.agent.request(l.methods.agent.session.new,{cwd:this.workDir,mcpServers:[]});this.agentSessionId=i.sessionId,this.configOptions=i.configOptions??[],this.modes=i.modes??null}async applyDynamicConfig(e,i){if(!this.agentSessionId)return;i&&this.modes?.availableModes.some(t=>t.id===i)&&(this.modes.currentModeId!==i&&(await this.requireClient().agent.request(l.methods.agent.session.setMode,{sessionId:this.agentSessionId,modeId:i}),this.modes.currentModeId=i),i=void 0);const s=[{category:"model",value:e},{category:"thought_level",value:i}];for(const t of s){if(!t.value)continue;const n=this.configOptions.find(o=>o.category===t.category&&o.type==="select");!n||n.currentValue===t.value||(await this.requireClient().agent.request(l.methods.agent.session.setConfigOption,{sessionId:this.agentSessionId,configId:n.id,value:t.value}),n.currentValue=t.value)}}handleSessionUpdate(e){if(e.sessionId===this.agentSessionId){if(this.importCapture){this.captureImportedUpdate(e.update);return}if(this.active){if(e.update.sessionUpdate==="tool_call"||e.update.sessionUpdate==="tool_call_update"){const i=JSON.stringify(e.update);if(this.emittedToolStates.get(e.update.toolCallId)===i)return;this.emittedToolStates.set(e.update.toolCallId,i)}for(const i of P(e.update,s=>this.makeEvent(s)))this.emit("agentEvent",i)}}}captureImportedUpdate(e){if(this.importCapture){if(e.sessionUpdate==="user_message_chunk"||e.sessionUpdate==="agent_message_chunk"){const i=e.content.type==="text"?e.content.text:e.content.type==="resource_link"?e.content.name||e.content.uri:`[${e.content.type}]`,s=e.sessionUpdate==="user_message_chunk"?"user":"agent",t=e.messageId?`message:${e.messageId}`:void 0,n=t?this.importCapture.find(o=>o.sourceHint===t):this.importCapture.at(-1);n?.role===s&&n.sourceHint===t?n.payload+=i:this.importCapture.push({role:s,payload:i,...t?{sourceHint:t}:{}});return}if(e.sessionUpdate==="tool_call"){const i=`tool:${e.toolCallId}`,s=this.importCapture.find(n=>n.sourceHint===i),t={role:"agent",sourceHint:i,payload:JSON.stringify({v:1,type:"tool_use",name:e.title,toolCallId:e.toolCallId,status:e.status??"running"})};s?s.payload=t.payload:this.importCapture.push(t);return}if(e.sessionUpdate==="tool_call_update"&&(e.status==="completed"||e.status==="failed")){const i=this.importCapture.find(t=>t.sourceHint===`tool:${e.toolCallId}`),s=JSON.stringify({v:1,type:"tool_result",name:e.title??"tool",toolCallId:e.toolCallId,status:e.status});i?i.payload=s:this.importCapture.push({role:"agent",sourceHint:`tool:${e.toolCallId}`,payload:s})}}}handlePermission(e){if(!this.active||e.sessionId!==this.agentSessionId)return Promise.resolve({outcome:{outcome:"cancelled"}});const i=p();return new Promise(s=>{const t=setTimeout(()=>{this.pendingPermissions.delete(i),s({outcome:{outcome:"cancelled"}})},this.options.permissionTimeoutMs??3e5);t.unref?.(),this.pendingPermissions.set(i,{optionIds:new Set(e.options.map(n=>n.optionId)),resolve:s,timer:t}),this.emitEvent({state:"approval-pending",runPhase:"waiting_approval",approval:{title:e.toolCall.title??void 0,source:this.manifest.name},permission:{id:i,sessionId:this.shennianSessionId,agentSessionId:this.agentSessionId??void 0,runId:this.runId,toolCallId:e.toolCall.toolCallId,options:e.options.map(n=>({id:n.optionId,name:n.name,kind:n.kind}))}})})}requireClient(){if(!this.client)throw new Error("ACP adapter has not started");return this.client}buildEnvironment(){return this.managedAgentPolicy?w(this.managedAgentPolicy)?b(this.extraEnv):{...this.extraEnv}:{...process.env,...this.extraEnv}}buildClientCapabilities(){const e=this.manifest.clientCapabilities??{},i=this.managedAgentPolicy?.adapterPolicy;return!i||w(this.managedAgentPolicy)?e:{readTextFile:!!e.readTextFile,writeTextFile:!!(e.writeTextFile&&i.fileAccess!=="workspace-read"),terminal:!1}}async retryAfterAuthentication(e){this.retryingAfterAuth=!0;try{await this.send(e.text,e.modelId,e.reasoningEffort,e.attachments,e.systemPrompt)}catch{}finally{this.retryingAfterAuth=!1}}async promptWithTimeout(e,i){let s;try{return await Promise.race([e.agent.request(l.methods.agent.session.prompt,{sessionId:this.agentSessionId,prompt:i}),new Promise((t,n)=>{s=setTimeout(()=>n(new Error("ACP prompt timed out")),this.options.promptTimeoutMs??720*6e4),s.unref?.()})])}catch(t){throw t instanceof Error&&t.message==="ACP prompt timed out"&&(await e.close(),this.client===e&&(this.client=null)),t}finally{s&&clearTimeout(s)}}cancelPendingPermissions(){for(const e of this.pendingPermissions.values())clearTimeout(e.timer),e.resolve({outcome:{outcome:"cancelled"}});this.pendingPermissions.clear()}makeEvent(e){return{...e,runId:this.runId,seq:this.seq++}}emitEvent(e){this.emit("agentEvent",this.makeEvent(e))}}function B(d){A(f(d.id),()=>new x(d))}export{x as AcpAgentAdapter,B as registerAcpAgent};
@@ -1 +1 @@
1
- import{getAgentConfigSummary as m}from"../config-status.js";import{isCacheFresh as v,readCache as l,writeCache as y}from"./cache.js";import{discoverModelsForAgent as A}from"./discovery.js";import{getAgentRuntimeSummary as f}from"../runtime-compatibility.js";import{resolveManagedAgentRuntimeProtection as C}from"../../security/managed-agent-runtime.js";const M={claude:"claude-alias-v2"};function S(i){const s=new Set,e=[];for(const o of i)!o.id||s.has(o.id)||(s.add(o.id),e.push(o));return e}function g(i){const s=i.type!=="pi"&&i.type!=="openclaw"&&!i.type.startsWith("acp:"),e=s?C({agentType:i.type,runtimeVersion:i.version,platform:i.runtimePlatform,isWsl:i.runtimeIsWsl,executableFingerprint:i.executableFingerprint,requireExecutableFingerprint:!0}):null;return{supportsManagedAgent:e?.level==="hard"||e?.level==="partial",supportsManagedNativeFull:s,supportsWorker:i.type!=="pi"&&i.type!=="openclaw"&&!i.type.startsWith("acp:"),supportsManagerMode:i.type==="codex"||i.type==="claude"||i.type==="pi-coding-agent",...e?{managedAgentProtection:e}:{}}}function F(i){const s=l();return i.map(e=>{const o=m(e.type),n=f(e);return{type:e.type,models:s.agents[e.type]?.models??e.models??[],...e.integration?{integration:e.integration}:{},...e.display?{display:e.display}:{},...e.acpCapabilities?{acpCapabilities:e.acpCapabilities}:{},...e.authState?{authState:e.authState}:{},...e.acpAuthMethods?{acpAuthMethods:e.acpAuthMethods}:{},...e.catalogRevision?{catalogRevision:e.catalogRevision}:{},...g(e),...o?{config:o}:{},...n?{runtime:n}:{}}})}async function O(i,s){const e=l();for(const o of i)delete e.agents[o.type];return y(e),R(i,s)}async function R(i,s){const e=l();let o=!1;const n=[];for(const t of i){const a=e.agents[t.type],c=M[t.type];let r;if(v(a,t.version,c))r=a.models;else{let p=null;try{p=S(await A(t,s)),r=p.length>0?p:a?.models??t.models??[]}catch{r=a?.models??t.models??[]}if(p&&p.length>0){const h={version:t.version,...c?{sourceRevision:c}:{},refreshedAt:Date.now(),models:r};JSON.stringify(a)!==JSON.stringify(h)&&(e.agents[t.type]=h,o=!0)}}const u=m(t.type),d=f(t);n.push({type:t.type,models:r,...t.integration?{integration:t.integration}:{},...t.display?{display:t.display}:{},...t.acpCapabilities?{acpCapabilities:t.acpCapabilities}:{},...t.authState?{authState:t.authState}:{},...t.acpAuthMethods?{acpAuthMethods:t.acpAuthMethods}:{},...t.catalogRevision?{catalogRevision:t.catalogRevision}:{},...g(t),...u?{config:u}:{},...d?{runtime:d}:{}})}return o&&y(e),n}export{F as getCachedAgentInfos,O as refreshAgentInfos,R as resolveAgentInfos};
1
+ import{getAgentConfigSummary as m}from"../config-status.js";import{isCacheFresh as v,readCache as l,writeCache as y}from"./cache.js";import{discoverModelsForAgent as A}from"./discovery.js";import{getAgentRuntimeSummary as f}from"../runtime-compatibility.js";import{resolveManagedAgentRuntimeProtection as C}from"../../security/managed-agent-runtime.js";const M={claude:"claude-alias-v2"};function S(i){const s=new Set,t=[];for(const o of i)!o.id||s.has(o.id)||(s.add(o.id),t.push(o));return t}function g(i){const s=i.type!=="pi"&&i.type!=="openclaw",t=s&&!i.type.startsWith("acp:"),o=s?C({agentType:i.type,runtimeVersion:i.version,platform:i.runtimePlatform,isWsl:i.runtimeIsWsl,executableFingerprint:i.executableFingerprint,requireExecutableFingerprint:!0}):null;return{supportsManagedAgent:t&&(o?.level==="hard"||o?.level==="partial"),supportsManagedNativeFull:s,supportsWorker:i.type!=="pi"&&i.type!=="openclaw"&&!i.type.startsWith("acp:"),supportsManagerMode:i.type==="codex"||i.type==="claude"||i.type==="pi-coding-agent",...o?{managedAgentProtection:o}:{}}}function E(i){const s=l();return i.map(t=>{const o=m(t.type),n=f(t);return{type:t.type,models:s.agents[t.type]?.models??t.models??[],...t.integration?{integration:t.integration}:{},...t.display?{display:t.display}:{},...t.acpCapabilities?{acpCapabilities:t.acpCapabilities}:{},...t.authState?{authState:t.authState}:{},...t.acpAuthMethods?{acpAuthMethods:t.acpAuthMethods}:{},...t.catalogRevision?{catalogRevision:t.catalogRevision}:{},...g(t),...o?{config:o}:{},...n?{runtime:n}:{}}})}async function O(i,s){const t=l();for(const o of i)delete t.agents[o.type];return y(t),R(i,s)}async function R(i,s){const t=l();let o=!1;const n=[];for(const e of i){const a=t.agents[e.type],p=M[e.type];let r;if(v(a,e.version,p))r=a.models;else{let c=null;try{c=S(await A(e,s)),r=c.length>0?c:a?.models??e.models??[]}catch{r=a?.models??e.models??[]}if(c&&c.length>0){const h={version:e.version,...p?{sourceRevision:p}:{},refreshedAt:Date.now(),models:r};JSON.stringify(a)!==JSON.stringify(h)&&(t.agents[e.type]=h,o=!0)}}const u=m(e.type),d=f(e);n.push({type:e.type,models:r,...e.integration?{integration:e.integration}:{},...e.display?{display:e.display}:{},...e.acpCapabilities?{acpCapabilities:e.acpCapabilities}:{},...e.authState?{authState:e.authState}:{},...e.acpAuthMethods?{acpAuthMethods:e.acpAuthMethods}:{},...e.catalogRevision?{catalogRevision:e.catalogRevision}:{},...g(e),...u?{config:u}:{},...d?{runtime:d}:{}})}return o&&y(t),n}export{E as getCachedAgentInfos,O as refreshAgentInfos,R as resolveAgentInfos};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.30",
3
+ "version": "0.4.31",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {