shennian 0.4.10 → 0.4.13
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 +185 -55
- package/dist/src/agents/acp/adapter.d.ts +78 -0
- package/dist/src/agents/acp/adapter.js +3 -0
- package/dist/src/agents/acp/auth-state.d.ts +3 -0
- package/dist/src/agents/acp/auth-state.js +1 -0
- package/dist/src/agents/acp/auth.d.ts +7 -0
- package/dist/src/agents/acp/auth.js +1 -0
- package/dist/src/agents/acp/capability-cache.d.ts +14 -0
- package/dist/src/agents/acp/capability-cache.js +2 -0
- package/dist/src/agents/acp/catalog.d.ts +11 -0
- package/dist/src/agents/acp/catalog.js +2 -0
- package/dist/src/agents/acp/client-services.d.ts +32 -0
- package/dist/src/agents/acp/client-services.js +3 -0
- package/dist/src/agents/acp/discovery.d.ts +9 -0
- package/dist/src/agents/acp/discovery.js +1 -0
- package/dist/src/agents/acp/events.d.ts +4 -0
- package/dist/src/agents/acp/events.js +3 -0
- package/dist/src/agents/acp/manifests.d.ts +63 -0
- package/dist/src/agents/acp/manifests.js +2 -0
- package/dist/src/agents/acp/metrics.d.ts +24 -0
- package/dist/src/agents/acp/metrics.js +1 -0
- package/dist/src/agents/acp/package-commands.d.ts +4 -0
- package/dist/src/agents/acp/package-commands.js +1 -0
- package/dist/src/agents/acp/process-client.d.ts +39 -0
- package/dist/src/agents/acp/process-client.js +1 -0
- package/dist/src/agents/acp/process-tree.d.ts +2 -0
- package/dist/src/agents/acp/process-tree.js +1 -0
- package/dist/src/agents/acp/types.d.ts +31 -0
- package/dist/src/agents/acp/types.js +1 -0
- package/dist/src/agents/acp/watcher.d.ts +22 -0
- package/dist/src/agents/acp/watcher.js +1 -0
- package/dist/src/agents/adapter.d.ts +96 -2
- package/dist/src/agents/adapter.js +1 -1
- package/dist/src/agents/claude.d.ts +14 -0
- package/dist/src/agents/claude.js +5 -5
- package/dist/src/agents/codex.d.ts +6 -0
- package/dist/src/agents/codex.js +1 -1
- package/dist/src/agents/command-spec.d.ts +5 -0
- package/dist/src/agents/command-spec.js +3 -3
- package/dist/src/agents/cursor.d.ts +6 -0
- package/dist/src/agents/cursor.js +1 -1
- package/dist/src/agents/custom-recovery-conformance.d.ts +2 -0
- package/dist/src/agents/custom-recovery-conformance.js +2 -0
- package/dist/src/agents/custom.d.ts +9 -0
- package/dist/src/agents/custom.js +4 -4
- package/dist/src/agents/detect.d.ts +11 -2
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/gemini.d.ts +6 -0
- package/dist/src/agents/gemini.js +1 -1
- package/dist/src/agents/manager.d.ts +2 -0
- package/dist/src/agents/manager.js +5 -5
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/opencode.d.ts +6 -0
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/pi-coding-agent.d.ts +6 -0
- package/dist/src/agents/pi-coding-agent.js +4 -4
- package/dist/src/agents/workbuddy.d.ts +6 -0
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/commands/agent.d.ts +2 -0
- package/dist/src/commands/agent.js +2 -0
- package/dist/src/commands/daemon.d.ts +6 -2
- package/dist/src/commands/daemon.js +19 -23
- package/dist/src/commands/pair.js +10 -10
- package/dist/src/commands/upgrade.d.ts +7 -1
- package/dist/src/commands/upgrade.js +1 -1
- package/dist/src/config/index.d.ts +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/daemon-ipc/control.d.ts +16 -0
- package/dist/src/daemon-ipc/control.js +1 -0
- package/dist/src/index.js +3 -3
- package/dist/src/log-reporter.d.ts +23 -0
- package/dist/src/log-reporter.js +1 -1
- package/dist/src/relay/client.d.ts +1 -0
- package/dist/src/relay/client.js +1 -1
- package/dist/src/room/activation-service.d.ts +2 -0
- package/dist/src/room/activation-service.js +5 -5
- package/dist/src/session/handlers/agents.d.ts +1 -0
- package/dist/src/session/handlers/agents.js +1 -1
- package/dist/src/session/handlers/chat.d.ts +3 -0
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/manager.d.ts +23 -2
- package/dist/src/session/manager.js +3 -1
- package/dist/src/session/queue.d.ts +7 -1
- package/dist/src/session/queue.js +8 -8
- package/dist/src/session/run-recovery/barrier.d.ts +12 -0
- package/dist/src/session/run-recovery/barrier.js +1 -0
- package/dist/src/session/run-recovery/coordinator.d.ts +32 -0
- package/dist/src/session/run-recovery/coordinator.js +1 -0
- package/dist/src/session/run-recovery/journal.d.ts +30 -0
- package/dist/src/session/run-recovery/journal.js +2 -0
- package/dist/src/session/run-recovery/schema.d.ts +43 -0
- package/dist/src/session/run-recovery/schema.js +1 -0
- package/dist/src/session/types.d.ts +10 -0
- package/dist/src/upgrade/engine.d.ts +6 -5
- package/dist/src/upgrade/engine.js +2 -2
- package/dist/src/upgrade/scheduler.d.ts +14 -0
- package/dist/src/upgrade/scheduler.js +1 -1
- package/dist/src/upgrade/updater-bootstrap.d.ts +12 -0
- package/dist/src/upgrade/updater-bootstrap.js +2 -0
- package/dist/src/upgrade/updater-launcher.d.ts +28 -0
- package/dist/src/upgrade/updater-launcher.js +3 -0
- package/dist/src/upgrade/updater-protocol.d.ts +37 -0
- package/dist/src/upgrade/updater-protocol.js +1 -0
- package/dist/src/upgrade/updater-worker.d.ts +11 -0
- package/dist/src/upgrade/updater-worker.js +2 -0
- package/node_modules/@shennian/wire/dist/events.d.ts +31 -0
- package/node_modules/@shennian/wire/dist/index.d.ts +1 -0
- package/node_modules/@shennian/wire/dist/index.js +1 -0
- package/node_modules/@shennian/wire/dist/machine.d.ts +49 -1
- package/node_modules/@shennian/wire/dist/message-payload.d.ts +1 -0
- package/node_modules/@shennian/wire/dist/message-payload.js +4 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/relay-connectivity.d.ts +30 -0
- package/node_modules/@shennian/wire/dist/relay-connectivity.js +29 -0
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -2
- package/node_modules/@shennian/wire/dist/session-sync.js +2 -0
- package/node_modules/@shennian/wire/dist/session.d.ts +86 -1
- package/node_modules/@shennian/wire/dist/session.js +5 -1
- package/package.json +5 -3
- package/dist/native/macos-background-client/Shennian.app/Contents/CodeResources +0 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Info.plist +0 -17
- package/dist/native/macos-background-client/Shennian.app/Contents/MacOS/ShennianClientLauncher +0 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -2
- package/dist/native/macos-background-client/Shennian.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings +0 -2
- package/dist/native/macos-background-client/Shennian.app/Contents/_CodeSignature/CodeResources +0 -153
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawn as
|
|
2
|
-
${t.stderr??""}`.trim().match(/\d+\.\d+[\w.-]*/)?.[0]}function
|
|
3
|
-
${t.stderr??""}`.match(/^\s*CLI Version\s+(\d+\.\d+(?:\.\d+)?[\w.-]*)\s*$/im)?.[1]}export{
|
|
1
|
+
import{spawn as k,spawnSync as b}from"node:child_process";import p from"node:fs";import j from"node:crypto";import F from"node:os";import s from"node:path";const N={claude:[{command:"claude",args:[],display:"claude"}],codex:[{command:"codex",args:[],display:"codex"}],workbuddy:[{command:"codebuddy",args:[],display:"WorkBuddy"}],gemini:[{command:"gemini",args:[],display:"gemini"}],cursor:[{command:"agent",args:[],display:"agent"},{command:"cursor",args:["agent"],display:"cursor agent"}],openclaw:[],opencode:[{command:"opencode",args:[],display:"opencode"}],pi:[{command:"shennian",args:[],display:"shennian"}],"pi-coding-agent":[{command:"pi",args:[],display:"pi"}],manager:[{command:"shennian",args:["manager"],display:"shennian manager"}]},m=new Map,f=new Map,h=new Map,u=new Map;function X(){m.clear(),f.clear(),h.clear(),u.clear()}function c(){return process.platform}function R(n){return n.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)}function C(n){if(s.basename(n)!==n)return[n];const e=F.homedir();if(c()==="win32"){const o=s.win32,r=s.extname(n)?[n]:[n,`${n}.cmd`,`${n}.exe`,`${n}.bat`],a=process.env.APPDATA||o.join(e,"AppData","Roaming"),l=process.env.LOCALAPPDATA||o.join(e,"AppData","Local"),v=[o.join(e,".shennian","node"),o.join("C:\\","nvm4w","nodejs"),o.join(l,"npm-global"),o.join(a,"npm"),o.join(l,"pnpm"),o.join(e,"scoop","shims"),o.join("C:\\","Program Files","nodejs")].flatMap(w=>r.map(g=>o.join(w,g)));if(n==="codebuddy"){const w=[o.join(l,"Programs","WorkBuddy"),o.join(l,"WorkBuddy"),o.join("C:\\","Program Files","WorkBuddy")];for(const g of w)for(const E of["codebuddy.exe","codebuddy.cmd","codebuddy"])v.push(o.join(g,"resources","app.asar.unpacked","cli","bin",E))}return v}const i=[s.join(e,".npm-global","bin"),s.join(e,".local","bin"),s.join(e,".bun","bin"),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"].map(o=>s.join(o,n));return n==="claude"&&c()==="darwin"&&i.push(...V(e)),n==="codebuddy"&&c()==="darwin"&&i.push("/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy",s.join(e,"Applications","WorkBuddy.app","Contents","Resources","app.asar.unpacked","cli","bin","codebuddy")),i}function V(n){const e=[s.join(n,"Library","Application Support","Claude","claude-code")],t=[];for(const i of e){let o;try{o=p.readdirSync(i,{withFileTypes:!0}).filter(r=>r.isDirectory()).map(r=>r.name).sort((r,a)=>a.localeCompare(r,void 0,{numeric:!0}))}catch{continue}for(const r of o)t.push(s.join(i,r,"claude.app","Contents","MacOS","claude"))}return t}function P(n,e){const t=n?.split(s.delimiter).filter(Boolean)??[];if(c()==="win32"){if(e&&s.basename(e)!==e){const i=s.win32.dirname(e);t.includes(i)||t.unshift(i)}for(const i of C("shennian")){const o=s.win32.dirname(i);t.includes(o)||t.push(o)}return t.join(s.delimiter)}if(e&&s.basename(e)!==e){const i=s.dirname(e);t.includes(i)||t.unshift(i)}for(const i of C("shennian")){const o=s.dirname(i);t.includes(o)||t.push(o)}return t.join(s.delimiter)}function d(n){if(h.has(n))return h.get(n)??[];const e=c()==="win32",i=b(e?"where":"which",[n],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:3e3,windowsHide:!0,env:{...process.env,PATH:e?process.env.PATH:P(process.env.PATH,n)}}),r=[...i.status===0?R(i.stdout??""):[]];for(const a of C(n))p.existsSync(a)&&!r.includes(a)&&r.push(a);return h.set(n,r),r}function A(){return process.env.ComSpec||"cmd.exe"}function $(n){const e=s.extname(n).toLowerCase();return e===".exe"||e===".com"}function x(n){const e=s.extname(n).toLowerCase();return e===".cmd"||e===".bat"}function S(n){const e=s.extname(n).toLowerCase();return e===".js"||e===".mjs"||e===".cjs"}function W(){return d("node.exe")[0]??d("node")[0]??"node"}function B(n){return`"${n.replace(/"/g,'""')}"`}function y(n){return`'${n.replace(/'/g,"'\\''")}'`}function z(n){if(!n)return;const e=n.replace(/\//g,"\\"),t=e.match(/^([A-Za-z]):\\(.*)$/);if(t){const[,i,o]=t,r=o.replace(/\\/g,"/");return`/mnt/${i.toLowerCase()}/${r}`}if(e.startsWith("\\\\wsl$\\"))return`/${e.replace(/^\\\\wsl\$\\[^\\]+\\?/,"").replace(/\\/g,"/")}`;if(n.startsWith("/"))return n}function H(n){const e=n.trim();if(!e)throw new Error("Command string is empty");if(/[\\/]/.test(e)||/^[A-Za-z]:/.test(e))return x(e)?{kind:"cmd-shim",path:e}:{kind:"direct",path:e};const i=d(e),o=i.find($);if(o)return{kind:"direct",path:o};const r=i.find(x);return r?{kind:"cmd-shim",path:r}:{kind:"direct",path:e}}function L(n){if(u.has(n))return u.get(n)??!1;const e=d("wsl.exe")[0]??d("wsl")[0];if(!e)return u.set(n,!1),!1;const i=b(e,["-e","sh","-lc",`command -v ${y(n)} >/dev/null 2>&1`],{stdio:"ignore",timeout:5e3,windowsHide:!0}).status===0;return u.set(n,i),i}function Y(n){if(m.has(n))return m.get(n)??!1;const e=d(n).length>0||c()==="win32"&&L(n);return m.set(n,e),e}function nn(n){const e=d(n)[0];if(!e)return null;try{const t=p.realpathSync(e),i=p.statSync(t,{bigint:!0}),o=j.createHash("sha256").update(t).digest("hex"),r=j.createHash("sha256").update(`${i.dev}:${i.ino}:${i.size}:${i.mtimeNs}`).digest("hex");return{pathHash:o,fingerprint:r}}catch{return null}}function I(n){const e=d(n.command),t=e.find($);if(t)return{...n,kind:"native",path:t};const i=e.find(x);if(i)return{...n,kind:"cmd-shim",path:i};if(L(n.command)){const o=d("wsl.exe")[0]??d("wsl")[0];if(o)return{...n,kind:"wsl-bridge",path:o}}return null}function en(n){if(f.has(n))return f.get(n)??null;const e=N[n].map(t=>{if(c()==="win32")return I(t);const i=d(t.command)[0];return i?{...t,kind:"native",path:i}:null}).find(Boolean)??null;return f.set(n,e),e}function D(n,e,t){if(n.kind==="native")return c()==="win32"&&S(n.path)?{command:W(),args:[n.path,...n.args,...e],cwd:t}:{command:n.path,args:[...n.args,...e],cwd:t};if(n.kind==="cmd-shim"){const r=[n.path,...n.args,...e].map(B).join(" ");return{command:A(),args:["/d","/s","/c",`"${r}"`],cwd:t}}const i=t?z(t):void 0;if(t&&!i)throw new Error(`Cannot map Windows path to WSL workDir: ${t}`);const o=[i?`cd ${y(i)}`:"",`exec ${[n.command,...n.args,...e].map(y).join(" ")}`].filter(Boolean).join(" && ");return{command:n.path,args:["-e","sh","-lc",o],cwd:void 0}}function O(n,e,t={}){const i=D(n,e,t.cwd);return k(i.command,i.args,{...t,cwd:i.cwd,windowsHide:t.windowsHide??!0,...c()==="win32"&&n.kind==="cmd-shim"?{windowsVerbatimArguments:!0}:{}})}function tn(n,e,t={}){return O(n,e,t)}function T(n,e,t={}){const i=D(n,e,t.cwd);return b(i.command,i.args,{...t,cwd:i.cwd,windowsHide:t.windowsHide??!0,...c()==="win32"&&n.kind==="cmd-shim"?{windowsVerbatimArguments:!0}:{}})}function M(n){const e=[];let t="",i=!1,o=!1;for(const r of n){if(r==="'"&&!o){i=!i;continue}if(r==='"'&&!i){o=!o;continue}if(r===" "&&!i&&!o){t&&(e.push(t),t="");continue}t+=r}return t&&e.push(t),e}function q(n,e,t){const i=M(n),o=i[0];if(!o)throw new Error("Command string is empty");const r=i.slice(1);if(c()!=="win32")return{command:o,args:[...r,...e],cwd:t};const a=H(o);return a.kind==="cmd-shim"?{command:A(),args:["/d","/s","/c",`"${[a.path,...r,...e].map(B).join(" ")}"`],cwd:t}:S(a.path)?{command:W(),args:[a.path,...r,...e],cwd:t}:{command:a.path,args:[...r,...e],cwd:t}}function on(n,e,t={}){const i=q(n,e,t.cwd),o=M(n),r=c()==="win32"&&o[0]?H(o[0]):null;return k(i.command,i.args,{...t,cwd:i.cwd,windowsHide:t.windowsHide??!0,env:{...t.env,PATH:P(t.env?.PATH??process.env.PATH,o[0])},...c()==="win32"&&r?.kind==="cmd-shim"?{windowsVerbatimArguments:!0}:{}})}function U(n,e="--version"){const t=T(n,[e],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:5e3});return t.status!==0?void 0:`${t.stdout??""}
|
|
2
|
+
${t.stderr??""}`.trim().match(/\d+\.\d+[\w.-]*/)?.[0]}function rn(n,e){if(n!=="cursor")return U(e);const t=T(e,["about"],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:5e3});return t.status!==0?void 0:`${t.stdout??""}
|
|
3
|
+
${t.stderr??""}`.match(/^\s*CLI Version\s+(\d+\.\d+(?:\.\d+)?[\w.-]*)\s*$/im)?.[1]}export{q as buildCommandStringLaunchSpec,D as buildLaunchSpec,Y as commandExists,rn as getBuiltinAgentVersion,U as getCommandVersion,c as getProcessPlatform,M as parseCommandString,X as resetCommandSpecCache,en as resolveBuiltinCommand,nn as resolveCommandFileIdentity,tn as spawnAgentCommand,on as spawnCommandString,O as spawnResolvedCommand,T as spawnResolvedCommandSync,z as windowsPathToWsl};
|
|
@@ -3,6 +3,12 @@ import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian
|
|
|
3
3
|
import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
4
4
|
export declare class CursorAdapter extends AgentAdapter {
|
|
5
5
|
readonly type: "cursor";
|
|
6
|
+
get recoveryCapabilities(): {
|
|
7
|
+
inspectTerminal: boolean;
|
|
8
|
+
reattachRun: boolean;
|
|
9
|
+
resumeRun: boolean;
|
|
10
|
+
resumeSession: boolean;
|
|
11
|
+
};
|
|
6
12
|
private process;
|
|
7
13
|
private chatId;
|
|
8
14
|
private workDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as d}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as c,registerAgent as h}from"./adapter.js";import{resolveBuiltinCommand as u,spawnResolvedCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as g,buildManagedAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as p}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as v}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as E}from"../security/managed-agent-runtime.js";import{compileCursorManagedPermissions as w,createCursorManagedConfigDirectory as y,CURSOR_MANAGED_RUNTIME_SECURITY as C,disposeCursorManagedConfigDirectory as k}from"./cursor-managed-permissions.js";E("cursor",C);class x extends c{type="cursor";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;chatId=null;workDir=null;seq=0;runId="";emittedText="";externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(t,s,e){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.seq=0,e&&(this.chatId=e)}async send(t,s,e,n,o,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=l(),this.resetRunState();const a=["-p",`${p(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o)}${t}`,"--output-format","stream-json","--stream-partial-output"];s&&a.push("--model",s),this.chatId&&a.push("--continue"),await this.spawnAndParse(a)}async resume(t){await this.killProcess(),this.chatId=t,this.runId=l(),this.resetRunState(),await this.spawnAndParse(["--resume",t,"--output-format","stream-json","--stream-partial-output"])}async stop(){await this.killProcess()}async spawnAndParse(t){const s=u("cursor");if(!s){this.emit("error",new Error('Cursor Agent CLI not found. Expected "agent" or "cursor agent".'));return}const e=await this.prepareManagedLaunch();let n;try{n=m(s,[...t,...e.args],{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:e.env})}catch(i){throw await this.disposeManagedConfig(e.configDir),i}this.process=n,this.managedConfigDir=e.configDir,d({input:n.stdout}).on("line",i=>{if(i.trim())try{const a=JSON.parse(i);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:i})}});let r="";n.stderr?.on("data",i=>{r+=i.toString()}),n.on("close",i=>{if(this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i!==0&&i!==null)){const a=r.trim()||`cursor agent exited with code ${i}`;this.emitEvent({state:"error",message:a})}}),n.on("error",i=>{this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i.code==="ENOENT"?this.emit("error",new Error('Command "cursor" not found. Is Cursor CLI installed?')):this.emit("error",i))})}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{args:["--trust"],env:g(this.extraEnv),configDir:null};const s=w(v(t),t.adapterPolicy.workDir),e=await y(s),n=f({agentType:"cursor",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return n.CURSOR_CONFIG_DIR=e,{args:s.args,env:n,configDir:e}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await k(t).catch(()=>{}))}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;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}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)}}h("cursor",()=>new x);export{x as CursorAdapter};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare function hasCustomResumeConformance(agentType: string, fingerprint: string | undefined, runtimeVersion: string | undefined): boolean;
|
|
2
|
+
export declare function recordCustomResumeConformance(agentType: string, fingerprint: string, runtimeVersion: string | undefined): void;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import n from"node:fs";import i from"node:path";import{randomUUID as u}from"node:crypto";import{resolveShennianPath as l}from"../config/index.js";function m(){return l("runtime","recovery-conformance","custom.json")}function f(){try{const e=JSON.parse(n.readFileSync(m(),"utf8"));return e?.schemaVersion===1&&e.agents&&typeof e.agents=="object"?e:{schemaVersion:1,agents:{}}}catch{return{schemaVersion:1,agents:{}}}}function d(e,t,c){if(!t)return!1;const r=f().agents[e];return!!(r&&r.fingerprint===t&&r.runtimeVersion===(c??null))}function g(e,t,c){const r=m();n.mkdirSync(i.dirname(r),{recursive:!0,mode:448}),process.platform!=="win32"&&n.chmodSync(i.dirname(r),448);const s=f();s.agents[e]={fingerprint:t,runtimeVersion:c??null,verifiedAt:new Date().toISOString()};const o=`${r}.tmp-${process.pid}-${u()}`;try{n.writeFileSync(o,`${JSON.stringify(s,null,2)}
|
|
2
|
+
`,{mode:384,flag:"wx"});const a=n.openSync(o,"r");try{n.fsyncSync(a)}finally{n.closeSync(a)}n.renameSync(o,r)}finally{try{n.unlinkSync(o)}catch{}}}export{d as hasCustomResumeConformance,g as recordCustomResumeConformance};
|
|
@@ -4,6 +4,12 @@ import type { CustomAgentEntry } from '../config/index.js';
|
|
|
4
4
|
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
5
5
|
export declare class CustomAgentAdapter extends AgentAdapter {
|
|
6
6
|
readonly type: CustomAgentType;
|
|
7
|
+
get recoveryCapabilities(): {
|
|
8
|
+
inspectTerminal: boolean;
|
|
9
|
+
reattachRun: boolean;
|
|
10
|
+
resumeRun: boolean;
|
|
11
|
+
resumeSession: boolean;
|
|
12
|
+
};
|
|
7
13
|
private command;
|
|
8
14
|
private mode;
|
|
9
15
|
private proactive;
|
|
@@ -19,6 +25,9 @@ export declare class CustomAgentAdapter extends AgentAdapter {
|
|
|
19
25
|
private managedAgentPolicy;
|
|
20
26
|
private managedConfigDir;
|
|
21
27
|
private configurationRestartRequired;
|
|
28
|
+
private readonly executableFingerprint;
|
|
29
|
+
private resumeConformant;
|
|
30
|
+
private expectedResumedSessionId;
|
|
22
31
|
private stoppingStdioProcess;
|
|
23
32
|
private stdioProcess;
|
|
24
33
|
private spawnProcess;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createInterface as
|
|
2
|
-
`)}}async stop(){this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess()}async runSpawn(t,
|
|
3
|
-
`),await i.promise}async stopStdioProcess(){const t=this.stdioProcess;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.stoppingStdioProcess=!0;const
|
|
4
|
-
`),await
|
|
1
|
+
import{createInterface as l}from"node:readline";import{createHash as u,randomUUID as o}from"node:crypto";import{AgentAdapter as p,registerAgent as m}from"./adapter.js";import{spawnCommandString as h}from"./command-spec.js";import{buildAgentProcessEnv as g,buildManagedAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as w}from"./platform-instructions.js";import{collectPolicyEnforcementGaps as P}from"../security/managed-agent-runtime.js";import{assessCustomRuntimeSecurity as y,createCustomManagedPolicyFile as v,CUSTOM_RUNTIME_POLICY_ENV as S,disposeCustomManagedPolicyFile as C,registerCustomRuntimeSecurity as D,resolveCustomRuntimeExecutableFingerprint as d}from"./custom-managed-runtime.js";import{hasCustomResumeConformance as E,recordCustomResumeConformance as x}from"./custom-recovery-conformance.js";const I=3e3;class b extends p{type;get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:this.entry.caps.resume===!0&&this.resumeConformant}}command;mode;proactive;entry;sessionId=null;agentSessionId=null;workDir=null;runId="";seq=0;externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configurationRestartRequired=!1;executableFingerprint;resumeConformant;expectedResumedSessionId=null;stoppingStdioProcess=!1;stdioProcess=null;spawnProcess=null;dispatchGuard=null;dispatchObservedEvent=!1;constructor(t,e){super(),this.type=`custom:${t}`,this.entry=e,this.command=e.command,this.mode=e.caps.mode,this.proactive=e.caps.proactive??!1,this.executableFingerprint=d(e),this.resumeConformant=E(this.type,this.executableFingerprint,e.caps.version)}async start(t,e,i){this.sessionId=t,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??e,this.seq=0,i&&(this.agentSessionId=i,this.expectedResumedSessionId=i),this.mode==="stdio"&&(await this.startStdioProcess(),i&&await this.resume(i))}configure(t){const e=this.configurationFingerprint();this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir),e!==this.configurationFingerprint()&&(this.stdioProcess||this.spawnProcess)&&(this.configurationRestartRequired=!0)}async send(t,e,i,n,s,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");this.configurationRestartRequired&&(this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess(),this.configurationRestartRequired=!1),this.runId=o(),this.seq=0;const a=`${w(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,s)}${t}`;this.mode==="stdio"?await this.sendStdio(a,e):await this.runSpawn(a,e)}async resume(t){if(this.mode==="stdio"){const e=JSON.stringify({method:"resume",id:o(),params:{sessionId:this.sessionId,agentSessionId:t}});this.stdioProcess?.stdin?.write(e+`
|
|
2
|
+
`)}}async stop(){this.mode==="stdio"?await this.stopStdioProcess():await this.killSpawnProcess()}async runSpawn(t,e){await this.killSpawnProcess();const i=this.startDispatchGuard();this.dispatchObservedEvent=!1;const n=["/run","--workdir",this.workDir??process.cwd()];this.sessionId&&n.push("--session",this.sessionId),this.agentSessionId&&n.push("--resume",this.agentSessionId),e&&n.push("--model",e);const s=await this.prepareManagedLaunch();let r;try{r=h(this.command,n,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:s.env})}catch(a){throw await this.disposeManagedConfig(s.configDir),a}this.spawnProcess=r,this.managedConfigDir=s.configDir,this.attachOutputHandlers(r,()=>{this.spawnProcess===r&&(this.spawnProcess=null),this.disposeManagedConfig(s.configDir)}),r.stdin?.once("error",a=>this.rejectDispatch(a)),r.stdin?.write(t),r.stdin?.end(),await i.promise}async killSpawnProcess(){const t=this.spawnProcess;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.spawnProcess=null,t.kill("SIGTERM"),await new Promise(e=>{t.on("close",e),setTimeout(()=>{t.kill("SIGKILL"),e()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}async startStdioProcess(){const t=await this.prepareManagedLaunch();let e;try{e=h(this.command,["/start","--workdir",this.workDir??process.cwd()],{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:t.env})}catch(i){throw await this.disposeManagedConfig(t.configDir),i}this.stdioProcess=e,this.managedConfigDir=t.configDir,this.attachOutputHandlers(e,()=>{this.stdioProcess===e&&(this.stdioProcess=null,this.disposeManagedConfig(t.configDir),this.stoppingStdioProcess||this.emit("error",new Error("Custom agent process exited unexpectedly")))})}async sendStdio(t,e){this.stdioProcess||await this.startStdioProcess();const i=this.startDispatchGuard();this.dispatchObservedEvent=!1;const n=JSON.stringify({method:"send",id:o(),params:{sessionId:this.sessionId,text:t,modelId:e}});this.stdioProcess?.stdin?.once("error",s=>this.rejectDispatch(s)),this.stdioProcess?.stdin?.write(n+`
|
|
3
|
+
`),await i.promise}async stopStdioProcess(){const t=this.stdioProcess;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.stoppingStdioProcess=!0;const e=new Promise(n=>{const s=setTimeout(()=>{t.kill("SIGTERM"),setTimeout(()=>{t.kill("SIGKILL"),n()},3e3)},5e3);t.on("close",()=>{clearTimeout(s),n()})}),i=JSON.stringify({method:"stop",id:o()});t.stdin?.write(i+`
|
|
4
|
+
`),await e,this.stdioProcess=null,await this.disposeManagedConfig(this.managedConfigDir),this.stoppingStdioProcess=!1}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{env:g(this.extraEnv),configDir:null};const e=d(this.entry);if(!t.enforcement.executableFingerprint||e!==t.enforcement.executableFingerprint)throw new Error("custom_managed_runtime_executable_changed");if(t.enforcement.level!=="unverified")throw new Error("custom_managed_runtime_protection_must_be_unverified");const i=y(this.entry);if(i.state==="declared"&&t.enforcement.runtimeVersion!==this.entry.caps.version)throw new Error("custom_managed_runtime_version_changed");if(i.state==="declared"){const r=i.declaration.platforms[t.enforcement.platform],a=P(t.requestedPolicy,r??null);if(a.unavailableCapabilities.length>0)throw new Error(`custom_managed_runtime_capability_unavailable:${a.unavailableCapabilities.join(",")}`)}const n=f({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});if(i.state==="unverified")return{env:n,configDir:null};const s=await v(t);return n[S]=s.policyPath,{env:n,configDir:s.directory}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await C(t).catch(()=>{}))}configurationFingerprint(){const t=Object.keys(this.extraEnv).sort().map(e=>[e,this.extraEnv[e]??null]);return u("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:t,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}attachOutputHandlers(t,e){l({input:t.stdout}).on("line",s=>{if(s.trim())try{const r=JSON.parse(s);this.handleProtocolEvent(r)}catch{}});let n="";t.stderr?.on("data",s=>{n+=s.toString()}),t.on("close",s=>{if(e(),s!==0&&s!==null){const r=n.trim()||`Agent exited with code ${s}`;this.rejectDispatch(new Error(r)),this.emitEvent({state:"error",message:r})}else this.dispatchObservedEvent?this.acceptDispatch():this.rejectDispatch(new Error("Custom agent exited without protocol events"))}),t.on("error",s=>{e(),this.rejectDispatch(s),this.emit("error",s)})}handleProtocolEvent(t){const{state:e}=t;if(e)switch(this.dispatchObservedEvent=!0,e){case"delta":this.acceptDispatch(),this.emitEvent({state:"delta",text:t.text??"",thinking:t.thinking});break;case"final":this.acceptDispatch(),this.expectedResumedSessionId&&t.agentSessionId===this.expectedResumedSessionId&&this.executableFingerprint&&(x(this.type,this.executableFingerprint,this.entry.caps.version),this.resumeConformant=!0,this.expectedResumedSessionId=null),t.agentSessionId&&(this.agentSessionId=t.agentSessionId),this.emitEvent({state:"final",usage:t.usage,agentSessionId:this.agentSessionId??void 0});break;case"error":this.rejectDispatch(new Error(t.message??"unknown error")),this.emitEvent({state:"error",message:t.message??"unknown error"});break;case"tool-call":this.acceptDispatch(),this.emitEvent({state:"tool-call",name:t.name??"",args:t.args});break;case"tool-result":this.acceptDispatch(),this.emitEvent({state:"tool-result",name:t.name??"",result:t.result??""});break;case"notify":this.acceptDispatch(),this.emitEvent({state:"notify",text:t.text??"",title:t.title,source:t.source});break}}startDispatchGuard(){this.acceptDispatch();let t;const e=new Promise((i,n)=>{t={settled:!1,timer:setTimeout(()=>this.acceptDispatch(t),I),resolve:i,reject:n,promise:Promise.resolve()}});return t.promise=e,this.dispatchGuard=t,t}acceptDispatch(t=this.dispatchGuard){!t||t.settled||(t.settled=!0,clearTimeout(t.timer),this.dispatchGuard===t&&(this.dispatchGuard=null),t.resolve())}rejectDispatch(t,e=this.dispatchGuard){!e||e.settled||(e.settled=!0,clearTimeout(e.timer),this.dispatchGuard===e&&(this.dispatchGuard=null),e.reject(t))}emitEvent(t){const e={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",e)}}function L(c,t){const e=`custom:${c}`;D(e,t),m(e,()=>new b(c,t))}export{b as CustomAgentAdapter,L as registerCustomAgent};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentType, type ModelInfo } from '@shennian/wire';
|
|
1
|
+
import { type AgentType, type ModelInfo, type AgentIntegration, type AgentDisplayMetadata, type AcpCapabilitySummary, type AgentAuthState, type AcpAuthMethodSummary } from '@shennian/wire';
|
|
2
2
|
export type DetectedAgent = {
|
|
3
3
|
type: AgentType;
|
|
4
4
|
command: string;
|
|
@@ -8,6 +8,15 @@ export type DetectedAgent = {
|
|
|
8
8
|
runtimeIsWsl?: boolean;
|
|
9
9
|
executableFingerprint?: string;
|
|
10
10
|
models?: ModelInfo[];
|
|
11
|
+
integration?: AgentIntegration;
|
|
12
|
+
display?: AgentDisplayMetadata;
|
|
13
|
+
acpCapabilities?: AcpCapabilitySummary;
|
|
14
|
+
authState?: AgentAuthState;
|
|
15
|
+
acpAuthMethods?: AcpAuthMethodSummary[];
|
|
16
|
+
catalogRevision?: string;
|
|
11
17
|
};
|
|
12
18
|
export declare function isPiCodingAgentRuntimeCompatible(piVersion: string | undefined): boolean;
|
|
13
|
-
export declare function detectAgents(
|
|
19
|
+
export declare function detectAgents(options?: {
|
|
20
|
+
serverUrl?: string;
|
|
21
|
+
authToken?: string;
|
|
22
|
+
}): Promise<DetectedAgent[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AVAILABLE_BUILTIN_AGENT_TYPES as
|
|
1
|
+
import{AVAILABLE_BUILTIN_AGENT_TYPES as m}from"@shennian/wire";import{loadConfig as c}from"../config/index.js";import{getBuiltinAgentVersion as l,resolveBuiltinCommand as u}from"./command-spec.js";import{registerCustomRuntimeSecurity as a,resolveCustomRuntimeExecutableFingerprint as f}from"./custom-managed-runtime.js";import{resolveManagedAgentExecutableIdentity as p}from"../security/managed-agent-executable.js";import{discoverAcpAgents as d}from"./acp/discovery.js";const g=m.filter(t=>t!=="pi");function A(t,o){const r=t?.match(/(?:^|v)(\d+)\.(\d+)\.(\d+)/);if(!r)return!1;const n=r.slice(1,4).map(Number);for(let e=0;e<o.length;e+=1)if(n[e]!==o[e])return n[e]>o[e];return!0}function v(t){return A(t,[0,85,0])}function x(t){const o=t.models?.length?t.models:t.model?[t.model]:[],r=t.defaultModel??t.model;return o.map(n=>({id:n,name:n,isDefault:n===r}))}async function T(t={}){const o=[];for(const n of g){const e=u(n);if(!e)continue;const s=l(n,e);if(n==="pi-coding-agent"&&!v(s))continue;const i=p(e);o.push({type:n,command:e.command,args:e.args,version:s,runtimePlatform:i.platform,runtimeIsWsl:i.isWsl,executableFingerprint:i.executableFingerprint})}const r=c();for(const[n,e]of Object.entries(r.customAgents??{})){const s=`custom:${n}`;a(s,e),o.push({type:s,command:e.command,args:[],version:e.caps.version,runtimePlatform:process.platform,runtimeIsWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:f(e),models:x(e.caps)})}return o.push(...await d(t)),o}export{T as detectAgents,v as isPiCodingAgentRuntimeCompatible};
|
|
@@ -2,6 +2,12 @@ import { AgentAdapter } from './adapter.js';
|
|
|
2
2
|
import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
3
|
export declare class GeminiAdapter extends AgentAdapter {
|
|
4
4
|
readonly type: "gemini";
|
|
5
|
+
get recoveryCapabilities(): {
|
|
6
|
+
inspectTerminal: boolean;
|
|
7
|
+
reattachRun: boolean;
|
|
8
|
+
resumeRun: boolean;
|
|
9
|
+
resumeSession: boolean;
|
|
10
|
+
};
|
|
5
11
|
private process;
|
|
6
12
|
private agentSessionId;
|
|
7
13
|
private workDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as m}from"node:readline";import{randomUUID as u}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 k(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 I extends d{type="gemini";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}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,i,l,a){await this.killProcess(),this.runId=u(),this.seq=0;const o=["-p",`${g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a)}${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=u(),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 o=JSON.parse(s);this.handleStreamEvent(o)}catch{this.emitEvent({state:"delta",text:s})}});let a="";i.stderr?.on("data",s=>{a+=s.toString()}),i.on("close",s=>{if(this.process===i&&(this.process=null,s!==0&&s!==null)){const o=a.trim()||`gemini exited with code ${s}`;this.emitEvent({state:"error",message:o})}}),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:k(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 I);export{I as GeminiAdapter};
|
|
@@ -9,6 +9,7 @@ export declare class ManagerModeAdapter extends AgentAdapter {
|
|
|
9
9
|
private readonly inner;
|
|
10
10
|
private readonly substrate;
|
|
11
11
|
readonly type: import("@shennian/wire").AgentType;
|
|
12
|
+
get recoveryCapabilities(): import("./adapter.js").AgentRecoveryCapabilities;
|
|
12
13
|
private sessionId;
|
|
13
14
|
private workDir;
|
|
14
15
|
private agentSessionId;
|
|
@@ -38,6 +39,7 @@ export declare class ManagerModeAdapter extends AgentAdapter {
|
|
|
38
39
|
export declare function combineManagerInstructions(managerInstructions: string, systemPrompt?: string | null): string;
|
|
39
40
|
export declare class ManagerAdapter extends AgentAdapter {
|
|
40
41
|
readonly type: "manager";
|
|
42
|
+
get recoveryCapabilities(): import("./adapter.js").AgentRecoveryCapabilities;
|
|
41
43
|
private inner;
|
|
42
44
|
private sessionId;
|
|
43
45
|
private workDir;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import{AgentAdapter as
|
|
1
|
+
import{AgentAdapter as g,registerAgent as I}from"./adapter.js";import{CodexAdapter as p}from"./codex.js";import{ClaudeAdapter as f}from"./claude.js";import{MANAGER_SYSTEM_PROMPT as y,buildManagerPrompt as l}from"../manager/prompt.js";import{getManagerRuntimeService as a}from"../manager/runtime.js";import d from"node:fs";import c from"node:path";import{PLATFORM_OUTPUT_INSTRUCTIONS as S}from"./platform-instructions.js";function M(r){return r==="claude"?"claude":"codex"}function w(r){return r.replace(/[^a-zA-Z0-9_.-]/g,"_")}function v(r){const e=c.join(r,"AGENTS.md");try{return d.readFileSync(e,"utf8").trim()}catch{return""}}function A(r,e){return c.join(r,".shennian","runtime","manager",w(e),"instructions.md")}function u(r,e){const n=v(r),t=a(),s=t?.getManagerExternalChannelSystemPrompt(e)??"",i=typeof t?.getManagerWorkerDefaults=="function"?t.getManagerWorkerDefaults(e):void 0,o=i?.agentType?`\u9ED8\u8BA4 worker Agent\uFF1A${i.agentType}${i.modelId?`
|
|
2
2
|
\u9ED8\u8BA4 worker \u6A21\u578B\uFF1A${i.modelId}`:""}\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u6216\u4EFB\u52A1\u660E\u663E\u9700\u8981\u5176\u4ED6 Agent/\u6A21\u578B\uFF0C\u521B\u5EFA worker \u65F6\u4F18\u5148\u4F7F\u7528\u8FD9\u4E2A\u9ED8\u8BA4\u7EC4\u5408\u3002`:"";return`${["# Shennian Manager Instructions","This file is generated by Shennian for a Manager Agent substrate session. Do not edit it by hand.",n?`## Project Instructions
|
|
3
3
|
|
|
4
4
|
${n}`:"",`## Manager Instructions
|
|
5
5
|
|
|
6
|
-
${y}`,
|
|
6
|
+
${y}`,o?`## Worker Defaults
|
|
7
7
|
|
|
8
|
-
${
|
|
8
|
+
${o}`:"",s?`## External Message Channel Instructions
|
|
9
9
|
|
|
10
10
|
${s}`:"",S].filter(Boolean).join(`
|
|
11
11
|
|
|
12
12
|
`)}
|
|
13
|
-
`}function k(r,e){const n=A(r,e),t=
|
|
13
|
+
`}function k(r,e){const n=A(r,e),t=u(r,e);d.mkdirSync(c.dirname(n),{recursive:!0});try{if(d.readFileSync(n,"utf8")===t)return n}catch{}return d.writeFileSync(n,t),n}class F extends g{inner;substrate;type;get recoveryCapabilities(){return this.inner.recoveryCapabilities}sessionId="";workDir="";agentSessionId=null;configured={};constructor(e,n){super(),this.inner=e,this.substrate=n,this.type=e.type,e.on("agentEvent",t=>{t.agentSessionId&&(this.agentSessionId=t.agentSessionId),this.emit("agentEvent",{...t,source:t.source??n})}),e.on("error",t=>this.emit("error",t))}configure(e){this.configured=e,e.sessionId&&a()?.setManagedAgentPolicy(e.sessionId,e.managedAgentPolicy??null),this.applyEnvironment()}async start(e,n,t,s){this.sessionId=e,this.workDir=n,this.agentSessionId=t??null,this.applyEnvironment(),await this.inner.start(e,n,t,s),a()?.registerManager({sessionId:e,agentSessionId:this.agentSessionId,workDir:n,modelId:this.substrate,agentType:this.type,status:"idle"}),a()?.bindManagerAdapterEvents(e,this.inner)}async send(e,n,t,s,i,o){a()?.registerManager({sessionId:this.sessionId,agentSessionId:this.agentSessionId,workDir:this.workDir,modelId:this.substrate,agentType:this.type,providerModelId:n??null,status:"running"}),await a()?.ready(),this.applyEnvironment();const h=u(this.workDir,this.sessionId),m=E(h,i);return this.inner.send(l(e),n,t,s,m,o)}async resume(e){this.agentSessionId=e,await this.inner.resume(e)}async stop(){a()?.setManagedAgentPolicy(this.sessionId,null),await this.inner.stop()}getStatus(){return this.inner.getStatus?.()??Promise.resolve({active:!1})}setTitle(e,n,t){return this.inner.setTitle?.(e,n,t)??Promise.resolve()}applyEnvironment(){const e=this.sessionId?a()?.getInjectedEnv(this.sessionId,this.agentSessionId,this.workDir,this.substrate)??{}:{};this.inner.configure?.({...this.configured,sessionId:this.configured.sessionId??this.sessionId,env:{...this.configured.env??{},...e},managedAgentPolicy:this.configured.managedAgentPolicy??null})}}function E(r,e){const n=e?.trim()??"";if(!n)return r;const s=["[\u667A\u80FD\u4F53\u79C1\u6709\u7CFB\u7EDF\u63D0\u793A\u8BCD]","[\u667A\u80FD\u4F53\u8EAB\u4EFD]"].map(i=>n.indexOf(i)).filter(i=>i>=0).sort((i,o)=>i-o)[0]??-1;return n.startsWith("[\u795E\u5FF5\u53D7\u7BA1\u4F1A\u8BDD\u89C4\u5219]")&&s>=0?`${n.slice(0,s)}[\u4F1A\u8BDD\u6A21\u5F0F]
|
|
14
14
|
${r.trim()}
|
|
15
15
|
|
|
16
16
|
${n.slice(s)}`:`${r.trim()}
|
|
17
17
|
|
|
18
|
-
${n}`}class
|
|
18
|
+
${n}`}class b extends g{type="manager";get recoveryCapabilities(){return this.inner?.recoveryCapabilities??{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}inner=null;sessionId="";workDir="";agentSessionId=null;modelId="codex";async start(e,n,t){this.sessionId=e,this.workDir=n,this.agentSessionId=t??null,a()?.registerManager({sessionId:e,agentSessionId:this.agentSessionId,workDir:n,modelId:this.modelId,agentType:"manager",status:"idle"})}async send(e,n){const t=M(n);if(!this.inner||t!==this.modelId){await this.inner?.stop().catch(()=>{}),this.inner?.removeAllListeners(),this.modelId=t;const s=k(this.workDir,this.sessionId),i=u(this.workDir,this.sessionId);this.inner=t==="claude"?new f({systemPrompt:i,hidden:!0}):new p({modelInstructionsFile:s,hidden:!0}),this.bindInner(this.inner),await this.inner.start(this.sessionId,this.workDir,this.agentSessionId),a()?.bindManagerAdapterEvents(this.sessionId,this.inner)}a()?.registerManager({sessionId:this.sessionId,agentSessionId:this.agentSessionId,workDir:this.workDir,modelId:this.modelId,agentType:"manager",status:"running"}),await a()?.ready(),await this.withManagerEnv(()=>this.inner.send(l(e)))}async resume(e){this.agentSessionId=e,this.inner&&await this.inner.resume(e)}async stop(){await this.inner?.stop()}bindInner(e){e.on("agentEvent",n=>{n.agentSessionId&&(this.agentSessionId=n.agentSessionId),this.emit("agentEvent",this.wrapEvent(n))}),e.on("error",n=>this.emit("error",n))}wrapEvent(e){return{...e,source:e.source??this.modelId,agentSessionId:e.agentSessionId??this.agentSessionId??void 0}}async withManagerEnv(e){const n=a()?.getInjectedEnv(this.sessionId,this.agentSessionId,this.workDir,this.modelId)??{},t=new Map;for(const[s,i]of Object.entries(n))t.set(s,process.env[s]),process.env[s]=i;try{return await e()}finally{for(const[s,i]of t)i===void 0?delete process.env[s]:process.env[s]=i}}}I("manager",()=>new b);export{b as ManagerAdapter,F as ManagerModeAdapter,E as combineManagerInstructions};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAgentConfigSummary as
|
|
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 S={claude:"claude-alias-v2"};function M(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 e=i.type!=="pi"&&i.type!=="openclaw"&&!i.type.startsWith("acp:")?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",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 O(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 F(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],p=S[t.type];let r;if(v(a,t.version,p))r=a.models;else{let c=null;try{c=M(await A(t,s)),r=c.length>0?c:a?.models??t.models??[]}catch{r=a?.models??t.models??[]}if(c&&c.length>0){const h={version:t.version,...p?{sourceRevision:p}:{},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{O as getCachedAgentInfos,F as refreshAgentInfos,R as resolveAgentInfos};
|
|
@@ -3,6 +3,12 @@ import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian
|
|
|
3
3
|
import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
4
4
|
export declare class OpenCodeAdapter extends AgentAdapter {
|
|
5
5
|
readonly type: "opencode";
|
|
6
|
+
get recoveryCapabilities(): {
|
|
7
|
+
inspectTerminal: boolean;
|
|
8
|
+
reattachRun: boolean;
|
|
9
|
+
resumeRun: boolean;
|
|
10
|
+
resumeSession: boolean;
|
|
11
|
+
};
|
|
6
12
|
private process;
|
|
7
13
|
private agentSessionId;
|
|
8
14
|
private workDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as u}from"node:readline";import{randomUUID as l}from"node:crypto";import{AgentAdapter as p,registerAgent as f}from"./adapter.js";import{resolveBuiltinCommand as h,spawnAgentCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as E,buildManagedAgentProcessEnv as C}from"../agent-env.js";import{buildInlineChannelPreamble as y}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as I}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as D}from"../security/managed-agent-runtime.js";import{compileOpenCodeManagedPermissions as O,createOpenCodeManagedConfigDirectory as S,disposeOpenCodeManagedConfigDirectory as w,OPENCODE_MANAGED_RUNTIME_SECURITY as P}from"./opencode-managed-permissions.js";D("opencode",P);function _(i){if(!i)return;const e=i.input,t=i.output;if(!(typeof e!="number"&&typeof t!="number"))return{inputTokens:typeof e=="number"?e:0,outputTokens:typeof t=="number"?t:0}}function c(i){if(i==null)return"";if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return String(i)}}function g(i){if(!i)return"opencode request failed";if(typeof i=="string")return i;if(typeof i!="object")return String(i);const e=i;return typeof e.message=="string"?e.message:typeof e.name=="string"?e.name:"data"in e?g(e.data):c(i)||"opencode request failed"}class A extends p{type="opencode";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="closed";externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(e,t,r){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,r&&(this.agentSessionId=r),this.resetRunState()}async send(e,t,r,s,d,o){if(o?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=l(),this.resetRunState();const n=["run","--format","json","--dir",this.workDir??process.cwd()];this.agentSessionId&&n.push("--session",this.agentSessionId),t&&n.push("--model",t);const a=`${y(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,d)}${e}`;n.push(a),await this.spawnAndParse(n)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=l(),this.resetRunState(),this.emitEvent({state:"start",agentSessionId:e}),this.emitFinalIfOpen({state:"final",agentSessionId:e})}async stop(){await this.killProcess()}resetRunState(){this.seq=0,this.terminalState="open"}async spawnAndParse(e){const t=h("opencode");if(!t){this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".'));return}const r=await this.prepareManagedLaunch();let s;try{s=m(t,[e[0],...r.args,...e.slice(1)],{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:r.env})}catch(n){throw await this.disposeManagedConfig(r.configDir),n}this.process=s,this.managedConfigDir=r.configDir,u({input:s.stdout}).on("line",n=>{if(n.trim())try{const a=JSON.parse(n);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:n})}});let o="";s.stderr?.on("data",n=>{o+=n.toString()}),s.on("close",n=>{if(this.process!==s||(this.process=null,this.disposeManagedConfig(r.configDir),this.terminalState!=="open"))return;const a=o.trim();if(n!==0&&n!==null){this.emitErrorIfOpen({state:"error",message:a||`opencode exited with code ${n}`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}),s.on("error",n=>{this.process===s&&(this.process=null,this.disposeManagedConfig(r.configDir),n.code==="ENOENT"?this.emit("error",new Error('Command "opencode" not found. Install it with "npm i -g opencode-ai@latest".')):this.emit("error",n))})}async prepareManagedLaunch(){const e=this.managedAgentPolicy;if(!e)return{args:["--auto"],env:E({NO_COLOR:"1",...this.extraEnv}),configDir:null};const t=O(I(e),e.adapterPolicy.workDir),r=await S(t),s=C({agentType:"opencode",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return s.OPENCODE_CONFIG=r.configPath,s.OPENCODE_CONFIG_DIR=r.directory,s.OPENCODE_CONFIG_CONTENT=JSON.stringify(t.config),s.OPENCODE_DISABLE_PROJECT_CONFIG="true",s.OPENCODE_DISABLE_CLAUDE_CODE="1",s.OPENCODE_DISABLE_CLAUDE_CODE_PROMPT="1",s.OPENCODE_DISABLE_CLAUDE_CODE_SKILLS="1",{args:t.args,env:s,configDir:r.directory}}async disposeManagedConfig(e){e&&(this.managedConfigDir===e&&(this.managedConfigDir=null),await w(e).catch(()=>{}))}handleStreamEvent(e){e.sessionID&&e.sessionID!==this.agentSessionId&&(this.agentSessionId=e.sessionID,this.emitEvent({state:"start",agentSessionId:e.sessionID}));const t=e.part;if(e.type==="text"&&t?.text){this.emitEvent({state:"delta",text:t.text});return}if(e.type==="reasoning"&&t?.text){this.emitEvent({state:"delta",text:t.text,thinking:!0});return}if(e.type==="tool_use"&&t?.type==="tool"){t.state?.status==="completed"||t.state?.status==="error"?this.emitEvent({state:"tool-result",name:t.tool??"tool",result:t.state.status==="error"?t.state.error??"tool failed":c(t.state?.output)}):this.emitEvent({state:"tool-call",name:t.tool??"tool",args:t.state?.input??{}});return}if(e.type==="error"){this.emitErrorIfOpen({state:"error",message:g(e.error)});return}e.type==="step_finish"&&this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??e.sessionID,usage:_(t?.tokens)})}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="closed",this.emitEvent(e))}async killProcess(){const e=this.process;if(!e){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}}f("opencode",()=>new A);export{A as OpenCodeAdapter};
|
|
@@ -3,6 +3,12 @@ import { AgentAdapter, type AgentSendContext } from './adapter.js';
|
|
|
3
3
|
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
4
4
|
export declare class PiCodingAgentAdapter extends AgentAdapter {
|
|
5
5
|
readonly type: "pi-coding-agent";
|
|
6
|
+
get recoveryCapabilities(): {
|
|
7
|
+
inspectTerminal: boolean;
|
|
8
|
+
reattachRun: boolean;
|
|
9
|
+
resumeRun: boolean;
|
|
10
|
+
resumeSession: boolean;
|
|
11
|
+
};
|
|
6
12
|
private process;
|
|
7
13
|
private rpc;
|
|
8
14
|
private shennianSessionId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import c from"node:fs";import u from"node:path";import{randomUUID as w}from"node:crypto";import{AgentAdapter as _,registerAgent as E}from"./adapter.js";import{resolveBuiltinCommand as A,spawnAgentCommand as k}from"./command-spec.js";import{buildAgentProcessEnv as v,buildManagedAgentProcessEnv as x}from"../agent-env.js";import{loadConfig as T,resolveShennianPath as f}from"../config/index.js";import{buildInlineChannelPreamble as
|
|
2
|
-
[truncated]`}function G(i){return y(i,2e3).replace(/Bearer\s+[A-Za-z0-9._~-]+/gi,"Bearer [redacted]").replace(/\b(?:pimc|mrc)_[A-Za-z0-9_-]{20,}\b/g,"[redacted-capability]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g,"[redacted-key]")}function z(i){try{return y(JSON.stringify(i))}catch{return"[unserializable]"}}function P(i){if(!i||typeof i!="object")return;const
|
|
3
|
-
`)}`}function L(i,t){const s=[];for(const e of i??[]){if(e.kind!=="image"||!e.mimeType.startsWith("image/"))continue;let n=e.previewData?.trim()??"";const a=n.indexOf(",");if(n.startsWith("data:")&&a>=0&&(n=n.slice(a+1)),!n&&t&&e.path&&!/^https?:\/\//i.test(e.path))try{const l=c.statSync(e.path);l.isFile()&&l.size<=10*1024*1024&&(n=c.readFileSync(e.path).toString("base64"))}catch{n=""}n&&s.push({type:"image",data:n,mimeType:e.mimeType})}return s}class Z extends _{type="pi-coding-agent";process=null;rpc=null;shennianSessionId="";workDir="";sessionDir="";agentSessionId=null;externalChannel=null;extraEnv={};managedAgentPolicy=null;runId="";seq=0;active=!1;terminal=!0;stderr="";lastUsage;promptFile=null;modelBroker=null;persistNativeSession=!0;validationRoot=null;runPhase=null;configure(t){t.sessionId&&(this.shennianSessionId=t.sessionId),this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null)}async start(t,s,e,n){await this.stopProcess(),this.shennianSessionId=t,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.agentSessionId=e??null;const a=f("runtime","pi-coding-agent",g(t));this.persistNativeSession=n?.persistNativeSession!==!1,this.validationRoot=this.persistNativeSession?null:u.join(a,`validation-${w()}`),this.sessionDir=this.validationRoot?u.join(this.validationRoot,"sessions"):u.join(a,"sessions"),c.mkdirSync(this.sessionDir,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(this.sessionDir,448),this.active=!1,this.terminal=!0}async send(t,s,e,n,a,l){await this.ensureProcess(a);const h=this.requireRpc(),p=await this.readState(),o=!!(p.isStreaming||p.isCompacting);(!o||!this.runId)&&(this.runId=w(),this.seq=0,this.terminal=!1,this.lastUsage=void 0,this.runPhase="thinking");const d=H(s);if(d.modelId&&!o&&(p.model?.id!==d.modelId||d.provider&&p.model?.provider!==d.provider)){if(!d.provider)throw new Error("Pi model selection must include provider/modelId");await h.request("set_model",{provider:d.provider,modelId:d.modelId})}e&&!o&&await h.request("set_thinking_level",{level:e});const r=o?l?.source==="room_activation"?"follow_up":"steer":"prompt",m={message:X(t,n,this.workDir,this.externalChannel,this.shennianSessionId,null)},S=L(n,!this.managedAgentPolicy);S.length&&!this.modelBroker&&(m.images=S),this.modelBroker?.setTurnActive(!0);try{await h.request(r,m)}catch(I){throw this.modelBroker?.setTurnActive(!1),I}}async resume(t){await this.stopProcess(),this.agentSessionId=t}async stop(){const t=this.rpc;t&&(await t.request("clear_queue").catch(()=>{}),await t.request("abort").catch(()=>{})),!this.terminal&&this.runId&&this.emitTerminal({state:"aborted"}),await this.stopProcess()}async getStatus(){if(!this.rpc||!this.process)return{active:!1,runId:null,canStop:!1};const t=await this.readState().catch(()=>null),s=!!(t?.isStreaming||t?.isCompacting||this.active);return{active:s,runId:s?this.runId:null,runPhase:s?this.runPhase??"thinking":null,canStop:s}}async setTitle(t,s){this.rpc&&await this.rpc.request("set_session_name",{name:s.slice(0,200)})}async ensureProcess(t){if(this.process&&this.rpc)return;const s=A("pi-coding-agent");if(!s)throw new Error('Command "pi" not found. Install @earendil-works/pi-coding-agent from the official package.');const e=["--mode","rpc","--session-dir",this.sessionDir,"--offline"];this.agentSessionId&&e.push("--session",this.agentSessionId);const n=await this.prepareHostedModel();if(n&&e.push("--provider","shennian-dashscope","--model","deepseek-v4-flash-0731"),this.managedAgentPolicy){e.push("--no-extensions","--no-skills","--no-prompt-templates","--no-context-files");const r=this.managedTools(),m=B({outputPath:u.join(f("runtime","pi-coding-agent",g(this.shennianSessionId)),"managed-extension.ts"),policy:this.managedAgentPolicy,allowRoomControl:!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT,allowManagerControl:!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,allowHostShell:this.managedAgentPolicy.adapterPolicy.fileAccess==="host-full"});e.push("--no-builtin-tools","--extension",m),r.length?e.push("--tools",r.join(",")):e.push("--no-tools")}const a=R(this.workDir,this.externalChannel,this.shennianSessionId,t),l=f("runtime","pi-coding-agent",g(this.shennianSessionId));c.mkdirSync(l,{recursive:!0,mode:448}),this.promptFile=u.join(l,"instructions.md"),c.writeFileSync(this.promptFile,a,{encoding:"utf8",mode:384}),process.platform!=="win32"&&c.chmodSync(this.promptFile,384),e.push("--append-system-prompt",this.promptFile);const h={NO_COLOR:"1",...n?{PI_CODING_AGENT_DIR:n.configDir}:{}},p=this.managedAgentPolicy?x({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:h}):v({...h,...this.extraEnv});let o;try{o=k(s,e,{cwd:this.workDir,stdio:["pipe","pipe","pipe"],env:p})}catch(r){throw await this.stopModelBroker(),r}this.process=o,this.stderr="",o.stderr?.on("data",r=>{const m=typeof r=="string"?r:r.toString("utf8");this.stderr=y(`${this.stderr}${m}`,q)});const d=new b(o);this.rpc=d,d.on("event",r=>this.handleRpcEvent(r)),d.on("protocolError",r=>this.emitError(r.message)),o.once("close",r=>{this.process===o&&(this.process=null,this.rpc=null,this.terminal||this.emitError(r===0?"Pi RPC process closed before agent_settled":`Pi RPC process exited with code ${r??"unknown"}${this.stderr?`: ${this.stderr}`:""}`),this.stopModelBroker())}),o.once("error",r=>{this.process===o&&this.emitError(r.message)});try{const r=await this.readState();if(!r.sessionId)throw new Error("Pi RPC did not return a session ID");if(r.isStreaming||r.isCompacting)throw new Error("Pi RPC session is not idle after startup");if(this.agentSessionId&&r.sessionId!==this.agentSessionId)throw new Error("Pi RPC resumed a different session");if(!r.sessionFile||!this.ownsSessionFile(r.sessionFile))throw new Error("Pi RPC returned a session file outside the Shennian-owned directory");this.agentSessionId=r.sessionId,this.persistNativeSession&&O(r.sessionId,this.sessionDir)}catch(r){throw await this.stopProcess(),r}}managedTools(){const t=this.managedAgentPolicy?.adapterPolicy;if(!t)return[];const s=["read","grep","find","ls"];(t.fileAccess==="workspace-write"||t.fileAccess==="host-full")&&s.push("edit","write");const e=!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,n=!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT;return(e||n||t.fileAccess==="host-full")&&s.push("bash"),s}ownsSessionFile(t){const s=u.resolve(this.sessionDir),e=u.resolve(t),n=u.relative(s,e);return n!==""&&!n.startsWith("..")&&!u.isAbsolute(n)}async readState(){const t=await this.requireRpc().request("get_state");return F(t)}requireRpc(){if(!this.rpc)throw new Error("Pi RPC process is not running");return this.rpc}handleRpcEvent(t){if(t.type==="agent_start"){this.active=!0,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:this.agentSessionId??void 0});return}if(t.type==="message_update"){this.lastUsage=P(t.usage)??this.lastUsage;const s=t.assistantMessageEvent;if(!s||typeof s!="object")return;const e=s;e.type==="text_delta"&&typeof e.delta=="string"?(this.runPhase="streaming_text",this.emitEvent({state:"delta",text:e.delta})):e.type==="thinking_delta"&&typeof e.delta=="string"&&(this.runPhase="thinking",this.emitEvent({state:"delta",text:e.delta,thinking:!0}));return}if(t.type==="tool_execution_start"){this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:typeof t.toolName=="string"?t.toolName:"tool",args:t.args&&typeof t.args=="object"&&!Array.isArray(t.args)?t.args:{}});return}if(t.type==="tool_execution_end"){this.runPhase="thinking",this.emitEvent({state:"tool-result",name:typeof t.toolName=="string"?t.toolName:"tool",result:$(t.result)||z(t.result)});return}if(t.type==="tool_execution_update"){this.emitEvent({state:"notify",runPhase:"tool_running",message:"Pi tool is running"});return}if(t.type==="message_end"){const s=t.message;if(s&&typeof s=="object"){const e=s;this.lastUsage=P(e.usage)??this.lastUsage,e.stopReason==="error"&&(this.modelBroker?.setTurnActive(!1),this.emitError(typeof e.errorMessage=="string"?e.errorMessage:"Pi model request failed"))}return}if(t.type==="compaction_start"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is compacting context"});return}if(t.type==="auto_retry_start"||t.type==="summarization_retry_scheduled"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is retrying"});return}if(t.type==="extension_ui_request"){typeof t.id=="string"&&this.rpc?.notify("extension_ui_response",{id:t.id,cancelled:!0});return}if(t.type==="extension_error"){this.modelBroker?.setTurnActive(!1),this.emitError(typeof t.error=="string"?t.error:"Pi extension failed");return}t.type==="agent_settled"&&(this.active=!1,this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"final",agentSessionId:this.agentSessionId??void 0,usage:this.lastUsage}))}emitEvent(t){this.runId&&this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}emitTerminal(t){this.terminal||(this.terminal=!0,this.active=!1,this.runPhase=null,this.emitEvent(t))}emitError(t){this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"error",message:G(t)})}async prepareHostedModel(){const t=T(),s=t.machineToken?.trim();if(!s)return null;const e=new D({serverUrl:t.serverUrl?.trim()||"https://shennian.net",machineToken:s,sessionId:this.shennianSessionId}),n=await e.start(),a=f("runtime","pi-coding-agent",g(this.shennianSessionId),"agent-config");c.mkdirSync(a,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(a,448);const l=u.join(a,"models.json"),h={providers:{"shennian-dashscope":{baseUrl:n.baseUrl,api:"openai-completions",apiKey:n.apiKey,authHeader:!0,compat:{supportsDeveloperRole:!1,supportsReasoningEffort:!0},models:[{id:"deepseek-v4-flash-0731",name:"DeepSeek V4 Flash",reasoning:!0,input:["text"],thinkingLevelMap:{off:null,minimal:null,low:"low",medium:null,high:"high",xhigh:null,max:"max"}}]}}};return c.writeFileSync(l,`${JSON.stringify(h,null,2)}
|
|
4
|
-
`,{encoding:"utf8",mode:384}),process.platform!=="win32"&&c.chmodSync(l,384),this.modelBroker=
|
|
1
|
+
import c from"node:fs";import u from"node:path";import{randomUUID as w}from"node:crypto";import{AgentAdapter as _,registerAgent as E}from"./adapter.js";import{resolveBuiltinCommand as A,spawnAgentCommand as k}from"./command-spec.js";import{buildAgentProcessEnv as v,buildManagedAgentProcessEnv as x}from"../agent-env.js";import{loadConfig as T,resolveShennianPath as f}from"../config/index.js";import{buildInlineChannelPreamble as R,buildPlatformInstructions as N}from"./platform-instructions.js";import{PiRpcClient as b}from"./pi-rpc-client.js";import{registerManagedAgentRuntimeSecurity as M}from"../security/managed-agent-runtime.js";import{PI_CODING_AGENT_MANAGED_RUNTIME_SECURITY as C}from"./pi-coding-agent-managed-permissions.js";import{PiManagedModelBroker as D}from"./pi-managed-model-broker.js";import{writePiManagedExtension as B}from"./pi-managed-extension.js";import{recordPiSessionOwnership as O}from"./pi-session-ownership.js";M("pi-coding-agent",C);const q=32*1024,j=256*1024,U=3e3;function g(i){return i.replace(/[^a-zA-Z0-9_.-]/g,"_").slice(0,180)||"session"}function F(i){return i.data&&typeof i.data=="object"&&!Array.isArray(i.data)?i.data:{}}function $(i){if(typeof i=="string")return i;if(!i||typeof i!="object")return"";const e=i.content;return Array.isArray(e)?e.map(s=>{if(!s||typeof s!="object")return"";const t=s;return t.type==="text"&&typeof t.text=="string"?t.text:""}).join(""):""}function y(i,e=j){return i.length<=e?i:`${i.slice(0,e)}
|
|
2
|
+
[truncated]`}function G(i){return y(i,2e3).replace(/Bearer\s+[A-Za-z0-9._~-]+/gi,"Bearer [redacted]").replace(/\b(?:pimc|mrc)_[A-Za-z0-9_-]{20,}\b/g,"[redacted-capability]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g,"[redacted-key]")}function z(i){try{return y(JSON.stringify(i))}catch{return"[unserializable]"}}function P(i){if(!i||typeof i!="object")return;const e=i;if(!(typeof e.input!="number"&&typeof e.output!="number"))return{inputTokens:typeof e.input=="number"?e.input:0,outputTokens:typeof e.output=="number"?e.output:0}}function H(i){const e=i?.trim();if(!e)return{};const s=e.indexOf("/");return s<=0||s===e.length-1?{modelId:e}:{provider:e.slice(0,s),modelId:e.slice(s+1)}}function X(i,e,s,t,n,a){const l=R(s,t,n,a),h=e?.filter(o=>o.kind!=="image")??[];if(!h.length)return`${l}${i}`;const p=h.map(o=>`[${o.name}](<${o.path}>)`);return`${l}${[i,"",...p].join(`
|
|
3
|
+
`)}`}function L(i,e){const s=[];for(const t of i??[]){if(t.kind!=="image"||!t.mimeType.startsWith("image/"))continue;let n=t.previewData?.trim()??"";const a=n.indexOf(",");if(n.startsWith("data:")&&a>=0&&(n=n.slice(a+1)),!n&&e&&t.path&&!/^https?:\/\//i.test(t.path))try{const l=c.statSync(t.path);l.isFile()&&l.size<=10*1024*1024&&(n=c.readFileSync(t.path).toString("base64"))}catch{n=""}n&&s.push({type:"image",data:n,mimeType:t.mimeType})}return s}class Z extends _{type="pi-coding-agent";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;rpc=null;shennianSessionId="";workDir="";sessionDir="";agentSessionId=null;externalChannel=null;extraEnv={};managedAgentPolicy=null;runId="";seq=0;active=!1;terminal=!0;stderr="";lastUsage;promptFile=null;modelBroker=null;persistNativeSession=!0;validationRoot=null;runPhase=null;configure(e){e.sessionId&&(this.shennianSessionId=e.sessionId),this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null)}async start(e,s,t,n){await this.stopProcess(),this.shennianSessionId=e,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.agentSessionId=t??null;const a=f("runtime","pi-coding-agent",g(e));this.persistNativeSession=n?.persistNativeSession!==!1,this.validationRoot=this.persistNativeSession?null:u.join(a,`validation-${w()}`),this.sessionDir=this.validationRoot?u.join(this.validationRoot,"sessions"):u.join(a,"sessions"),c.mkdirSync(this.sessionDir,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(this.sessionDir,448),this.active=!1,this.terminal=!0}async send(e,s,t,n,a,l){await this.ensureProcess(a);const h=this.requireRpc(),p=await this.readState(),o=!!(p.isStreaming||p.isCompacting);(!o||!this.runId)&&(this.runId=w(),this.seq=0,this.terminal=!1,this.lastUsage=void 0,this.runPhase="thinking");const d=H(s);if(d.modelId&&!o&&(p.model?.id!==d.modelId||d.provider&&p.model?.provider!==d.provider)){if(!d.provider)throw new Error("Pi model selection must include provider/modelId");await h.request("set_model",{provider:d.provider,modelId:d.modelId})}t&&!o&&await h.request("set_thinking_level",{level:t});const r=o?l?.source==="room_activation"?"follow_up":"steer":"prompt",m={message:X(e,n,this.workDir,this.externalChannel,this.shennianSessionId,null)},S=L(n,!this.managedAgentPolicy);S.length&&!this.modelBroker&&(m.images=S),this.modelBroker?.setTurnActive(!0);try{await h.request(r,m)}catch(I){throw this.modelBroker?.setTurnActive(!1),I}}async resume(e){await this.stopProcess(),this.agentSessionId=e}async stop(){const e=this.rpc;e&&(await e.request("clear_queue").catch(()=>{}),await e.request("abort").catch(()=>{})),!this.terminal&&this.runId&&this.emitTerminal({state:"aborted"}),await this.stopProcess()}async getStatus(){if(!this.rpc||!this.process)return{active:!1,runId:null,canStop:!1};const e=await this.readState().catch(()=>null),s=!!(e?.isStreaming||e?.isCompacting||this.active);return{active:s,runId:s?this.runId:null,runPhase:s?this.runPhase??"thinking":null,canStop:s}}async setTitle(e,s){this.rpc&&await this.rpc.request("set_session_name",{name:s.slice(0,200)})}async ensureProcess(e){if(this.process&&this.rpc)return;const s=A("pi-coding-agent");if(!s)throw new Error('Command "pi" not found. Install @earendil-works/pi-coding-agent from the official package.');const t=["--mode","rpc","--session-dir",this.sessionDir,"--offline"];this.agentSessionId&&t.push("--session",this.agentSessionId);const n=await this.prepareHostedModel();if(n&&t.push("--provider","shennian-dashscope","--model","deepseek-v4-flash-0731"),this.managedAgentPolicy){t.push("--no-extensions","--no-skills","--no-prompt-templates","--no-context-files");const r=this.managedTools(),m=B({outputPath:u.join(f("runtime","pi-coding-agent",g(this.shennianSessionId)),"managed-extension.ts"),policy:this.managedAgentPolicy,allowRoomControl:!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT,allowManagerControl:!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,allowHostShell:this.managedAgentPolicy.adapterPolicy.fileAccess==="host-full"});t.push("--no-builtin-tools","--extension",m),r.length?t.push("--tools",r.join(",")):t.push("--no-tools")}const a=N(this.workDir,this.externalChannel,this.shennianSessionId,e),l=f("runtime","pi-coding-agent",g(this.shennianSessionId));c.mkdirSync(l,{recursive:!0,mode:448}),this.promptFile=u.join(l,"instructions.md"),c.writeFileSync(this.promptFile,a,{encoding:"utf8",mode:384}),process.platform!=="win32"&&c.chmodSync(this.promptFile,384),t.push("--append-system-prompt",this.promptFile);const h={NO_COLOR:"1",...n?{PI_CODING_AGENT_DIR:n.configDir}:{}},p=this.managedAgentPolicy?x({agentType:this.type,providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:h}):v({...h,...this.extraEnv});let o;try{o=k(s,t,{cwd:this.workDir,stdio:["pipe","pipe","pipe"],env:p})}catch(r){throw await this.stopModelBroker(),r}this.process=o,this.stderr="",o.stderr?.on("data",r=>{const m=typeof r=="string"?r:r.toString("utf8");this.stderr=y(`${this.stderr}${m}`,q)});const d=new b(o);this.rpc=d,d.on("event",r=>this.handleRpcEvent(r)),d.on("protocolError",r=>this.emitError(r.message)),o.once("close",r=>{this.process===o&&(this.process=null,this.rpc=null,this.terminal||this.emitError(r===0?"Pi RPC process closed before agent_settled":`Pi RPC process exited with code ${r??"unknown"}${this.stderr?`: ${this.stderr}`:""}`),this.stopModelBroker())}),o.once("error",r=>{this.process===o&&this.emitError(r.message)});try{const r=await this.readState();if(!r.sessionId)throw new Error("Pi RPC did not return a session ID");if(r.isStreaming||r.isCompacting)throw new Error("Pi RPC session is not idle after startup");if(this.agentSessionId&&r.sessionId!==this.agentSessionId)throw new Error("Pi RPC resumed a different session");if(!r.sessionFile||!this.ownsSessionFile(r.sessionFile))throw new Error("Pi RPC returned a session file outside the Shennian-owned directory");this.agentSessionId=r.sessionId,this.persistNativeSession&&O(r.sessionId,this.sessionDir)}catch(r){throw await this.stopProcess(),r}}managedTools(){const e=this.managedAgentPolicy?.adapterPolicy;if(!e)return[];const s=["read","grep","find","ls"];(e.fileAccess==="workspace-write"||e.fileAccess==="host-full")&&s.push("edit","write");const t=!!this.extraEnv.SHENNIAN_MANAGER_SESSION_ID,n=!!this.extraEnv.SHENNIAN_MANAGED_ROOM_CONTEXT;return(t||n||e.fileAccess==="host-full")&&s.push("bash"),s}ownsSessionFile(e){const s=u.resolve(this.sessionDir),t=u.resolve(e),n=u.relative(s,t);return n!==""&&!n.startsWith("..")&&!u.isAbsolute(n)}async readState(){const e=await this.requireRpc().request("get_state");return F(e)}requireRpc(){if(!this.rpc)throw new Error("Pi RPC process is not running");return this.rpc}handleRpcEvent(e){if(e.type==="agent_start"){this.active=!0,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:this.agentSessionId??void 0});return}if(e.type==="message_update"){this.lastUsage=P(e.usage)??this.lastUsage;const s=e.assistantMessageEvent;if(!s||typeof s!="object")return;const t=s;t.type==="text_delta"&&typeof t.delta=="string"?(this.runPhase="streaming_text",this.emitEvent({state:"delta",text:t.delta})):t.type==="thinking_delta"&&typeof t.delta=="string"&&(this.runPhase="thinking",this.emitEvent({state:"delta",text:t.delta,thinking:!0}));return}if(e.type==="tool_execution_start"){this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:typeof e.toolName=="string"?e.toolName:"tool",args:e.args&&typeof e.args=="object"&&!Array.isArray(e.args)?e.args:{}});return}if(e.type==="tool_execution_end"){this.runPhase="thinking",this.emitEvent({state:"tool-result",name:typeof e.toolName=="string"?e.toolName:"tool",result:$(e.result)||z(e.result)});return}if(e.type==="tool_execution_update"){this.emitEvent({state:"notify",runPhase:"tool_running",message:"Pi tool is running"});return}if(e.type==="message_end"){const s=e.message;if(s&&typeof s=="object"){const t=s;this.lastUsage=P(t.usage)??this.lastUsage,t.stopReason==="error"&&(this.modelBroker?.setTurnActive(!1),this.emitError(typeof t.errorMessage=="string"?t.errorMessage:"Pi model request failed"))}return}if(e.type==="compaction_start"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is compacting context"});return}if(e.type==="auto_retry_start"||e.type==="summarization_retry_scheduled"){this.runPhase="thinking",this.emitEvent({state:"notify",runPhase:"thinking",message:"Pi is retrying"});return}if(e.type==="extension_ui_request"){typeof e.id=="string"&&this.rpc?.notify("extension_ui_response",{id:e.id,cancelled:!0});return}if(e.type==="extension_error"){this.modelBroker?.setTurnActive(!1),this.emitError(typeof e.error=="string"?e.error:"Pi extension failed");return}e.type==="agent_settled"&&(this.active=!1,this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"final",agentSessionId:this.agentSessionId??void 0,usage:this.lastUsage}))}emitEvent(e){this.runId&&this.emit("agentEvent",{...e,runId:this.runId,seq:this.seq++})}emitTerminal(e){this.terminal||(this.terminal=!0,this.active=!1,this.runPhase=null,this.emitEvent(e))}emitError(e){this.modelBroker?.setTurnActive(!1),this.emitTerminal({state:"error",message:G(e)})}async prepareHostedModel(){const e=T(),s=e.machineToken?.trim();if(!s)return null;const t=new D({serverUrl:e.serverUrl?.trim()||"https://shennian.net",machineToken:s,sessionId:this.shennianSessionId}),n=await t.start(),a=f("runtime","pi-coding-agent",g(this.shennianSessionId),"agent-config");c.mkdirSync(a,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(a,448);const l=u.join(a,"models.json"),h={providers:{"shennian-dashscope":{baseUrl:n.baseUrl,api:"openai-completions",apiKey:n.apiKey,authHeader:!0,compat:{supportsDeveloperRole:!1,supportsReasoningEffort:!0},models:[{id:"deepseek-v4-flash-0731",name:"DeepSeek V4 Flash",reasoning:!0,input:["text"],thinkingLevelMap:{off:null,minimal:null,low:"low",medium:null,high:"high",xhigh:null,max:"max"}}]}}};return c.writeFileSync(l,`${JSON.stringify(h,null,2)}
|
|
4
|
+
`,{encoding:"utf8",mode:384}),process.platform!=="win32"&&c.chmodSync(l,384),this.modelBroker=t,{configDir:a}}async stopModelBroker(){const e=this.modelBroker;this.modelBroker=null,await e?.stop().catch(()=>{})}async stopProcess(){const e=this.process;this.process=null,this.rpc?.close(),this.rpc=null,e?(e.kill("SIGTERM"),await new Promise(s=>{let t=!1;const n=()=>{t||(t=!0,s())};e.once("close",n),setTimeout(()=>{e.kill("SIGKILL"),n()},U).unref()}),await this.stopModelBroker()):await this.stopModelBroker(),this.validationRoot&&(c.rmSync(this.validationRoot,{recursive:!0,force:!0}),this.validationRoot=null)}}E("pi-coding-agent",()=>new Z);export{Z as PiCodingAgentAdapter};
|
|
@@ -9,6 +9,12 @@ export declare function workBuddyRoomActivationText(text: string): string;
|
|
|
9
9
|
export declare function terminateWorkBuddyProcess(proc: Pick<ChildProcess, 'pid' | 'kill'>, platform?: NodeJS.Platform, terminateWindowsTree?: (pid: number) => boolean): void;
|
|
10
10
|
export declare class WorkBuddyAdapter extends AgentAdapter {
|
|
11
11
|
readonly type: "workbuddy";
|
|
12
|
+
get recoveryCapabilities(): {
|
|
13
|
+
inspectTerminal: boolean;
|
|
14
|
+
reattachRun: boolean;
|
|
15
|
+
resumeRun: boolean;
|
|
16
|
+
resumeSession: boolean;
|
|
17
|
+
};
|
|
12
18
|
private process;
|
|
13
19
|
private agentSessionId;
|
|
14
20
|
private workDir;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{spawnSync as y}from"node:child_process";import{createInterface as w}from"node:readline";import{randomUUID as k}from"node:crypto";import E from"node:path";import{AgentAdapter as S,registerAgent as P}from"./adapter.js";import{resolveBuiltinCommand as I,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as _,buildManagedAgentProcessEnv as B}from"../agent-env.js";import{buildPlatformInstructions as C,ensurePlatformInstructionsFile as
|
|
1
|
+
import{spawnSync as y}from"node:child_process";import{createInterface as w}from"node:readline";import{randomUUID as k}from"node:crypto";import E from"node:path";import{AgentAdapter as S,registerAgent as P}from"./adapter.js";import{resolveBuiltinCommand as I,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as _,buildManagedAgentProcessEnv as B}from"../agent-env.js";import{buildPlatformInstructions as C,ensurePlatformInstructionsFile as v}from"./platform-instructions.js";import{resolveWindowsSystemRoot as A}from"../windows-shell.js";import{resolveManagedAgentAdapterEnforcement as T}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as W}from"../security/managed-agent-runtime.js";import{resolveManagedRoomIpcPaths as D}from"../daemon-ipc/paths.js";import{compileWorkBuddyManagedPermissions as M,createWorkBuddyManagedConfigFiles as O,disposeWorkBuddyManagedConfigFiles as R,WORKBUDDY_MANAGED_RUNTIME_SECURITY as b}from"./workbuddy-managed-permissions.js";W("workbuddy",b);const N=new Set(["minimal","low","medium","high","xhigh","max"]);function q(n){const e=n?.trim();return e&&e!=="auto"?e:void 0}function F(n){const e=n?.trim();if(e){if(N.has(e))return e;throw new Error(`Unsupported WorkBuddy reasoning effort "${e}". Supported values: minimal, low, medium, high, xhigh, max.`)}}function U(n,e,t){if(!n)return[];if(e==="win32"){if(!t)throw new Error("WorkBuddy system prompt file is required on Windows.");return["--system-prompt-file",t]}return["--append-system-prompt",n]}function $(n){const e=n.split(/\n\n(?=<[^>\n]+>\n)/);return(e[e.length-1]??n).replace(/^\[群消息\][ \t]+<[^>\n]+>\n/,"").replace(/^\[群消息\]\n?/,"").replace(/^<[^>\n]+>\n/,"").trim()}function c(n,e=process.platform,t=z){e==="win32"&&n.pid&&t(n.pid)||n.kill("SIGTERM")}function z(n){const e=E.win32.join(A(),"System32","taskkill.exe");return y(e,["/pid",String(n),"/T","/F"],{stdio:"ignore",windowsHide:!0,timeout:5e3}).status===0}class j extends S{type="workbuddy";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;persistNativeSession=!0;externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;managedProcessEnv=null;configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(e,t,s,r){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=r?.persistNativeSession!==!1,s&&(this.agentSessionId=s)}async send(e,t,s,r,o,i){if((i?.source==="room_activation"||this.shennianSessionId?.startsWith("room-managed-")===!0)&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=k(),this.resetRunState();const d=i?.source==="room_activation",a=["-p",d?$(e):e,"--output-format","stream-json","--verbose"];this.persistNativeSession||a.push("--no-session-persistence");const h=q(t);h&&a.push("--model",h),d&&a.push("--max-turns","8");const m=this.shennianSessionId?.startsWith("room-managed-")&&!this.agentSessionId&&!d?null:C(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o);if(m){const g=process.platform==="win32"?v(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o):void 0;a.push(...U(m,process.platform,g))}const p=F(s);p&&a.push("--effort",p),this.agentSessionId&&a.push("--resume",this.agentSessionId),a.push(...await this.prepareManagedLaunch()),this.spawnAndParse(a)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(e){const t=I("workbuddy");if(!t){this.disposeManagedConfig(),this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}let s;try{s=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()})}catch(i){throw this.disposeManagedConfig(),i}this.process=s,w({input:s.stdout}).on("line",i=>{if(i.trim())try{this.handleStreamEvent(JSON.parse(i))}catch{}});let o="";s.stderr?.on("data",i=>{o=`${o}${i.toString()}`.slice(-8e3)}),s.on("close",i=>{this.process===s&&(this.process=null,this.runPhase=null,this.disposeManagedConfig(),this.handleProcessClose(i,o))}),s.on("error",i=>{if(this.process!==s)return;this.process=null,this.runPhase=null,this.disposeManagedConfig();const l=i.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:l?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":u(i.message)})})}handleStreamEvent(e){if(e.type==="system"&&e.subtype==="init"&&e.session_id){this.agentSessionId=e.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:e.session_id});return}if(e.type==="control_request"&&e.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:e.request.tool_name,args:e.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${e.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(e.type==="assistant"){const s=e.message?.content;if(s?.length)for(const r of s)this.handleContentBlock(r);else e.text&&this.emitText(e.text,!!e.thinking);return}if(e.type!=="result")return;if(e.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:e.name,result:f(e.content)});return}if(e.subtype==="error"||e.error){this.emitErrorIfOpen({state:"error",message:u(e.error||e.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&e.result&&this.emitText(e.result,!1),e.session_id&&(this.agentSessionId=e.session_id);const t=e.usage??e.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:t?{inputTokens:t.input_tokens??0,outputTokens:t.output_tokens??0}:void 0})}handleContentBlock(e){e.type==="text"&&e.text?this.emitText(e.text,!1):e.type==="thinking"&&e.thinking?this.emitText(e.thinking,!0):e.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:e.name,args:e.input})):e.type==="tool_result"&&this.emitEvent({state:"tool-result",name:e.name,result:f(e.content)})}emitText(e,t){this.runPhase=t?"thinking":"streaming_text";const s=!t&&this.hasEmittedText?`
|
|
2
2
|
|
|
3
|
-
`:"";this.emitEvent({state:"delta",text:s+e,thinking:t||void 0}),t||(this.hasEmittedText=!0)}handleProcessClose(e,t){if(e!==0&&e!==null){this.emitErrorIfOpen({state:"error",message:u(t)||`WorkBuddy exited with code ${e}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(e))}emitEvent(e){this.emit("agentEvent",{...e,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const e=this.process;if(!e){await this.disposeManagedConfig();return}this.process=null,this.runPhase=null,c(e),await new Promise(t=>{let s=!1;const r=()=>{s||(s=!0,t())};e.once("close",r),setTimeout(()=>{c(e),r()},3e3).unref?.()}),await this.disposeManagedConfig()}async prepareManagedLaunch(){const e=this.managedAgentPolicy;if(!e)return this.managedProcessEnv=null,["--permission-mode","bypassPermissions","--setting-sources","user,project,local","--tools","default"];const t=
|
|
3
|
+
`:"";this.emitEvent({state:"delta",text:s+e,thinking:t||void 0}),t||(this.hasEmittedText=!0)}handleProcessClose(e,t){if(e!==0&&e!==null){this.emitErrorIfOpen({state:"error",message:u(t)||`WorkBuddy exited with code ${e}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(e))}emitEvent(e){this.emit("agentEvent",{...e,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const e=this.process;if(!e){await this.disposeManagedConfig();return}this.process=null,this.runPhase=null,c(e),await new Promise(t=>{let s=!1;const r=()=>{s||(s=!0,t())};e.once("close",r),setTimeout(()=>{c(e),r()},3e3).unref?.()}),await this.disposeManagedConfig()}async prepareManagedLaunch(){const e=this.managedAgentPolicy;if(!e)return this.managedProcessEnv=null,["--permission-mode","bypassPermissions","--setting-sources","user,project,local","--tools","default"];const t=D(),s=M(T(e),e.adapterPolicy.workDir,{internalUnixSockets:t.transport==="unix_socket"?[t.endpoint]:[]}),r=await O(s);this.managedConfigDir=r.directory;const o=B({agentType:"workbuddy",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return Object.assign(o,s.runtimeEnv),delete o.CODEBUDDY_IS_SANDBOX,this.managedProcessEnv=o,["--permission-mode","dontAsk","--subagent-permission-mode","dontAsk","--settings",r.settingsPath,"--setting-sources","none","--strict-mcp-config","--mcp-config","{}","--tools",s.toolNames.join(",")]}buildProcessEnv(){return this.managedProcessEnv??_(this.extraEnv)}async disposeManagedConfig(){const e=this.managedConfigDir;this.managedConfigDir=null,this.managedProcessEnv=null,e&&await R(e).catch(()=>{})}}function f(n){if(typeof n=="string")return n;try{return JSON.stringify(n)}catch{return"[unserializable WorkBuddy tool result]"}}function u(n){return n.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}P("workbuddy",()=>new j);export{j as WorkBuddyAdapter,q as normalizeWorkBuddyModelId,F as normalizeWorkBuddyReasoningEffort,u as sanitizeWorkBuddyError,c as terminateWorkBuddyProcess,$ as workBuddyRoomActivationText,U as workBuddySystemPromptArgs};
|