shennian 0.2.117 → 0.2.118
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/publish-build-manifest.json +12 -12
- package/dist/src/agents/cursor.d.ts +9 -0
- package/dist/src/agents/cursor.js +1 -1
- package/dist/src/agents/custom.d.ts +9 -1
- package/dist/src/agents/custom.js +4 -4
- package/dist/src/agents/gemini.d.ts +9 -0
- package/dist/src/agents/gemini.js +1 -1
- package/dist/src/agents/openclaw.d.ts +9 -0
- package/dist/src/agents/openclaw.js +2 -2
- package/dist/src/agents/opencode.d.ts +9 -0
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/platform-instructions.d.ts +1 -0
- package/dist/src/agents/platform-instructions.js +11 -5
- package/package.json +1 -1
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
"file": "src/agents/cursor.js",
|
|
54
|
-
"beforeBytes":
|
|
55
|
-
"afterBytes":
|
|
54
|
+
"beforeBytes": 9147,
|
|
55
|
+
"afterBytes": 4197
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
"file": "src/agents/custom.js",
|
|
59
|
-
"beforeBytes":
|
|
60
|
-
"afterBytes":
|
|
59
|
+
"beforeBytes": 10627,
|
|
60
|
+
"afterBytes": 5463
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
"file": "src/agents/detect.js",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
"file": "src/agents/gemini.js",
|
|
74
|
-
"beforeBytes":
|
|
75
|
-
"afterBytes":
|
|
74
|
+
"beforeBytes": 6230,
|
|
75
|
+
"afterBytes": 3224
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
"file": "src/agents/manager.js",
|
|
@@ -116,13 +116,13 @@
|
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
"file": "src/agents/openclaw.js",
|
|
119
|
-
"beforeBytes":
|
|
120
|
-
"afterBytes":
|
|
119
|
+
"beforeBytes": 10951,
|
|
120
|
+
"afterBytes": 4987
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"file": "src/agents/opencode.js",
|
|
124
|
-
"beforeBytes":
|
|
125
|
-
"afterBytes":
|
|
124
|
+
"beforeBytes": 8238,
|
|
125
|
+
"afterBytes": 4391
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
"file": "src/agents/pi-context.js",
|
|
@@ -136,8 +136,8 @@
|
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
"file": "src/agents/platform-instructions.js",
|
|
139
|
-
"beforeBytes":
|
|
140
|
-
"afterBytes":
|
|
139
|
+
"beforeBytes": 3342,
|
|
140
|
+
"afterBytes": 1913
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"file": "src/channels/base.js",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentAdapter } from './adapter.js';
|
|
2
|
+
import type { ExternalChannelSessionStatus } from '@shennian/wire';
|
|
2
3
|
export declare class CursorAdapter extends AgentAdapter {
|
|
3
4
|
readonly type: "cursor";
|
|
4
5
|
private process;
|
|
@@ -7,6 +8,14 @@ export declare class CursorAdapter extends AgentAdapter {
|
|
|
7
8
|
private seq;
|
|
8
9
|
private runId;
|
|
9
10
|
private emittedText;
|
|
11
|
+
private externalChannel;
|
|
12
|
+
private shennianSessionId;
|
|
13
|
+
private extraEnv;
|
|
14
|
+
configure(options: {
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
externalChannel?: ExternalChannelSessionStatus | null;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
}): void;
|
|
10
19
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
11
20
|
send(text: string, modelId?: string): Promise<void>;
|
|
12
21
|
resume(agentSessionId: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as l}from"node:readline";import{randomUUID as o}from"node:crypto";import{AgentAdapter as h,registerAgent as u}from"./adapter.js";import{resolveBuiltinCommand as d,spawnResolvedCommand as c}from"./command-spec.js";import{buildAgentProcessEnv as m}from"../agent-env.js";import{buildInlineChannelPreamble as p}from"./platform-instructions.js";class f extends h{type="cursor";process=null;chatId=null;workDir=null;seq=0;runId="";emittedText="";externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,s,e){this.workDir=s,this.seq=0,e&&(this.chatId=e)}async send(t,s){await this.killProcess(),this.runId=o(),this.resetRunState();const n=["-p",this.chatId?t:`${p(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0)}${t}`,"--output-format","stream-json","--stream-partial-output","--trust"];s&&n.push("--model",s),this.chatId&&n.push("--continue"),this.spawnAndParse(n)}async resume(t){await this.killProcess(),this.chatId=t,this.runId=o(),this.resetRunState(),this.spawnAndParse(["--resume",t,"--output-format","stream-json","--stream-partial-output","--trust"])}async stop(){await this.killProcess()}spawnAndParse(t){const s=d("cursor");if(!s){this.emit("error",new Error('Cursor Agent CLI not found. Expected "agent" or "cursor agent".'));return}const e=c(s,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:m(this.extraEnv)});this.process=e,l({input:e.stdout}).on("line",i=>{if(i.trim())try{const r=JSON.parse(i);this.handleStreamEvent(r)}catch{this.emitEvent({state:"delta",text:i})}});let a="";e.stderr?.on("data",i=>{a+=i.toString()}),e.on("close",i=>{if(this.process===e&&(this.process=null,i!==0&&i!==null)){const r=a.trim()||`cursor agent exited with code ${i}`;this.emitEvent({state:"error",message:r})}}),e.on("error",i=>{this.process===e&&(this.process=null,i.code==="ENOENT"?this.emit("error",new Error('Command "cursor" not found. Is Cursor CLI installed?')):this.emit("error",i))})}handleStreamEvent(t){switch(t.session_id&&!this.chatId&&(this.chatId=t.session_id),t.chat_id&&!this.chatId&&(this.chatId=t.chat_id),t.type??t.event){case"system":break;case"assistant":{if(t.message?.content){if(!this.isLiveAssistantDelta(t))break;for(const e of t.message.content)e.type==="text"&&e.text?this.emitTextDelta(e.text):e.type==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.type==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});break}(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text});break}case"user":break;case"result":{if(t.subtype==="success"||!t.is_error){this.emitFinalResultDelta(t.result);const e=t.usage;this.emitEvent({state:"final",agentSessionId:this.chatId??void 0,usage:e?{inputTokens:e.inputTokens??e.input_tokens??0,outputTokens:e.outputTokens??e.output_tokens??0}:void 0})}else this.emitEvent({state:"error",message:t.result??t.error??"unknown error"});break}case"error":{this.emitEvent({state:"error",message:t.error??"unknown error"});break}case"text":case"delta":case"content":{const e=t.delta??t.text;e&&this.emitEvent({state:"delta",text:e});break}case"done":{this.emitEvent({state:"final",agentSessionId:this.chatId??void 0});break}default:t.done?this.emitEvent({state:"final",agentSessionId:this.chatId??void 0}):(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text})}}emitEvent(t){const s={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",s)}async killProcess(){const t=this.process;t&&(this.process=null,t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}))}resetRunState(){this.seq=0,this.emittedText=""}isLiveAssistantDelta(t){return t.timestamp_ms!=null&&!t.model_call_id}emitTextDelta(t){t&&(this.emittedText+=t,this.emitEvent({state:"delta",text:t}))}emitFinalResultDelta(t){if(!t)return;if(!this.emittedText){this.emitTextDelta(t);return}if(!t.startsWith(this.emittedText))return;const s=t.slice(this.emittedText.length);s&&this.emitTextDelta(s)}}u("cursor",()=>new f);export{f as CursorAdapter};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgentAdapter } from './adapter.js';
|
|
2
|
-
import type { CustomAgentType } from '@shennian/wire';
|
|
2
|
+
import type { CustomAgentType, ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
3
|
import type { CustomAgentEntry } from '../config/index.js';
|
|
4
4
|
export declare class CustomAgentAdapter extends AgentAdapter {
|
|
5
5
|
readonly type: CustomAgentType;
|
|
@@ -11,12 +11,20 @@ export declare class CustomAgentAdapter extends AgentAdapter {
|
|
|
11
11
|
private workDir;
|
|
12
12
|
private runId;
|
|
13
13
|
private seq;
|
|
14
|
+
private externalChannel;
|
|
15
|
+
private shennianSessionId;
|
|
16
|
+
private extraEnv;
|
|
14
17
|
private stdioProcess;
|
|
15
18
|
private spawnProcess;
|
|
16
19
|
private dispatchGuard;
|
|
17
20
|
private dispatchObservedEvent;
|
|
18
21
|
constructor(name: string, entry: CustomAgentEntry);
|
|
19
22
|
start(sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
23
|
+
configure(options: {
|
|
24
|
+
sessionId?: string;
|
|
25
|
+
externalChannel?: ExternalChannelSessionStatus | null;
|
|
26
|
+
env?: NodeJS.ProcessEnv;
|
|
27
|
+
}): void;
|
|
20
28
|
send(text: string, modelId?: string): Promise<void>;
|
|
21
29
|
resume(agentSessionId: string): Promise<void>;
|
|
22
30
|
stop(): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createInterface as d}from"node:readline";import{randomUUID as
|
|
2
|
-
`)}}async stop(){this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess()}async runSpawn(s,t){await this.killSpawnProcess();const
|
|
3
|
-
`),await
|
|
4
|
-
`),await new Promise(
|
|
1
|
+
import{createInterface as d}from"node:readline";import{randomUUID as o}from"node:crypto";import{AgentAdapter as l,registerAgent as p}from"./adapter.js";import{spawnCommandString as c}from"./command-spec.js";import{buildAgentProcessEnv as h}from"../agent-env.js";import{buildInlineChannelPreamble as m}from"./platform-instructions.js";const u=3e3;class w extends l{type;command;mode;proactive;sessionId=null;agentSessionId=null;workDir=null;runId="";seq=0;externalChannel=null;shennianSessionId=null;extraEnv={};stdioProcess=null;spawnProcess=null;dispatchGuard=null;dispatchObservedEvent=!1;constructor(s,t){super(),this.type=`custom:${s}`,this.command=t.command,this.mode=t.caps.mode,this.proactive=t.caps.proactive??!1}async start(s,t,i){this.sessionId=s,this.workDir=t,this.seq=0,i&&(this.agentSessionId=i),this.mode==="stdio"&&await this.startStdioProcess()}configure(s){this.shennianSessionId=s.sessionId??null,this.externalChannel=s.externalChannel??null,this.extraEnv=s.env??{}}async send(s,t){this.runId=o(),this.seq=0;const i=this.agentSessionId?s:`${m(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0)}${s}`;this.mode==="stdio"?await this.sendStdio(i,t):await this.runSpawn(i,t)}async resume(s){if(this.mode==="stdio"){const t=JSON.stringify({method:"resume",id:o(),params:{sessionId:this.sessionId,agentSessionId:s}});this.stdioProcess?.stdin?.write(t+`
|
|
2
|
+
`)}}async stop(){this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess()}async runSpawn(s,t){await this.killSpawnProcess();const i=this.startDispatchGuard();this.dispatchObservedEvent=!1;const r=["/run","--workdir",this.workDir??process.cwd()];this.sessionId&&r.push("--session",this.sessionId),this.agentSessionId&&r.push("--resume",this.agentSessionId),t&&r.push("--model",t);const e=c(this.command,r,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:h(this.extraEnv)});this.spawnProcess=e,this.attachOutputHandlers(e,()=>{this.spawnProcess===e&&(this.spawnProcess=null)}),e.stdin?.once("error",n=>this.rejectDispatch(n)),e.stdin?.write(s),e.stdin?.end(),await i.promise}async killSpawnProcess(){const s=this.spawnProcess;s&&(this.spawnProcess=null,s.kill("SIGTERM"),await new Promise(t=>{s.on("close",t),setTimeout(()=>{s.kill("SIGKILL"),t()},3e3)}))}async startStdioProcess(){const s=c(this.command,["/start","--workdir",this.workDir??process.cwd()],{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:h(this.extraEnv)});this.stdioProcess=s,this.attachOutputHandlers(s,()=>{this.stdioProcess===s&&(this.stdioProcess=null,this.emit("error",new Error("Custom agent process exited unexpectedly")))})}async sendStdio(s,t){this.stdioProcess||await this.startStdioProcess();const i=this.startDispatchGuard();this.dispatchObservedEvent=!1;const r=JSON.stringify({method:"send",id:o(),params:{sessionId:this.sessionId,text:s,modelId:t}});this.stdioProcess?.stdin?.once("error",e=>this.rejectDispatch(e)),this.stdioProcess?.stdin?.write(r+`
|
|
3
|
+
`),await i.promise}async stopStdioProcess(){const s=this.stdioProcess;if(!s)return;const t=JSON.stringify({method:"stop",id:o()});s.stdin?.write(t+`
|
|
4
|
+
`),await new Promise(i=>{const r=setTimeout(()=>{s.kill("SIGTERM"),setTimeout(()=>{s.kill("SIGKILL"),i()},3e3)},5e3);s.on("close",()=>{clearTimeout(r),i()})}),this.stdioProcess=null}attachOutputHandlers(s,t){d({input:s.stdout}).on("line",e=>{if(e.trim())try{const n=JSON.parse(e);this.handleProtocolEvent(n)}catch{}});let r="";s.stderr?.on("data",e=>{r+=e.toString()}),s.on("close",e=>{if(t(),e!==0&&e!==null){const n=r.trim()||`Agent exited with code ${e}`;this.rejectDispatch(new Error(n)),this.emitEvent({state:"error",message:n})}else this.dispatchObservedEvent?this.acceptDispatch():this.rejectDispatch(new Error("Custom agent exited without protocol events"))}),s.on("error",e=>{t(),this.rejectDispatch(e),this.emit("error",e)})}handleProtocolEvent(s){const{state:t}=s;if(t)switch(this.dispatchObservedEvent=!0,t){case"delta":this.acceptDispatch(),this.emitEvent({state:"delta",text:s.text??"",thinking:s.thinking});break;case"final":this.acceptDispatch(),s.agentSessionId&&(this.agentSessionId=s.agentSessionId),this.emitEvent({state:"final",usage:s.usage,agentSessionId:this.agentSessionId??void 0});break;case"error":this.rejectDispatch(new Error(s.message??"unknown error")),this.emitEvent({state:"error",message:s.message??"unknown error"});break;case"tool-call":this.acceptDispatch(),this.emitEvent({state:"tool-call",name:s.name??"",args:s.args});break;case"tool-result":this.acceptDispatch(),this.emitEvent({state:"tool-result",name:s.name??"",result:s.result??""});break;case"notify":this.acceptDispatch(),this.emitEvent({state:"notify",text:s.text??"",title:s.title,source:s.source});break}}startDispatchGuard(){this.acceptDispatch();let s;const t=new Promise((i,r)=>{s={settled:!1,timer:setTimeout(()=>this.acceptDispatch(s),u),resolve:i,reject:r,promise:Promise.resolve()}});return s.promise=t,this.dispatchGuard=s,s}acceptDispatch(s=this.dispatchGuard){!s||s.settled||(s.settled=!0,clearTimeout(s.timer),this.dispatchGuard===s&&(this.dispatchGuard=null),s.resolve())}rejectDispatch(s,t=this.dispatchGuard){!t||t.settled||(t.settled=!0,clearTimeout(t.timer),this.dispatchGuard===t&&(this.dispatchGuard=null),t.reject(s))}emitEvent(s){const t={...s,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}}function E(a,s){const t=`custom:${a}`;p(t,()=>new w(a,s))}export{w as CustomAgentAdapter,E as registerCustomAgent};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentAdapter } from './adapter.js';
|
|
2
|
+
import type { ExternalChannelSessionStatus } from '@shennian/wire';
|
|
2
3
|
export declare class GeminiAdapter extends AgentAdapter {
|
|
3
4
|
readonly type: "gemini";
|
|
4
5
|
private process;
|
|
@@ -6,6 +7,14 @@ export declare class GeminiAdapter extends AgentAdapter {
|
|
|
6
7
|
private workDir;
|
|
7
8
|
private seq;
|
|
8
9
|
private runId;
|
|
10
|
+
private externalChannel;
|
|
11
|
+
private shennianSessionId;
|
|
12
|
+
private extraEnv;
|
|
13
|
+
configure(options: {
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
externalChannel?: ExternalChannelSessionStatus | null;
|
|
16
|
+
env?: NodeJS.ProcessEnv;
|
|
17
|
+
}): void;
|
|
9
18
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
10
19
|
send(text: string, modelId?: string): Promise<void>;
|
|
11
20
|
resume(agentSessionId: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as m}from"node:readline";import{randomUUID as
|
|
1
|
+
import{createInterface as m}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as d,registerAgent as h}from"./adapter.js";import{resolveBuiltinCommand as c,spawnResolvedCommand as p}from"./command-spec.js";import{buildAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as g}from"./platform-instructions.js";function r(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function I(t){if(!t)return;const e=r(t.input_tokens)??r(t.prompt_tokens)??r(t.inputTokens)??r(t.promptTokenCount),n=r(t.output_tokens)??r(t.completion_tokens)??r(t.outputTokens)??r(t.candidatesTokenCount)??r(t.responseTokenCount);if(!(e===void 0&&n===void 0))return{inputTokens:e??0,outputTokens:n??0}}class k extends d{type="gemini";process=null;agentSessionId=null;workDir=null;seq=0;runId="";externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{}}async start(e,n,i){this.workDir=n,this.seq=0,i&&(this.agentSessionId=i)}async send(e,n){await this.killProcess(),this.runId=l(),this.seq=0;const o=["-p",this.agentSessionId?e:`${g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0)}${e}`,"--output-format","stream-json"];n&&o.push("-m",n),this.agentSessionId&&o.push("--resume",this.agentSessionId),this.spawnAndParse(o)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=l(),this.seq=0,this.spawnAndParse(["--resume",e,"--output-format","stream-json"])}async stop(){await this.killProcess()}spawnAndParse(e){const n=c("gemini");if(!n){this.emit("error",new Error('Command "gemini" not found. Is Gemini CLI installed?'));return}const i=p(n,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:f(this.extraEnv)});this.process=i,m({input:i.stdout}).on("line",s=>{if(s.trim())try{const a=JSON.parse(s);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:s})}});let u="";i.stderr?.on("data",s=>{u+=s.toString()}),i.on("close",s=>{if(this.process===i&&(this.process=null,s!==0&&s!==null)){const a=u.trim()||`gemini exited with code ${s}`;this.emitEvent({state:"error",message:a})}}),i.on("error",s=>{this.process===i&&(this.process=null,s.code==="ENOENT"?this.emit("error",new Error('Command "gemini" not found. Is Gemini CLI installed?')):this.emit("error",s))})}handleStreamEvent(e){switch(e.type){case"init":{e.session_id&&(this.agentSessionId=e.session_id);break}case"message":{e.role==="assistant"&&e.content&&this.emitEvent({state:"delta",text:e.content});break}case"result":{if(e.status==="success")this.emitEvent({state:"final",agentSessionId:this.agentSessionId??void 0,usage:I(e.stats)});else if(e.status==="error"){const n=typeof e.error=="string"&&e.error||typeof e.message=="string"&&e.message||"gemini result error";this.emitEvent({state:"error",message:n})}break}default:break}}emitEvent(e){const n={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",n)}async killProcess(){const e=this.process;e&&(this.process=null,e.kill("SIGTERM"),await new Promise(n=>{e.on("close",n),setTimeout(()=>{e.kill("SIGKILL"),n()},3e3)}))}}h("gemini",()=>new k);export{k as GeminiAdapter};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentAdapter } from './adapter.js';
|
|
2
|
+
import type { ExternalChannelSessionStatus } from '@shennian/wire';
|
|
2
3
|
export declare class OpenClawAdapter extends AgentAdapter {
|
|
3
4
|
readonly type: "openclaw";
|
|
4
5
|
private process;
|
|
@@ -9,6 +10,14 @@ export declare class OpenClawAdapter extends AgentAdapter {
|
|
|
9
10
|
private workDir;
|
|
10
11
|
private seq;
|
|
11
12
|
private runId;
|
|
13
|
+
private externalChannel;
|
|
14
|
+
private shennianSessionId;
|
|
15
|
+
private extraEnv;
|
|
16
|
+
configure(options: {
|
|
17
|
+
sessionId?: string;
|
|
18
|
+
externalChannel?: ExternalChannelSessionStatus | null;
|
|
19
|
+
env?: NodeJS.ProcessEnv;
|
|
20
|
+
}): void;
|
|
12
21
|
start(sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
13
22
|
send(text: string, modelId?: string): Promise<void>;
|
|
14
23
|
resume(agentSessionId: string): Promise<void>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomUUID as
|
|
2
|
-
`)}const e=t.response??t.message??t.text??t.output??t.content??t.result;if(typeof e=="string"&&e.trim())return e}return""}function k(
|
|
1
|
+
import{randomUUID as h}from"node:crypto";import{AgentAdapter as E}from"./adapter.js";import{resolveBuiltinCommand as g,spawnResolvedCommand as w}from"./command-spec.js";import{buildAgentProcessEnv as I}from"../agent-env.js";import{buildInlineChannelPreamble as x}from"./platform-instructions.js";function C(o){if(o==null)return"";if(typeof o=="string")return o;if(typeof o=="object"){const t=o;if(Array.isArray(t.payloads)){const r=[];for(const n of t.payloads)if(n&&typeof n=="object"){const s=n.text;typeof s=="string"&&r.push(s)}if(r.join("").trim())return r.join(`
|
|
2
|
+
`)}const e=t.response??t.message??t.text??t.output??t.content??t.result;if(typeof e=="string"&&e.trim())return e}return""}function k(o){if(o==null||typeof o!="object")return;const t=o,n=t.meta?.agentMeta?.sessionId??t.sessionId??t.session_id;return typeof n=="string"&&n?n:void 0}function v(o){if(o==null||typeof o!="object")return;const t=o,n=t.meta?.agentMeta?.usage??t.usage;if(!n||typeof n!="object")return;const s=n,u=s.input??s.inputTokens??s.input_tokens,i=s.output??s.outputTokens??s.output_tokens;if(!(typeof u!="number"&&typeof i!="number"))return{inputTokens:typeof u=="number"?u:0,outputTokens:typeof i=="number"?i:0}}function y(o){if(o==null||typeof o!="object")return;const t=o,e=t.error??t.err??t.errorMessage;if(typeof e=="string"&&e.trim())return e}class O extends E{type="openclaw";process=null;sessionId=null;agentSessionId=null;currentModelId=null;workDir=null;seq=0;runId="";externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,e,r){this.sessionId=t,this.workDir=e,this.seq=0,r&&(this.agentSessionId=r)}async send(t,e){await this.killProcess(),this.runId=h(),this.seq=0;const r=this.agentSessionId??this.sessionId;if(!r){this.emitEvent({state:"error",message:"openclaw: session not started"});return}if(e&&e!==this.currentModelId){const s=await this.runAgentCommand(["--message",`/model ${e}`,"--session-id",r]);if(!s.ok){this.emitEvent({state:"error",message:s.error??"openclaw model switch failed"});return}this.currentModelId=e}const n=this.agentSessionId?t:`${x(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0)}${t}`;this.spawnAgent(["--message",n,"--session-id",r])}async resume(t){await this.killProcess(),this.agentSessionId=t,this.runId=h(),this.seq=0,this.spawnAgent(["--message","\u200B","--session-id",t])}async stop(){await this.killProcess()}spawnAgent(t){const e=g("openclaw");if(!e){this.emit("error",new Error('Command "openclaw" not found. Is OpenClaw CLI installed?'));return}const r=["agent","--local","--json",...t],n=w(e,r,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:I(this.extraEnv)});this.process=n;let s="";n.stdout?.on("data",i=>{s+=i.toString()});let u="";n.stderr?.on("data",i=>{u+=i.toString()}),n.on("close",i=>{if(this.process!==n)return;if(this.process=null,i!==0&&i!==null){const f=u.trim()||`openclaw exited with code ${i}`;this.emitEvent({state:"error",message:f});return}const a=s.trim();if(!a){const f=u.trim()||"openclaw returned empty stdout";this.emitEvent({state:"error",message:f});return}let c;try{c=JSON.parse(a)}catch{this.emitEvent({state:"error",message:`openclaw stdout is not valid JSON: ${a.slice(0,200)}`});return}const l=c;if(l.ok===!1||l.success===!1){this.emitEvent({state:"error",message:y(c)??"openclaw request failed"});return}const p=k(c);p&&(this.agentSessionId=p);const m=C(c);m&&this.emitEvent({state:"delta",text:m});const S=v(c);this.emitEvent({state:"final",agentSessionId:this.agentSessionId??void 0,usage:S})}),n.on("error",i=>{this.process===n&&(this.process=null,i.code==="ENOENT"?this.emit("error",new Error('Command "openclaw" not found. Is OpenClaw CLI installed?')):this.emit("error",i))})}async runAgentCommand(t){return new Promise(e=>{const r=g("openclaw");if(!r){e({ok:!1,error:'Command "openclaw" not found. Is OpenClaw CLI installed?'});return}const n=["agent","--local","--json",...t],s=w(r,n,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:I(this.extraEnv)});let u="",i="";s.stdout?.on("data",a=>{u+=a.toString()}),s.stderr?.on("data",a=>{i+=a.toString()}),s.on("close",a=>{if(a!==0&&a!==null){e({ok:!1,error:i.trim()||`openclaw exited with code ${a}`});return}const c=u.trim();if(!c){e({ok:!0});return}try{const l=JSON.parse(c);if(l.ok===!1||l.success===!1){e({ok:!1,error:y(l)??"openclaw request failed"});return}const d=k(l);d&&(this.agentSessionId=d),e({ok:!0,parsed:l})}catch{e({ok:!1,error:`openclaw stdout is not valid JSON: ${c.slice(0,200)}`})}}),s.on("error",a=>{e({ok:!1,error:a instanceof Error?a.message:String(a)})})})}emitEvent(t){const e={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",e)}async killProcess(){const t=this.process;t&&(this.process=null,t.kill("SIGTERM"),await new Promise(e=>{t.on("close",e),setTimeout(()=>{t.kill("SIGKILL"),e()},3e3)}))}}export{O as OpenClawAdapter};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentAdapter } from './adapter.js';
|
|
2
|
+
import type { ExternalChannelSessionStatus } from '@shennian/wire';
|
|
2
3
|
export declare class OpenCodeAdapter extends AgentAdapter {
|
|
3
4
|
readonly type: "opencode";
|
|
4
5
|
private process;
|
|
@@ -7,6 +8,14 @@ export declare class OpenCodeAdapter extends AgentAdapter {
|
|
|
7
8
|
private seq;
|
|
8
9
|
private runId;
|
|
9
10
|
private terminalState;
|
|
11
|
+
private externalChannel;
|
|
12
|
+
private shennianSessionId;
|
|
13
|
+
private extraEnv;
|
|
14
|
+
configure(options: {
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
externalChannel?: ExternalChannelSessionStatus | null;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
}): void;
|
|
10
19
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
11
20
|
send(text: string, modelId?: string): Promise<void>;
|
|
12
21
|
resume(agentSessionId: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as h}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as d,registerAgent as f}from"./adapter.js";import{resolveBuiltinCommand as m,spawnAgentCommand as c}from"./command-spec.js";import{buildAgentProcessEnv as g}from"../agent-env.js";import{buildInlineChannelPreamble as I}from"./platform-instructions.js";function S(s){if(!s)return;const t=s.input,e=s.output;if(!(typeof t!="number"&&typeof e!="number"))return{inputTokens:typeof t=="number"?t:0,outputTokens:typeof e=="number"?e:0}}function u(s){if(s==null)return"";if(typeof s=="string")return s;try{return JSON.stringify(s)}catch{return String(s)}}function p(s){if(!s)return"opencode request failed";if(typeof s=="string")return s;if(typeof s!="object")return String(s);const t=s;return typeof t.message=="string"?t.message:typeof t.name=="string"?t.name:"data"in t?p(t.data):u(s)||"opencode request failed"}class y extends d{type="opencode";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="closed";externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,e,n){this.workDir=e,n&&(this.agentSessionId=n),this.resetRunState()}async send(t,e){await this.killProcess(),this.runId=l(),this.resetRunState();const n=["run","--format","json","--dangerously-skip-permissions","--dir",this.workDir??process.cwd()];this.agentSessionId&&n.push("--session",this.agentSessionId),e&&n.push("--model",e);const o=this.agentSessionId?t:`${I(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0)}${t}`;n.push(o),this.spawnAndParse(n)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.runId=l(),this.resetRunState(),this.emitEvent({state:"start",agentSessionId:t}),this.emitFinalIfOpen({state:"final",agentSessionId:t})}async stop(){await this.killProcess()}resetRunState(){this.seq=0,this.terminalState="open"}spawnAndParse(t){const e=m("opencode");if(!e){this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".'));return}const n=c(e,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:g({NO_COLOR:"1",...this.extraEnv})});this.process=n,h({input:n.stdout}).on("line",i=>{if(i.trim())try{const r=JSON.parse(i);this.handleStreamEvent(r)}catch{this.emitEvent({state:"delta",text:i})}});let a="";n.stderr?.on("data",i=>{a+=i.toString()}),n.on("close",i=>{if(this.process!==n||(this.process=null,this.terminalState!=="open"))return;const r=a.trim();if(i!==0&&i!==null){this.emitErrorIfOpen({state:"error",message:r||`opencode exited with code ${i}`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}),n.on("error",i=>{this.process===n&&(this.process=null,i.code==="ENOENT"?this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".')):this.emit("error",i))})}handleStreamEvent(t){t.sessionID&&t.sessionID!==this.agentSessionId&&(this.agentSessionId=t.sessionID,this.emitEvent({state:"start",agentSessionId:t.sessionID}));const e=t.part;if(t.type==="text"&&e?.text){this.emitEvent({state:"delta",text:e.text});return}if(t.type==="reasoning"&&e?.text){this.emitEvent({state:"delta",text:e.text,thinking:!0});return}if(t.type==="tool_use"&&e?.type==="tool"){e.state?.status==="completed"||e.state?.status==="error"?this.emitEvent({state:"tool-result",name:e.tool??"tool",result:e.state.status==="error"?e.state.error??"tool failed":u(e.state?.output)}):this.emitEvent({state:"tool-call",name:e.tool??"tool",args:e.state?.input??{}});return}if(t.type==="error"){this.emitErrorIfOpen({state:"error",message:p(t.error)});return}t.type==="step_finish"&&this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??t.sessionID,usage:S(e?.tokens)})}emitEvent(t){const e={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",e)}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(t))}async killProcess(){const t=this.process;t&&(this.process=null,t.kill("SIGTERM"),await new Promise(e=>{t.on("close",e),setTimeout(()=>{t.kill("SIGKILL"),e()},3e3)}))}}f("opencode",()=>new y);export{y as OpenCodeAdapter};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExternalChannelSessionStatus } from '@shennian/wire';
|
|
2
2
|
export declare const PLATFORM_OUTPUT_INSTRUCTIONS = "## Shennian Output Instructions\n\nWhen you mention a local file that the user may want to open in Shennian, format it as a Markdown link using the exact local path:\n\n- Use [filename.ext](</absolute/path/to/filename.ext>) for absolute Unix, macOS, or Windows paths.\n- Use [filename.ext](<relative/path/to/filename.ext>) for paths relative to the current working directory.\n- Do not use file:// URLs for local files.\n- Do not put user-openable file paths only inside code blocks.\n- Keep normal http:// and https:// links unchanged.";
|
|
3
3
|
export declare function buildPlatformInstructions(workDir: string, externalChannel?: ExternalChannelSessionStatus | null, sessionId?: string): string;
|
|
4
|
+
export declare function buildInlineChannelPreamble(workDir: string, externalChannel?: ExternalChannelSessionStatus | null, sessionId?: string): string;
|
|
4
5
|
export declare function ensurePlatformInstructionsFile(workDir: string, externalChannel?: ExternalChannelSessionStatus | null, sessionId?: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import a from"node:fs";import i from"node:path";import{createHash as c}from"node:crypto";import{resolveShennianPath as u}from"../config/index.js";import{buildExternalChannelInstructions as l}from"./external-channel-instructions.js";import{buildInstalledSkillInstructions as f}from"../skills/registry.js";const h=`## Shennian Output Instructions
|
|
2
2
|
|
|
3
3
|
When you mention a local file that the user may want to open in Shennian, format it as a Markdown link using the exact local path:
|
|
4
4
|
|
|
@@ -6,11 +6,17 @@ When you mention a local file that the user may want to open in Shennian, format
|
|
|
6
6
|
- Use [filename.ext](<relative/path/to/filename.ext>) for paths relative to the current working directory.
|
|
7
7
|
- Do not use file:// URLs for local files.
|
|
8
8
|
- Do not put user-openable file paths only inside code blocks.
|
|
9
|
-
- Keep normal http:// and https:// links unchanged.`;function m(n){const
|
|
9
|
+
- Keep normal http:// and https:// links unchanged.`;function m(n){const t=i.join(n,"AGENTS.md");try{return a.readFileSync(t,"utf8").trim()}catch{return""}}function p(n,t,r){const o=m(n),e=l(t,i.resolve(n),r),s=f();return`${["# Shennian Agent Instructions",o?`## Project Instructions
|
|
10
10
|
|
|
11
|
-
${
|
|
11
|
+
${o}`:"",h,s,e?`## External Message Channel Instructions
|
|
12
12
|
|
|
13
|
-
${
|
|
13
|
+
${e}`:""].filter(Boolean).join(`
|
|
14
14
|
|
|
15
15
|
`)}
|
|
16
|
-
`}function b(n,
|
|
16
|
+
`}function b(n,t,r){const o=l(t,i.resolve(n),r);return o?`# \u795E\u5FF5\u5916\u90E8\u6D88\u606F\u901A\u9053\u8BF4\u660E\uFF08\u7CFB\u7EDF\u6307\u4EE4\uFF0C\u975E\u7528\u6237\u8F93\u5165\uFF0C\u8BF7\u9075\u5FAA\u540E\u518D\u56DE\u5E94\u4E0B\u65B9\u7528\u6237\u6D88\u606F\uFF09
|
|
17
|
+
|
|
18
|
+
${o}
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
`:""}function $(n,t,r){const o=c("sha256").update(`${i.resolve(n)}:${r??""}:${JSON.stringify(t??null)}`).digest("hex").slice(0,16),e=u("runtime","agent-instructions",`${o}.md`),s=p(n,t,r);a.mkdirSync(i.dirname(e),{recursive:!0});try{if(a.readFileSync(e,"utf8")===s)return e}catch{}return a.writeFileSync(e,s),e}export{h as PLATFORM_OUTPUT_INSTRUCTIONS,b as buildInlineChannelPreamble,p as buildPlatformInstructions,$ as ensurePlatformInstructionsFile};
|