qlogicagent 2.17.10 → 2.18.1

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.
Files changed (44) hide show
  1. package/dist/agent.js +16 -16
  2. package/dist/cli.js +1 -1
  3. package/dist/index.js +431 -423
  4. package/dist/skills/mcp/astraclaw-native-mcp-server.js +6 -5
  5. package/dist/types/agent/tool-loop.d.ts +6 -0
  6. package/dist/types/agent/types.d.ts +5 -0
  7. package/dist/types/cli/handlers/agents-handler.d.ts +9 -0
  8. package/dist/types/cli/handlers/config-handler.d.ts +5 -0
  9. package/dist/types/cli/handlers/files-handler.d.ts +7 -0
  10. package/dist/types/cli/handlers/session-handler.d.ts +43 -0
  11. package/dist/types/cli/handlers/turn-handler.d.ts +12 -0
  12. package/dist/types/cli/permission-rule-persistence.d.ts +16 -0
  13. package/dist/types/cli/stdio-rpc-handler-hosts.d.ts +4 -0
  14. package/dist/types/cli/stdio-server.d.ts +16 -1
  15. package/dist/types/cli/turn-core.d.ts +2 -0
  16. package/dist/types/orchestration/agent-instance.d.ts +4 -0
  17. package/dist/types/orchestration/delegation-coordinator.d.ts +10 -0
  18. package/dist/types/orchestration/product-persistence.d.ts +4 -0
  19. package/dist/types/protocol/wire/acp-agent-management.d.ts +16 -0
  20. package/dist/types/protocol/wire/agent-methods.d.ts +3 -0
  21. package/dist/types/runtime/infra/acp-detector.d.ts +35 -5
  22. package/dist/types/runtime/infra/acp-protocol-adapter.d.ts +14 -0
  23. package/dist/types/runtime/infra/agent-process.d.ts +15 -0
  24. package/dist/types/runtime/infra/llmrouter-catalog.d.ts +26 -6
  25. package/dist/types/runtime/infra/model-registry.d.ts +43 -2
  26. package/dist/types/runtime/infra/user-dirs.d.ts +21 -0
  27. package/dist/types/runtime/permission-model.d.ts +1 -1
  28. package/dist/types/runtime/ports/permission-contracts.d.ts +5 -2
  29. package/dist/types/runtime/prompt/fresh-workspace-evidence.d.ts +1 -2
  30. package/dist/types/runtime/prompt/identity-section.d.ts +1 -1
  31. package/dist/types/runtime/session/native/claude-session-source.d.ts +23 -0
  32. package/dist/types/runtime/session/native/codex-index.d.ts +21 -0
  33. package/dist/types/runtime/session/native/codex-session-source.d.ts +31 -0
  34. package/dist/types/runtime/session/native/cwd-normalize.d.ts +53 -0
  35. package/dist/types/runtime/session/native/ephemeral-rollout-sweep.d.ts +13 -0
  36. package/dist/types/runtime/session/native/native-cwd-agents.d.ts +17 -0
  37. package/dist/types/runtime/session/native/native-session-source.d.ts +89 -0
  38. package/dist/types/runtime/session/native/native-title.d.ts +1 -0
  39. package/dist/types/runtime/session/native/register-builtin-sources.d.ts +1 -0
  40. package/dist/types/runtime/session/session-types.d.ts +8 -0
  41. package/dist/types/skills/permissions/hook-runner.d.ts +9 -0
  42. package/dist/types/skills/permissions/settings-watcher.d.ts +7 -0
  43. package/dist/types/transport/acp-server.d.ts +6 -0
  44. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import{createInterface as Qe}from"node:readline";import{existsSync as Ye,readFileSync as Ze}from"node:fs";import{readFileSync as be}from"node:fs";import{randomUUID as ve}from"node:crypto";import{readdirSync as kt,readFileSync as Pt,statSync as ne,writeFileSync as _t}from"node:fs";import{readdir as se,readFile as ie,stat as ae,writeFile as le}from"node:fs/promises";import{join as u}from"node:path";import{homedir as Q}from"node:os";import{join as c,resolve as lt}from"node:path";import{existsSync as ut}from"node:fs";import{createHash as mt}from"node:crypto";var k=".qlogicagent";function Y(){return process.env.QLOGICAGENT_HOME||c(Q(),k)}function Z(){let t=process.env.QLOGIC_LLMROUTER_USER_ID?.trim();if(t)return t;let e=process.env.QLOGIC_IMPLICIT_OWNER_ID?.trim();if(e)return e;let o=process.env.QLOGIC_DEVICE_ID?.trim();return o?`oc_${o}`:"oc_local"}function X(t){let e=t.trim();if(!e)throw new Error("ownerUserId is required for profile-scoped storage");return encodeURIComponent(e).replace(/\./g,"%2E")}function p(t=Z()){return c(Y(),"profiles",X(t))}function h(){return c(p(),"skills")}function P(){return c(p(),"mcp.json")}function ee(t){if(!t)throw new Error("getProjectAgentDir: cwd is required (must not fall back to process.cwd())");return c(t,k)}function _(t){return c(ee(t),"skills-disabled.json")}import{readFileSync as te,writeFileSync as gt,mkdirSync as ht,renameSync as yt}from"node:fs";import{dirname as vt,join as oe}from"node:path";function C(){return{version:1,disabled:[]}}function x(t){try{let e=JSON.parse(te(t,"utf8"));return!e||typeof e!="object"||!Array.isArray(e.disabled)?C():{version:1,disabled:[...new Set(e.disabled.filter(r=>typeof r=="string"&&r.trim().length>0))]}}catch{return C()}}function re(){return oe(p(),"skills-disabled.json")}function A(){return new Set(x(re()).disabled)}function R(t){return new Set(x(_(t)).disabled)}var ce=["auto-skill-","test-skill-"],ue=".skills_prompt_snapshot.json";function de(t){return ce.some(e=>t.startsWith(e))}function me(t){let e=t.replace(/\r\n/g,`
3
- `),o=e.match(/^---\n[\s\S]*?^version:\s*(\S+)/m),r=e.match(/^---\n[\s\S]*?^description:\s*(.+)/m),n=e.match(/^---\n[\s\S]*?^category:\s*(.+)/m),i=e.match(/^---\n[\s\S]*?^author:\s*(.+)/m),s=e.match(/^---\n[\s\S]*?^requires:\s*(.+)/m),a=e.match(/^---\n[\s\S]*?^environments:\s*(.+)/m);return{version:o?.[1],description:r?.[1]?.trim(),category:n?.[1]?.trim()||void 0,author:i?.[1]?.trim()||void 0,requiredTools:E(s?.[1]),environments:E(a?.[1])}}var M=new Map,y=new Map;function pe(t,e,o,r){let n=e.has(t.name),i=o.has(t.name),s=!he(t.meta,r);return{name:t.name,filePath:t.filePath,baseDir:t.baseDir,globallyDisabled:n,projectDisabled:i,active:!n&&!i&&!s,systemGenerated:t.systemGenerated,description:t.meta.description,version:t.meta.version,category:t.meta.category,author:t.meta.author,requiredTools:t.meta.requiredTools,environments:t.meta.environments}}async function fe(t){let e;try{e=await se(t,{withFileTypes:!0})}catch{e=[]}let o=[];for(let s of e){if(!s.isDirectory()||s.name.startsWith("."))continue;let a=u(t,s.name),l=u(a,"SKILL.md");try{let g=await ae(l);if(!g.isFile())continue;o.push({name:s.name,filePath:l,baseDir:a,mtimeMs:g.mtimeMs,size:g.size})}catch{continue}}o.sort((s,a)=>s.name.localeCompare(a.name));let r=o.map(s=>`${s.name}:${s.mtimeMs}:${s.size}`).join("|"),n=M.get(t);if(n&&n.fingerprint===r)return n.raw;let i=[];for(let s of o){let a;try{a=await ie(s.filePath,"utf8")}catch{continue}i.push({name:s.name,filePath:s.filePath,baseDir:s.baseDir,systemGenerated:de(s.name),mtimeMs:s.mtimeMs,size:s.size,meta:me(a)})}return M.set(t,{fingerprint:r,raw:i}),ye(t,i),i}async function ge(t,e){let o=h(),r=A(),n=t?R(t):new Set,i=y.get(o);return i||(i=fe(o).finally(()=>y.delete(o)),y.set(o,i)),(await i).map(a=>pe(a,r,n,e))}async function D(t,e){return(await ge(t,e)).filter(o=>o.active)}function I(t){let e=h(),o=u(e,t),r=u(o,"SKILL.md");try{if(ne(r).isFile())return{baseDir:o,filePath:r}}catch{}return null}function E(t){if(!t)return;let e=t.replace(/^\[|\]$/g,"").split(/[,\s]+/).map(o=>o.replace(/^["']|["']$/g,"").trim()).filter(Boolean);return e.length>0?e:void 0}function he(t,e){if(t.environments&&e?.currentEnvironment&&!t.environments.includes(e.currentEnvironment))return!1;if(t.requiredTools){if(!e?.availableToolNames)return!1;let o=new Set(e.availableToolNames);return t.requiredTools.every(r=>o.has(r))}return!0}async function ye(t,e){try{let r={version:1,entries:e.map(n=>({name:n.name,size:n.size,mtimeMs:n.mtimeMs,description:n.meta.description,version:n.meta.version,requiredTools:n.meta.requiredTools,environments:n.meta.environments}))};await le(u(t,ue),JSON.stringify(r,null,2),"utf8")}catch{}}var j="astraclaw_capabilities";async function L(t){if(t.tool==="skills_list")return{handled:!0,result:{skills:(await D(t.projectRoot,{availableToolNames:t.availableToolNames,currentEnvironment:t.currentEnvironment??process.platform})).map(we)}};if(t.tool==="skill_view"){let e=typeof t.args.name=="string"?t.args.name.trim():"";if(!e)throw new Error("skill_view requires a skill name");let o=I(e);if(!o)throw new Error(`Skill not found: ${e}`);return{handled:!0,result:{name:e,content:be(o.filePath,"utf8")}}}if(t.tool==="mcp_connectors_list"){if(!t.toolCatalog)return{handled:!0,result:{tools:[],note:"AstraClaw MCP connector catalog is not attached to this capability server instance."}};let e=t.toolCatalog.getToolManifest().filter(o=>o.function.name.startsWith("mcp__")).map(o=>({toolName:o.function.name,server:Se(o.function.name),name:ke(o.function.name),description:o.function.description,inputSchema:o.function.parameters}));return{handled:!0,result:{tools:e,count:e.length}}}if(t.tool==="mcp_tool_call"){if(!t.toolCatalog)throw new Error("AstraClaw MCP connector catalog is not attached");let e=typeof t.args.toolName=="string"?t.args.toolName.trim():"";if(!e.startsWith("mcp__"))throw new Error("mcp_tool_call requires a connector toolName returned by mcp_connectors_list");let o=t.toolCatalog.findTool(e);if(!o?.execute)throw new Error(`MCP connector tool not found or not executable: ${e}`);let r=Te(t.args.args),n=`astraclaw_mcp_${ve().slice(0,8)}`,i=await o.execute(n,r);return{handled:!0,result:{toolName:e,content:i.content,details:i.details,imageUrls:i.imageUrls}}}return{handled:!1}}function we(t){return{name:t.name,description:t.description,version:t.version,category:t.category,requiredTools:t.requiredTools,environments:t.environments}}function Te(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:{}}function Se(t){let e=t.split("__");return e.length>=3?e[1]??"":""}function ke(t){let e=t.split("__");return e.length>=3?e.slice(2).join("__"):t}import d from"node:path";import{fileURLToPath as Ae}from"node:url";import{createRuntime as Re}from"mcporter";var O=new Set(["gateway","agents_list","session_status","sessions_send","sessions_list","sessions_history","sessions_spawn","cron","config","workflow"]),Pe=new Set([...O,"agent"]);function N(t){return t?.riskLevel?t.riskLevel:t?.isDangerous?"system":t?.isReadOnly?"read":t?.isEgress?"external_egress":"write"}var b=class{toolPool=new Map;setTools(e){this.toolPool.clear();for(let o of e)this.toolPool.set(o.name,o)}addTool(e){this.toolPool.set(e.name,e)}addTools(e){for(let o of e)this.addTool(o)}removeTool(e){return this.toolPool.delete(e)}findTool(e){return this.toolPool.get(e)}hasTool(e){return this.toolPool.has(e)}getToolNames(){return Array.from(this.toolPool.keys())}getToolCount(){return this.toolPool.size}async executeTool(e,o,r,n){let i=this.toolPool.get(e);if(!i)throw new Error(`Tool not found: ${e}`);return i.execute(o,r,n)}getTools(){return Array.from(this.toolPool.values()).filter(e=>e.isEnabled?.()!==!1)}getToolManifest(){let e=[];for(let o of this.toolPool.values()){if(o.isEnabled?.()===!1)continue;let r=N(o);r==="write"&&(o.category==="media"||o.category==="web"||o.category==="mcp"?r="external_egress":(o.category==="system"||o.name==="exec")&&(r="system")),e.push({type:"function",function:{name:o.name,description:o.description,parameters:o.parameters},meta:{category:o.category??"other",displayName:o.displayName??{key:`capability.tool.${o.name}.name`,fallback:o.label},displayDescription:o.displayDescription??{key:`capability.tool.${o.name}.description`,fallback:""},parallelSafe:o.isConcurrencySafe??!1,riskLevel:r,isReadOnly:r==="read",isDangerous:r==="system",isDelete:o.isDelete??!1,isEgress:o.isEgress??r==="external_egress",egressCarriesData:o.egressCarriesData??!1}})}return e}},$=new b;function q(t){$.addTools(t)}function U(t){return $.removeTool(t)}var _e=new Set(["describe","fetch","find","get","inspect","list","query","read","search","stat"]),Ce=new Set(["append","copy","create","delete","edit","move","patch","remove","rename","set","update","upload","write"]);function F(t){let e=t.annotations,o=xe(t.name),r=o.some(l=>Ce.has(l)),n=o.some(l=>_e.has(l)),i=e?.destructiveHint===!0||r,s=e?.readOnlyHint===!0||!i&&n,a=s||e?.idempotentHint===!0&&!i;return{isReadOnly:s,isConcurrencySafe:a,isDestructive:i}}function xe(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}var Me=12e4,v=3,Ee=1e3;async function De(t,e){let o=e.sleep??(i=>new Promise(s=>setTimeout(s,i))),r=Math.max(1,e.attempts),n;for(let i=1;i<=r;i++)try{return await t()}catch(s){n=s,i<r&&(e.onRetry?.(i,s),await o(e.baseDelayMs*i))}throw n}var f=class{runtime=null;definitions=[];toolsByServer=new Map;injectedNames=new Set;log;workspaceRoot;toolCatalog;serverToolFilters=new Map;serverOAuth=new Map;constructor(e){this.log=e.log??{info:()=>{},warn:()=>{}},this.workspaceRoot=e.workspaceRoot?d.resolve(e.workspaceRoot):void 0,this.toolCatalog=e.toolCatalog,this.definitions=e.servers.filter(o=>!o.disabled).filter(o=>{let r=He(o);for(let n of r)this.log.warn(`[mcp] rejected server "${o.name}": ${n.message}`);return o.tools&&this.serverToolFilters.set(o.name,o.tools),o.oauth===!0&&this.serverOAuth.set(o.name,!0),r.length===0}).map(o=>this.toServerDefinition(o)).filter(o=>o!==null)}toServerDefinition(e){if((e.type??(e.url?"http":"stdio"))==="http"){if(!e.url)return this.log.warn(`[mcp] server "${e.name}" is type "http" but has no url, skipping`),null;let r;try{r=new URL(e.url)}catch{return this.log.warn(`[mcp] server "${e.name}" has an invalid url "${e.url}", skipping`),null}return{name:e.name,command:{kind:"http",url:r,headers:e.headers}}}return e.command?{name:e.name,command:{kind:"stdio",command:e.command,args:e.args??[],cwd:e.cwd??this.workspaceRoot??process.cwd()},env:e.env}:(this.log.warn(`[mcp] server "${e.name}" is type "stdio" but has no command, skipping`),null)}async connectAll(){this.definitions.length!==0&&(this.runtime=await Re({servers:this.definitions,clientInfo:{name:"qlogicagent",version:"1.0.0"},logger:{info:e=>this.log.info(`[mcp] ${e}`),warn:e=>this.log.warn(`[mcp] ${e}`),error:(e,o)=>this.log.warn(`[mcp] ${e}${o?` (${o instanceof Error?o.message:String(o)})`:""}`),debug:()=>{}}}),await Promise.allSettled(this.definitions.map(async e=>{try{let o=await De(()=>this.runtime.listTools(e.name,{includeSchema:!0,disableOAuth:this.serverOAuth.get(e.name)!==!0}),{attempts:v,baseDelayMs:Ee,onRetry:(n,i)=>this.log.warn(`[mcp] connect attempt ${n}/${v} for ${e.name} failed, retrying: ${i instanceof Error?i.message:i}`)}),r=z(o,this.serverToolFilters.get(e.name));this.toolsByServer.set(e.name,r),this.log.info(`[mcp] connected to ${e.name} (${r.length}/${o.length} tools)`)}catch(o){this.log.warn(`[mcp] failed to connect to ${e.name} after ${v} attempts: ${o instanceof Error?o.message:o}`)}})))}injectTools(){for(let[e,o]of this.toolsByServer){let r=this.injectPortableTools(e,o);this.log.info(`[mcp] injected ${r.length} tools from ${e}`)}}async refreshServerTools(e){if(!this.runtime)throw new Error("MCP runtime is not connected");let o=await this.runtime.listTools(e,{includeSchema:!0,disableOAuth:this.serverOAuth.get(e)!==!0}),r=z(o,this.serverToolFilters.get(e));return this.toolsByServer.set(e,r),this.retractServerTools(e),this.injectPortableTools(e,r),r.length}getConnectedServers(){return Array.from(this.toolsByServer.keys())}getToolCount(){let e=0;for(let o of this.toolsByServer.values())e+=o.length;return e}async disconnectAll(){for(let o of this.injectedNames)this.removeRegisteredTool(o);this.injectedNames.clear(),this.toolsByServer.clear();let e=this.runtime;if(this.runtime=null,e)try{await e.close()}catch{}}toPortableTool(e,o){let r=F({name:o.name,description:o.description});return{name:`mcp__${G(e)}__${o.name}`,label:`[${e}] ${o.name}`,category:"mcp",description:o.description??`MCP tool from ${e}`,parameters:o.inputSchema??{type:"object",properties:{}},isConcurrencySafe:r.isConcurrencySafe,isReadOnly:r.isReadOnly,isDestructive:r.isDestructive,searchHint:`mcp ${e} ${o.name.replace(/[_-]+/g," ")}`,execute:async(n,i)=>{let s=this.runtime;if(!s)return{content:[{type:"text",text:`MCP server "${e}" is not connected.`}],details:{error:"mcp_not_connected"}};try{let a=await s.callTool(e,o.name,{args:i,timeoutMs:Me});return Ie(a)}catch(a){let l=a instanceof Error?a.message:String(a);return{content:[{type:"text",text:`MCP tool error: ${l}`}],details:{error:l}}}}}}wrapToolsForWorkspaceBoundary(e){return this.workspaceRoot?e.map(o=>{if(!o.execute)return o;let r=o.execute;return{...o,execute:async(n,i,s)=>{let a=Le(o.name,i,this.workspaceRoot);return a?{content:[{type:"text",text:a}],details:{type:"mcp",error:"workspace_boundary"}}:r(n,i,s)}}}):e}injectPortableTools(e,o){let r=this.wrapToolsForWorkspaceBoundary(o.map(n=>this.toPortableTool(e,n)));this.registerTools(r);for(let n of r)this.injectedNames.add(n.name);return r}retractServerTools(e){let o=`mcp__${G(e)}__`;for(let r of[...this.injectedNames])r.startsWith(o)&&(this.removeRegisteredTool(r),this.injectedNames.delete(r))}registerTools(e){this.toolCatalog?this.toolCatalog.addTools(e):q(e)}removeRegisteredTool(e){return this.toolCatalog?.removeTool(e)??U(e)}};function Ie(t){let e=t&&typeof t=="object"?t:{};return{content:[{type:"text",text:(Array.isArray(e.content)?e.content:[]).filter(i=>!!i&&typeof i=="object").filter(i=>i.type==="text"&&typeof i.text=="string").map(i=>i.text).join(`
4
- `)||"(no text output)"}],...e.isError?{details:{error:"mcp_tool_error"}}:{}}}function G(t){return t.replace(/[^a-zA-Z0-9_]/g,"_").toLowerCase()}var je=/(?:^|_)(path|paths|file|files|filename|filenames|dir|dirs|directory|directories|folder|folders|cwd|root|uri)(?:$|_)/i;function Le(t,e,o){if(!o)return null;for(let r of T(e)){let n=$e(r.value,o);if(n&&!qe(n.candidate,n.root,n.pathApi))return`Blocked: MCP tool "${t}" path "${r.value}" is outside the workspace boundary "${n.root}"`}return null}function T(t,e="",o=0){if(o>8)return[];if(typeof t=="string")return Oe(e,t)?[{key:e,value:t}]:[];if(Array.isArray(t))return t.flatMap(n=>T(n,e,o+1));if(!t||typeof t!="object")return[];let r=[];for(let[n,i]of Object.entries(t))r.push(...T(i,n,o+1));return r}function Oe(t,e){if(!je.test(t))return!1;let o=e.trim();return!o||/^https?:\/\//i.test(o)?!1:Ne(o)}function Ne(t){return d.isAbsolute(t)||/^[A-Za-z]:[\\/]/.test(t)||/^\\\\/.test(t)||/^file:\/\//i.test(t)||t.includes("../")||t.includes("..\\")||t.startsWith(".\\")||t.startsWith("./")||t.includes("/")||t.includes("\\")}function $e(t,e){let o=t.trim();if(/^file:\/\//i.test(o))try{o=Ae(o)}catch{return null}let r=W(o)||W(e)?d.win32:d,n=r.resolve(e);return{candidate:r.isAbsolute(o)?r.resolve(o):r.resolve(n,o),root:n,pathApi:r}}function qe(t,e,o){let r=o.relative(e,t);return r===""||!r.startsWith("..")&&!o.isAbsolute(r)}function W(t){return/^[A-Za-z]:[\\/]/.test(t)||/^\\\\/.test(t)}function J(t){if(!t||typeof t!="object")return[];let e=t,o=[],r=e.mcpServers??e.servers??e;for(let[n,i]of Object.entries(r)){if(!i||typeof i!="object")continue;let s=i;if(typeof s.url=="string"){o.push({name:n,type:"http",url:s.url,headers:s.headers&&typeof s.headers=="object"?s.headers:void 0,disabled:s.disabled===!0,oauth:s.oauth===!0,tools:B(s.tools)});continue}typeof s.command=="string"&&o.push({name:n,type:"stdio",command:s.command,args:Array.isArray(s.args)?s.args:void 0,env:s.env&&typeof s.env=="object"?s.env:void 0,cwd:typeof s.cwd=="string"?s.cwd:void 0,disabled:s.disabled===!0,oauth:s.oauth===!0,tools:B(s.tools)})}return o}function z(t,e){let o=new Set(e?.include??[]),r=new Set(e?.exclude??[]);return t.filter(n=>o.size>0&&!o.has(n.name)?!1:!r.has(n.name))}function B(t){if(!t||typeof t!="object")return;let e=t,o=H(e.include),r=H(e.exclude);if(!(!o&&!r))return{...o?{include:o}:{},...r?{exclude:r}:{}}}function H(t){if(!Array.isArray(t))return;let e=t.filter(o=>typeof o=="string"&&o.trim().length>0);return e.length>0?e:void 0}var Ue=new Set(["bash","sh","zsh","fish","cmd","cmd.exe","powershell","powershell.exe","pwsh","pwsh.exe"]),Fe=new Set(["-c","/c","-command","-encodedcommand","-e"]),Ge=["curl","wget","invokewebrequest","invoke-restmethod","iwr","irm","nc","netcat"],We=["env","processenv","apikey","api_key","token","secret","credential","credentials","awsaccesskey","awssecret"],ze=["authorizedkeys","sshknownhosts","crontab","launchctl","schtasks","startup","runkey","runonce","bashrc","profile"],Be=/\bssh-(?:rsa|ed25519|ecdsa-[^\s]+)\s+[A-Za-z0-9+/=]{40,}/i;function He(t){let e=[],o=[t.command??"",...t.args??[],...Object.keys(t.env??{}),...Object.values(t.env??{})].join(`
5
- `);if(Be.test(o)&&e.push({code:"ioc_public_key",message:"configuration contains an inline SSH public key IOC"}),(t.type??(t.url?"http":"stdio"))!=="stdio"||!Je(t.command))return e;let r=Ve(t.args??[]);if(!r)return e;let n=Ke(r);return w(n,ze)&&e.push({code:"persistence",message:"stdio shell command attempts a persistence write"}),w(n,Ge)&&w(n,We)&&e.push({code:"dangerous_egress",message:"stdio shell command combines network egress with likely secret access"}),e}function Je(t){if(!t)return!1;let e=d.basename(t).toLowerCase();return Ue.has(e)}function Ve(t){for(let e=0;e<t.length;e++){let o=t[e]?.toLowerCase();if(o&&Fe.has(o))return t.slice(e+1).join(" ")}return""}function Ke(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"")}function w(t,e){return e.some(o=>t.includes(o))}var Xe=[{name:"skills_list",description:"List AstraClaw skills active for the current workspace.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"skill_view",description:"Read the full SKILL.md instructions for one AstraClaw skill.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Skill name from skills_list."}},required:["name"],additionalProperties:!1}},{name:"mcp_connectors_list",description:"List MCP connector tools installed in AstraClaw's Plugins page and shared with this external agent.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"mcp_tool_call",description:"Call one AstraClaw shared MCP connector tool by toolName from mcp_connectors_list.",inputSchema:{type:"object",properties:{toolName:{type:"string",description:"Connector tool name, for example mcp__filesystem__read_text_file."},args:{type:"object",description:"Arguments for the connector tool."}},required:["toolName"],additionalProperties:!1}}],S=class{tools=new Map;findTool(e){return this.tools.get(e)}getToolManifest(){return Array.from(this.tools.values()).map(e=>({type:"function",function:{name:e.name,description:e.description,parameters:e.parameters}}))}getToolNames(){return Array.from(this.tools.keys())}setTools(e){this.tools.clear(),this.addTools(e)}addTool(e){this.tools.set(e.name,e)}addTools(e){for(let o of e)this.addTool(o)}removeTool(e){return this.tools.delete(e)}},m=null,et=Qe({input:process.stdin,crlfDelay:Number.POSITIVE_INFINITY});et.on("line",t=>{tt(t)});async function tt(t){let e=t.trim();if(!e)return;let o;try{o=JSON.parse(e)}catch{return}if(o.id===void 0||o.id===null)return o.method==="notifications/initialized",void 0;try{let r=await ot(o);K({jsonrpc:"2.0",id:o.id,result:r})}catch(r){K({jsonrpc:"2.0",id:o.id,error:{code:-32603,message:r instanceof Error?r.message:String(r)}})}}async function ot(t){switch(t.method){case"initialize":return{protocolVersion:"2024-11-05",capabilities:{tools:{}},serverInfo:{name:j,version:"1.0.0"}};case"tools/list":return{tools:Xe};case"tools/call":{let e=V(t.params),o=typeof e.name=="string"?e.name:"",r=V(e.arguments),n=rt(),i=o==="mcp_connectors_list"||o==="mcp_tool_call"?await nt(n):void 0,s=await L({tool:o,args:r,projectRoot:n,toolCatalog:i});if(!s.handled)throw new Error(`Unknown tool: ${o}`);return{content:[{type:"text",text:JSON.stringify(s.result)}]}}default:throw new Error(`Unknown method: ${t.method}`)}}function rt(){return process.env.ASTRACLAW_PROJECT_ROOT||process.env.QLOGICAGENT_PROJECT_ROOT||process.cwd()}async function nt(t){if(m?.projectRoot===t)return await m.ready,m.catalog;m?.manager?.disconnectAll().catch(()=>{});let e=new S,o=new f({servers:st(),workspaceRoot:t,toolCatalog:e,log:{info:n=>process.stderr.write(`${n}
2
+ import{createInterface as Ye}from"node:readline";import{existsSync as Ze,readFileSync as Xe}from"node:fs";import{readFileSync as ve}from"node:fs";import{randomUUID as we}from"node:crypto";import{readdirSync as Pt,readFileSync as _t,statSync as ne,writeFileSync as Ct}from"node:fs";import{readdir as se,readFile as ie,stat as ae,writeFile as le}from"node:fs/promises";import{join as u}from"node:path";import{homedir as Q}from"node:os";import{join as c,resolve as ct}from"node:path";import{existsSync as dt}from"node:fs";import{createHash as pt}from"node:crypto";var k=".qlogicagent";function Y(){return process.env.QLOGICAGENT_HOME||c(Q(),k)}function Z(){let t=process.env.QLOGIC_LLMROUTER_USER_ID?.trim();if(t)return t;let e=process.env.QLOGIC_IMPLICIT_OWNER_ID?.trim();if(e)return e;let o=process.env.QLOGIC_DEVICE_ID?.trim();return o?`oc_${o}`:"oc_local"}function X(t){let e=t.trim();if(!e)throw new Error("ownerUserId is required for profile-scoped storage");return encodeURIComponent(e).replace(/\./g,"%2E")}function p(t=Z()){return c(Y(),"profiles",X(t))}function h(){return c(p(),"skills")}function P(){return c(p(),"mcp.json")}function ee(t){if(!t)throw new Error("getProjectAgentDir: cwd is required (must not fall back to process.cwd())");return c(t,k)}function _(t){return c(ee(t),"skills-disabled.json")}import{readFileSync as te,writeFileSync as ht,mkdirSync as yt,renameSync as bt}from"node:fs";import{dirname as wt,join as oe}from"node:path";function C(){return{version:1,disabled:[]}}function x(t){try{let e=JSON.parse(te(t,"utf8"));return!e||typeof e!="object"||!Array.isArray(e.disabled)?C():{version:1,disabled:[...new Set(e.disabled.filter(r=>typeof r=="string"&&r.trim().length>0))]}}catch{return C()}}function re(){return oe(p(),"skills-disabled.json")}function A(){return new Set(x(re()).disabled)}function R(t){return new Set(x(_(t)).disabled)}var ce=["auto-skill-","test-skill-"],ue=".skills_prompt_snapshot.json";function de(t){return ce.some(e=>t.startsWith(e))}function me(t){let e=t.replace(/\r\n/g,`
3
+ `),o=e.match(/^---\n[\s\S]*?^version:\s*(\S+)/m),r=e.match(/^---\n[\s\S]*?^category:\s*(.+)/m),n=e.match(/^---\n[\s\S]*?^author:\s*(.+)/m),i=e.match(/^---\n[\s\S]*?^requires:\s*(.+)/m),s=e.match(/^---\n[\s\S]*?^environments:\s*(.+)/m),a=e.match(/^---\n([\s\S]*?)\n---/)?.[1]??"";return{version:o?.[1],description:pe(a),category:r?.[1]?.trim()||void 0,author:n?.[1]?.trim()||void 0,requiredTools:D(i?.[1]),environments:D(s?.[1])}}function pe(t){let e=t.split(`
4
+ `),o=e.findIndex(s=>/^description:/.test(s));if(o===-1)return;let r=e[o].replace(/^description:[ \t]*/,"").trim(),n=/^[>|][+-]?$/.test(r),i=[];!n&&r&&i.push(r);for(let s=o+1;s<e.length;s++){let a=e[s];if(!/^[ \t]/.test(a))break;let l=a.trim();l&&i.push(l)}if(i.length!==0)return i.join(" ").replace(/^["']|["']$/g,"").trim()||void 0}var M=new Map,y=new Map;function fe(t,e,o,r){let n=e.has(t.name),i=o.has(t.name),s=!ye(t.meta,r);return{name:t.name,filePath:t.filePath,baseDir:t.baseDir,globallyDisabled:n,projectDisabled:i,active:!n&&!i&&!s,systemGenerated:t.systemGenerated,description:t.meta.description,version:t.meta.version,category:t.meta.category,author:t.meta.author,requiredTools:t.meta.requiredTools,environments:t.meta.environments}}async function ge(t){let e;try{e=await se(t,{withFileTypes:!0})}catch{e=[]}let o=[];for(let s of e){if(!s.isDirectory()||s.name.startsWith("."))continue;let a=u(t,s.name),l=u(a,"SKILL.md");try{let g=await ae(l);if(!g.isFile())continue;o.push({name:s.name,filePath:l,baseDir:a,mtimeMs:g.mtimeMs,size:g.size})}catch{continue}}o.sort((s,a)=>s.name.localeCompare(a.name));let r=o.map(s=>`${s.name}:${s.mtimeMs}:${s.size}`).join("|"),n=M.get(t);if(n&&n.fingerprint===r)return n.raw;let i=[];for(let s of o){let a;try{a=await ie(s.filePath,"utf8")}catch{continue}i.push({name:s.name,filePath:s.filePath,baseDir:s.baseDir,systemGenerated:de(s.name),mtimeMs:s.mtimeMs,size:s.size,meta:me(a)})}return M.set(t,{fingerprint:r,raw:i}),be(t,i),i}async function he(t,e){let o=h(),r=A(),n=t?R(t):new Set,i=y.get(o);return i||(i=ge(o).finally(()=>y.delete(o)),y.set(o,i)),(await i).map(a=>fe(a,r,n,e))}async function E(t,e){return(await he(t,e)).filter(o=>o.active)}function I(t){let e=h(),o=u(e,t),r=u(o,"SKILL.md");try{if(ne(r).isFile())return{baseDir:o,filePath:r}}catch{}return null}function D(t){if(!t)return;let e=t.replace(/^\[|\]$/g,"").split(/[,\s]+/).map(o=>o.replace(/^["']|["']$/g,"").trim()).filter(Boolean);return e.length>0?e:void 0}function ye(t,e){if(t.environments&&e?.currentEnvironment&&!t.environments.includes(e.currentEnvironment))return!1;if(t.requiredTools){if(!e?.availableToolNames)return!1;let o=new Set(e.availableToolNames);return t.requiredTools.every(r=>o.has(r))}return!0}async function be(t,e){try{let r={version:1,entries:e.map(n=>({name:n.name,size:n.size,mtimeMs:n.mtimeMs,description:n.meta.description,version:n.meta.version,requiredTools:n.meta.requiredTools,environments:n.meta.environments}))};await le(u(t,ue),JSON.stringify(r,null,2),"utf8")}catch{}}var j="astraclaw_capabilities";async function L(t){if(t.tool==="skills_list")return{handled:!0,result:{skills:(await E(t.projectRoot,{availableToolNames:t.availableToolNames,currentEnvironment:t.currentEnvironment??process.platform})).map(Te)}};if(t.tool==="skill_view"){let e=typeof t.args.name=="string"?t.args.name.trim():"";if(!e)throw new Error("skill_view requires a skill name");let o=I(e);if(!o)throw new Error(`Skill not found: ${e}`);return{handled:!0,result:{name:e,content:ve(o.filePath,"utf8")}}}if(t.tool==="mcp_connectors_list"){if(!t.toolCatalog)return{handled:!0,result:{tools:[],note:"AstraClaw MCP connector catalog is not attached to this capability server instance."}};let e=t.toolCatalog.getToolManifest().filter(o=>o.function.name.startsWith("mcp__")).map(o=>({toolName:o.function.name,server:ke(o.function.name),name:Pe(o.function.name),description:o.function.description,inputSchema:o.function.parameters}));return{handled:!0,result:{tools:e,count:e.length}}}if(t.tool==="mcp_tool_call"){if(!t.toolCatalog)throw new Error("AstraClaw MCP connector catalog is not attached");let e=typeof t.args.toolName=="string"?t.args.toolName.trim():"";if(!e.startsWith("mcp__"))throw new Error("mcp_tool_call requires a connector toolName returned by mcp_connectors_list");let o=t.toolCatalog.findTool(e);if(!o?.execute)throw new Error(`MCP connector tool not found or not executable: ${e}`);let r=Se(t.args.args),n=`astraclaw_mcp_${we().slice(0,8)}`,i=await o.execute(n,r);return{handled:!0,result:{toolName:e,content:i.content,details:i.details,imageUrls:i.imageUrls}}}return{handled:!1}}function Te(t){return{name:t.name,description:t.description,version:t.version,category:t.category,requiredTools:t.requiredTools,environments:t.environments}}function Se(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:{}}function ke(t){let e=t.split("__");return e.length>=3?e[1]??"":""}function Pe(t){let e=t.split("__");return e.length>=3?e.slice(2).join("__"):t}import d from"node:path";import{fileURLToPath as Re}from"node:url";import{createRuntime as Me}from"mcporter";var O=new Set(["gateway","agents_list","session_status","sessions_send","sessions_list","sessions_history","sessions_spawn","cron","config","workflow"]),_e=new Set([...O,"agent"]);function N(t){return t?.riskLevel?t.riskLevel:t?.isDangerous?"system":t?.isReadOnly?"read":t?.isEgress?"external_egress":"write"}var b=class{toolPool=new Map;setTools(e){this.toolPool.clear();for(let o of e)this.toolPool.set(o.name,o)}addTool(e){this.toolPool.set(e.name,e)}addTools(e){for(let o of e)this.addTool(o)}removeTool(e){return this.toolPool.delete(e)}findTool(e){return this.toolPool.get(e)}hasTool(e){return this.toolPool.has(e)}getToolNames(){return Array.from(this.toolPool.keys())}getToolCount(){return this.toolPool.size}async executeTool(e,o,r,n){let i=this.toolPool.get(e);if(!i)throw new Error(`Tool not found: ${e}`);return i.execute(o,r,n)}getTools(){return Array.from(this.toolPool.values()).filter(e=>e.isEnabled?.()!==!1)}getToolManifest(){let e=[];for(let o of this.toolPool.values()){if(o.isEnabled?.()===!1)continue;let r=N(o);r==="write"&&(o.category==="media"||o.category==="web"||o.category==="mcp"?r="external_egress":(o.category==="system"||o.name==="exec")&&(r="system")),e.push({type:"function",function:{name:o.name,description:o.description,parameters:o.parameters},meta:{category:o.category??"other",displayName:o.displayName??{key:`capability.tool.${o.name}.name`,fallback:o.label},displayDescription:o.displayDescription??{key:`capability.tool.${o.name}.description`,fallback:""},parallelSafe:o.isConcurrencySafe??!1,riskLevel:r,isReadOnly:r==="read",isDangerous:r==="system",isDelete:o.isDelete??!1,isEgress:o.isEgress??r==="external_egress",egressCarriesData:o.egressCarriesData??!1}})}return e}},$=new b;function q(t){$.addTools(t)}function U(t){return $.removeTool(t)}var Ce=new Set(["describe","fetch","find","get","inspect","list","query","read","search","stat"]),xe=new Set(["append","copy","create","delete","edit","move","patch","remove","rename","set","update","upload","write"]);function F(t){let e=t.annotations,o=Ae(t.name),r=o.some(l=>xe.has(l)),n=o.some(l=>Ce.has(l)),i=e?.destructiveHint===!0||r,s=e?.readOnlyHint===!0||!i&&n,a=s||e?.idempotentHint===!0&&!i;return{isReadOnly:s,isConcurrencySafe:a,isDestructive:i}}function Ae(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean)}var De=12e4,v=3,Ee=1e3;async function Ie(t,e){let o=e.sleep??(i=>new Promise(s=>setTimeout(s,i))),r=Math.max(1,e.attempts),n;for(let i=1;i<=r;i++)try{return await t()}catch(s){n=s,i<r&&(e.onRetry?.(i,s),await o(e.baseDelayMs*i))}throw n}var f=class{runtime=null;definitions=[];toolsByServer=new Map;injectedNames=new Set;log;workspaceRoot;toolCatalog;serverToolFilters=new Map;serverOAuth=new Map;constructor(e){this.log=e.log??{info:()=>{},warn:()=>{}},this.workspaceRoot=e.workspaceRoot?d.resolve(e.workspaceRoot):void 0,this.toolCatalog=e.toolCatalog,this.definitions=e.servers.filter(o=>!o.disabled).filter(o=>{let r=Je(o);for(let n of r)this.log.warn(`[mcp] rejected server "${o.name}": ${n.message}`);return o.tools&&this.serverToolFilters.set(o.name,o.tools),o.oauth===!0&&this.serverOAuth.set(o.name,!0),r.length===0}).map(o=>this.toServerDefinition(o)).filter(o=>o!==null)}toServerDefinition(e){if((e.type??(e.url?"http":"stdio"))==="http"){if(!e.url)return this.log.warn(`[mcp] server "${e.name}" is type "http" but has no url, skipping`),null;let r;try{r=new URL(e.url)}catch{return this.log.warn(`[mcp] server "${e.name}" has an invalid url "${e.url}", skipping`),null}return{name:e.name,command:{kind:"http",url:r,headers:e.headers}}}return e.command?{name:e.name,command:{kind:"stdio",command:e.command,args:e.args??[],cwd:e.cwd??this.workspaceRoot??process.cwd()},env:e.env}:(this.log.warn(`[mcp] server "${e.name}" is type "stdio" but has no command, skipping`),null)}async connectAll(){this.definitions.length!==0&&(this.runtime=await Me({servers:this.definitions,clientInfo:{name:"qlogicagent",version:"1.0.0"},logger:{info:e=>this.log.info(`[mcp] ${e}`),warn:e=>this.log.warn(`[mcp] ${e}`),error:(e,o)=>this.log.warn(`[mcp] ${e}${o?` (${o instanceof Error?o.message:String(o)})`:""}`),debug:()=>{}}}),await Promise.allSettled(this.definitions.map(async e=>{try{let o=await Ie(()=>this.runtime.listTools(e.name,{includeSchema:!0,disableOAuth:this.serverOAuth.get(e.name)!==!0}),{attempts:v,baseDelayMs:Ee,onRetry:(n,i)=>this.log.warn(`[mcp] connect attempt ${n}/${v} for ${e.name} failed, retrying: ${i instanceof Error?i.message:i}`)}),r=z(o,this.serverToolFilters.get(e.name));this.toolsByServer.set(e.name,r),this.log.info(`[mcp] connected to ${e.name} (${r.length}/${o.length} tools)`)}catch(o){this.log.warn(`[mcp] failed to connect to ${e.name} after ${v} attempts: ${o instanceof Error?o.message:o}`)}})))}injectTools(){for(let[e,o]of this.toolsByServer){let r=this.injectPortableTools(e,o);this.log.info(`[mcp] injected ${r.length} tools from ${e}`)}}async refreshServerTools(e){if(!this.runtime)throw new Error("MCP runtime is not connected");let o=await this.runtime.listTools(e,{includeSchema:!0,disableOAuth:this.serverOAuth.get(e)!==!0}),r=z(o,this.serverToolFilters.get(e));return this.toolsByServer.set(e,r),this.retractServerTools(e),this.injectPortableTools(e,r),r.length}getConnectedServers(){return Array.from(this.toolsByServer.keys())}getToolCount(){let e=0;for(let o of this.toolsByServer.values())e+=o.length;return e}async disconnectAll(){for(let o of this.injectedNames)this.removeRegisteredTool(o);this.injectedNames.clear(),this.toolsByServer.clear();let e=this.runtime;if(this.runtime=null,e)try{await e.close()}catch{}}toPortableTool(e,o){let r=F({name:o.name,description:o.description});return{name:`mcp__${G(e)}__${o.name}`,label:`[${e}] ${o.name}`,category:"mcp",description:o.description??`MCP tool from ${e}`,parameters:o.inputSchema??{type:"object",properties:{}},isConcurrencySafe:r.isConcurrencySafe,isReadOnly:r.isReadOnly,isDestructive:r.isDestructive,searchHint:`mcp ${e} ${o.name.replace(/[_-]+/g," ")}`,execute:async(n,i)=>{let s=this.runtime;if(!s)return{content:[{type:"text",text:`MCP server "${e}" is not connected.`}],details:{error:"mcp_not_connected"}};try{let a=await s.callTool(e,o.name,{args:i,timeoutMs:De});return je(a)}catch(a){let l=a instanceof Error?a.message:String(a);return{content:[{type:"text",text:`MCP tool error: ${l}`}],details:{error:l}}}}}}wrapToolsForWorkspaceBoundary(e){return this.workspaceRoot?e.map(o=>{if(!o.execute)return o;let r=o.execute;return{...o,execute:async(n,i,s)=>{let a=Oe(o.name,i,this.workspaceRoot);return a?{content:[{type:"text",text:a}],details:{type:"mcp",error:"workspace_boundary"}}:r(n,i,s)}}}):e}injectPortableTools(e,o){let r=this.wrapToolsForWorkspaceBoundary(o.map(n=>this.toPortableTool(e,n)));this.registerTools(r);for(let n of r)this.injectedNames.add(n.name);return r}retractServerTools(e){let o=`mcp__${G(e)}__`;for(let r of[...this.injectedNames])r.startsWith(o)&&(this.removeRegisteredTool(r),this.injectedNames.delete(r))}registerTools(e){this.toolCatalog?this.toolCatalog.addTools(e):q(e)}removeRegisteredTool(e){return this.toolCatalog?.removeTool(e)??U(e)}};function je(t){let e=t&&typeof t=="object"?t:{};return{content:[{type:"text",text:(Array.isArray(e.content)?e.content:[]).filter(i=>!!i&&typeof i=="object").filter(i=>i.type==="text"&&typeof i.text=="string").map(i=>i.text).join(`
5
+ `)||"(no text output)"}],...e.isError?{details:{error:"mcp_tool_error"}}:{}}}function G(t){return t.replace(/[^a-zA-Z0-9_]/g,"_").toLowerCase()}var Le=/(?:^|_)(path|paths|file|files|filename|filenames|dir|dirs|directory|directories|folder|folders|cwd|root|uri)(?:$|_)/i;function Oe(t,e,o){if(!o)return null;for(let r of T(e)){let n=qe(r.value,o);if(n&&!Ue(n.candidate,n.root,n.pathApi))return`Blocked: MCP tool "${t}" path "${r.value}" is outside the workspace boundary "${n.root}"`}return null}function T(t,e="",o=0){if(o>8)return[];if(typeof t=="string")return Ne(e,t)?[{key:e,value:t}]:[];if(Array.isArray(t))return t.flatMap(n=>T(n,e,o+1));if(!t||typeof t!="object")return[];let r=[];for(let[n,i]of Object.entries(t))r.push(...T(i,n,o+1));return r}function Ne(t,e){if(!Le.test(t))return!1;let o=e.trim();return!o||/^https?:\/\//i.test(o)?!1:$e(o)}function $e(t){return d.isAbsolute(t)||/^[A-Za-z]:[\\/]/.test(t)||/^\\\\/.test(t)||/^file:\/\//i.test(t)||t.includes("../")||t.includes("..\\")||t.startsWith(".\\")||t.startsWith("./")||t.includes("/")||t.includes("\\")}function qe(t,e){let o=t.trim();if(/^file:\/\//i.test(o))try{o=Re(o)}catch{return null}let r=W(o)||W(e)?d.win32:d,n=r.resolve(e);return{candidate:r.isAbsolute(o)?r.resolve(o):r.resolve(n,o),root:n,pathApi:r}}function Ue(t,e,o){let r=o.relative(e,t);return r===""||!r.startsWith("..")&&!o.isAbsolute(r)}function W(t){return/^[A-Za-z]:[\\/]/.test(t)||/^\\\\/.test(t)}function J(t){if(!t||typeof t!="object")return[];let e=t,o=[],r=e.mcpServers??e.servers??e;for(let[n,i]of Object.entries(r)){if(!i||typeof i!="object")continue;let s=i;if(typeof s.url=="string"){o.push({name:n,type:"http",url:s.url,headers:s.headers&&typeof s.headers=="object"?s.headers:void 0,disabled:s.disabled===!0,oauth:s.oauth===!0,tools:B(s.tools)});continue}typeof s.command=="string"&&o.push({name:n,type:"stdio",command:s.command,args:Array.isArray(s.args)?s.args:void 0,env:s.env&&typeof s.env=="object"?s.env:void 0,cwd:typeof s.cwd=="string"?s.cwd:void 0,disabled:s.disabled===!0,oauth:s.oauth===!0,tools:B(s.tools)})}return o}function z(t,e){let o=new Set(e?.include??[]),r=new Set(e?.exclude??[]);return t.filter(n=>o.size>0&&!o.has(n.name)?!1:!r.has(n.name))}function B(t){if(!t||typeof t!="object")return;let e=t,o=H(e.include),r=H(e.exclude);if(!(!o&&!r))return{...o?{include:o}:{},...r?{exclude:r}:{}}}function H(t){if(!Array.isArray(t))return;let e=t.filter(o=>typeof o=="string"&&o.trim().length>0);return e.length>0?e:void 0}var Fe=new Set(["bash","sh","zsh","fish","cmd","cmd.exe","powershell","powershell.exe","pwsh","pwsh.exe"]),Ge=new Set(["-c","/c","-command","-encodedcommand","-e"]),We=["curl","wget","invokewebrequest","invoke-restmethod","iwr","irm","nc","netcat"],ze=["env","processenv","apikey","api_key","token","secret","credential","credentials","awsaccesskey","awssecret"],Be=["authorizedkeys","sshknownhosts","crontab","launchctl","schtasks","startup","runkey","runonce","bashrc","profile"],He=/\bssh-(?:rsa|ed25519|ecdsa-[^\s]+)\s+[A-Za-z0-9+/=]{40,}/i;function Je(t){let e=[],o=[t.command??"",...t.args??[],...Object.keys(t.env??{}),...Object.values(t.env??{})].join(`
6
+ `);if(He.test(o)&&e.push({code:"ioc_public_key",message:"configuration contains an inline SSH public key IOC"}),(t.type??(t.url?"http":"stdio"))!=="stdio"||!Ve(t.command))return e;let r=Ke(t.args??[]);if(!r)return e;let n=Qe(r);return w(n,Be)&&e.push({code:"persistence",message:"stdio shell command attempts a persistence write"}),w(n,We)&&w(n,ze)&&e.push({code:"dangerous_egress",message:"stdio shell command combines network egress with likely secret access"}),e}function Ve(t){if(!t)return!1;let e=d.basename(t).toLowerCase();return Fe.has(e)}function Ke(t){for(let e=0;e<t.length;e++){let o=t[e]?.toLowerCase();if(o&&Ge.has(o))return t.slice(e+1).join(" ")}return""}function Qe(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"")}function w(t,e){return e.some(o=>t.includes(o))}var et=[{name:"skills_list",description:"List AstraClaw skills active for the current workspace.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"skill_view",description:"Read the full SKILL.md instructions for one AstraClaw skill.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Skill name from skills_list."}},required:["name"],additionalProperties:!1}},{name:"mcp_connectors_list",description:"List MCP connector tools installed in AstraClaw's Plugins page and shared with this external agent.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"mcp_tool_call",description:"Call one AstraClaw shared MCP connector tool by toolName from mcp_connectors_list.",inputSchema:{type:"object",properties:{toolName:{type:"string",description:"Connector tool name, for example mcp__filesystem__read_text_file."},args:{type:"object",description:"Arguments for the connector tool."}},required:["toolName"],additionalProperties:!1}}],S=class{tools=new Map;findTool(e){return this.tools.get(e)}getToolManifest(){return Array.from(this.tools.values()).map(e=>({type:"function",function:{name:e.name,description:e.description,parameters:e.parameters}}))}getToolNames(){return Array.from(this.tools.keys())}setTools(e){this.tools.clear(),this.addTools(e)}addTool(e){this.tools.set(e.name,e)}addTools(e){for(let o of e)this.addTool(o)}removeTool(e){return this.tools.delete(e)}},m=null,tt=Ye({input:process.stdin,crlfDelay:Number.POSITIVE_INFINITY});tt.on("line",t=>{ot(t)});async function ot(t){let e=t.trim();if(!e)return;let o;try{o=JSON.parse(e)}catch{return}if(o.id===void 0||o.id===null)return o.method==="notifications/initialized",void 0;try{let r=await rt(o);K({jsonrpc:"2.0",id:o.id,result:r})}catch(r){K({jsonrpc:"2.0",id:o.id,error:{code:-32603,message:r instanceof Error?r.message:String(r)}})}}async function rt(t){switch(t.method){case"initialize":return{protocolVersion:"2024-11-05",capabilities:{tools:{}},serverInfo:{name:j,version:"1.0.0"}};case"tools/list":return{tools:et};case"tools/call":{let e=V(t.params),o=typeof e.name=="string"?e.name:"",r=V(e.arguments),n=nt(),i=o==="mcp_connectors_list"||o==="mcp_tool_call"?await st(n):void 0,s=await L({tool:o,args:r,projectRoot:n,toolCatalog:i});if(!s.handled)throw new Error(`Unknown tool: ${o}`);return{content:[{type:"text",text:JSON.stringify(s.result)}]}}default:throw new Error(`Unknown method: ${t.method}`)}}function nt(){return process.env.ASTRACLAW_PROJECT_ROOT||process.env.QLOGICAGENT_PROJECT_ROOT||process.cwd()}async function st(t){if(m?.projectRoot===t)return await m.ready,m.catalog;m?.manager?.disconnectAll().catch(()=>{});let e=new S,o=new f({servers:it(),workspaceRoot:t,toolCatalog:e,log:{info:n=>process.stderr.write(`${n}
6
7
  `),warn:n=>process.stderr.write(`${n}
7
- `)}}),r=o.connectAll().then(()=>o.injectTools());return m={projectRoot:t,catalog:e,manager:o,ready:r},await r,e}function st(){let t=process.env.ASTRACLAW_MCP_CONFIG_PATH||P();if(!Ye(t))return[];try{let e=Ze(t,"utf8").replace(/^\uFEFF/,"");return J(JSON.parse(e))}catch(e){return process.stderr.write(`[astraclaw-native-mcp] failed to read MCP config: ${e instanceof Error?e.message:String(e)}
8
+ `)}}),r=o.connectAll().then(()=>o.injectTools());return m={projectRoot:t,catalog:e,manager:o,ready:r},await r,e}function it(){let t=process.env.ASTRACLAW_MCP_CONFIG_PATH||P();if(!Ze(t))return[];try{let e=Xe(t,"utf8").replace(/^\uFEFF/,"");return J(JSON.parse(e))}catch(e){return process.stderr.write(`[astraclaw-native-mcp] failed to read MCP config: ${e instanceof Error?e.message:String(e)}
8
9
  `),[]}}function V(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:{}}function K(t){process.stdout.write(`${JSON.stringify(t)}
9
10
  `)}
@@ -64,6 +64,12 @@ export interface ToolLoopParams {
64
64
  * or the same array reference if unchanged.
65
65
  */
66
66
  refreshTools?: () => ToolDefinition[];
67
+ /**
68
+ * Drain user guidance queued MID-TURN (队列"运行中引导"). Called at the top of
69
+ * every loop round; each returned string is appended as a user message before
70
+ * the next LLM call, steering the run without waiting for the turn to finish.
71
+ */
72
+ drainPendingGuidance?: () => string[];
67
73
  /**
68
74
  * Post-sampling hooks -fire-and-forget after LLM response completes (CC executePostSamplingHooks parity).
69
75
  * Cannot modify messages. Used for background tasks (memory extraction, skill learning, etc.).
@@ -88,6 +88,11 @@ export interface TurnRequest {
88
88
  * become callable in the SAME turn instead of only on the next user message.
89
89
  */
90
90
  refreshTools?: () => ToolDefinition[];
91
+ /**
92
+ * Drain user guidance queued mid-turn (运行中引导). Consulted at the top of every
93
+ * tool-loop round; returned strings become user messages before the next LLM call.
94
+ */
95
+ drainPendingGuidance?: () => string[];
91
96
  systemPrompt?: string;
92
97
  config?: TurnConfig;
93
98
  }
@@ -69,6 +69,15 @@ export declare function handleAgentsProcesses(this: AgentsHandlerHost, msg: Agen
69
69
  export declare function handleAgentsKill(this: AgentsHandlerHost, msg: AgentRpcRequest): void;
70
70
  export declare function handleAgentsGetLog(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
71
71
  export declare function handleAgentsTestConnection(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
72
+ /** Formats a session persisted transcript into a compact prior-conversation block for an external
73
+ * ACP agent that was cold-spawned (fresh process, empty session). Mirrors the built-in agent
74
+ * withResumedHistory: the engine on-disk history is authoritative, so we re-supply it and the agent
75
+ * regains full context after a restart/eviction. Only user/assistant text turns are included;
76
+ * returns "" when there is no prior history (e.g. the first turn of a new session). */
77
+ export declare function buildExternalHistoryPreamble(messages: Array<{
78
+ role: string;
79
+ content: unknown;
80
+ }>): string;
72
81
  /** Pre-warm an external agent's process (on agent-select) so the first message skips the cold start. */
73
82
  export declare function handleAgentsWarm(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
74
83
  export declare function handleAgentsPrompt(this: AgentsHandlerHost, msg: AgentRpcRequest): Promise<void>;
@@ -7,6 +7,7 @@
7
7
  * config.get/config.update only operate on tunables and non-model settings.
8
8
  */
9
9
  import { type AgentRpcError, type AgentRpcRequest } from "../../protocol/wire/index.js";
10
+ import type { PermissionMode } from "../../runtime/ports/index.js";
10
11
  import type { BackgroundTaskManager } from "../../runtime/infra/background-tasks.js";
11
12
  import type { TaskStore } from "../../runtime/infra/task-runtime.js";
12
13
  import type { ToolCatalog } from "../../runtime/ports/index.js";
@@ -15,6 +16,10 @@ export interface ConfigHandlerHost {
15
16
  backgroundTasks: BackgroundTaskManager;
16
17
  toolCatalog?: ToolCatalog;
17
18
  syncToolListMediaConfig?(): void;
19
+ /** 权限热应用:config.update 写盘后立即同步活的 rule engine(否则要等下一轮 _meta/重启)。 */
20
+ applyPermissionConfig?(perm: {
21
+ mode: PermissionMode;
22
+ }): void;
18
23
  sendResponse(id: string | number, result?: unknown, error?: AgentRpcError): void;
19
24
  }
20
25
  export declare function handleToolsList(this: ConfigHandlerHost, msg: AgentRpcRequest): void;
@@ -13,6 +13,13 @@ export interface FilesHandlerHost {
13
13
  export declare function resolveProjectDir(projectId: string): string | null;
14
14
  export declare function handleFilesList(this: FilesHandlerHost, msg: AgentRpcRequest): Promise<void>;
15
15
  export declare function handleFilesCreate(this: FilesHandlerHost, msg: AgentRpcRequest): Promise<void>;
16
+ /**
17
+ * files.readLocal — read an ABSOLUTE local path for chat file-reference preview.
18
+ * Workspace-relative reads keep going through files.read; this endpoint serves the
19
+ * "user/agent mentioned a file outside every project workspace" preview gap (the
20
+ * media proxy only serves media extensions, so .txt/.log etc. had no channel).
21
+ */
22
+ export declare function handleFilesReadLocal(this: FilesHandlerHost, msg: AgentRpcRequest): Promise<void>;
16
23
  export declare function handleFilesRead(this: FilesHandlerHost, msg: AgentRpcRequest): Promise<void>;
17
24
  export declare function handleFilesRename(this: FilesHandlerHost, msg: AgentRpcRequest): Promise<void>;
18
25
  export declare function handleFilesDelete(this: FilesHandlerHost, msg: AgentRpcRequest): Promise<void>;
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { SessionState } from "../../runtime/session/session-state.js";
14
14
  import { createMemoryPrefetchState } from "../../runtime/hooks/memory-hooks.js";
15
+ import { type NativeSessionEntry } from "../../runtime/session/native/native-session-source.js";
15
16
  import { type AgentRpcError, type AgentRpcErrorCode, type AgentRpcRequest } from "../../protocol/wire/index.js";
16
17
  import type { SessionHistoryCoordinator } from "../session-history-coordinator.js";
17
18
  export interface SessionHandlerHost {
@@ -32,7 +33,49 @@ export declare function handleThreadCreate(this: SessionHandlerHost, msg: AgentR
32
33
  export declare function handleThreadList(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
33
34
  export declare function handleSessionCreate(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
34
35
  export declare function handleSessionResolve(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
36
+ /**
37
+ * Resolve an agentId's sessionHistory capability ("native-cwd" vs "own") for session.list routing.
38
+ *
39
+ * Mirrors the hard invariant already encoded in {@link assembleBackendDescriptor} (acp-detector.ts):
40
+ * only codex/claude are adapted for native CWD-scoped session discovery — every other agentId,
41
+ * including undefined/missing (the default for all existing callers), custom agents, and unknown
42
+ * ids, resolves to "own". This is intentionally NOT a live/async descriptor lookup (that would
43
+ * require an AcpDetector scan — CLI probing — on the session.list hot path); it keys off the static
44
+ * ACP_BACKENDS registry so the two places can never drift on WHICH ids are native-adapted, without
45
+ * paying scan cost. Cut 1 does not distinguish installed vs not-installed codex/claude here (unlike
46
+ * assembleBackendDescriptor's not_installed branch) because resolveSessionSource's "no registered
47
+ * adapter" fallback already yields [] for native-cwd either way — the distinction has no observable
48
+ * effect until Cut 2 lands a real adapter.
49
+ */
50
+ export declare function resolveAgentSessionHistory(agentId?: string): "native-cwd" | "own";
51
+ /**
52
+ * Select the session source for session.list: "own" (default, includes missing/unknown agentId)
53
+ * returns the caller's already-assembled own entries UNCHANGED (same reference — zero-regression
54
+ * contract for every existing caller, none of which pass agentId today); "native-cwd" routes to a
55
+ * registered {@link NativeSessionSource} for that agentId, or an empty list when none is registered
56
+ * yet (Cut 1: always, until Cut 2 lands codex/claude adapters) or cwd is unavailable.
57
+ */
58
+ export declare function resolveSessionSource<T>(args: {
59
+ agentId?: string;
60
+ capability?: "native-cwd" | "own";
61
+ cwd?: string;
62
+ limit?: number;
63
+ ownEntries: T[];
64
+ }): Promise<T[] | NativeSessionEntry[]>;
35
65
  export declare function handleSessionList(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
66
+ /**
67
+ * Loads the full transcript of a single native (third-party-agent-owned) session, identified by
68
+ * the opaque `nativeRef` a prior session.list call returned in a `source: "native"` entry. Read-only
69
+ * and idempotent — the frontend calls this when a user opens a native session in the UI.
70
+ *
71
+ * Unlike handleSessionList (which falls back to [] on ANY unresolvable state — missing capability,
72
+ * missing cwd, no registered adapter), missing/unregistered here has exactly one meaning (no adapter
73
+ * registered for that agentId, or no nativeRef was sent at all) and resolves the same way: an empty
74
+ * transcript, NOT an error. Only an actual adapter failure (loadTranscript rejecting) is an error —
75
+ * that distinction matters because "no adapter yet" is an expected state during rollout (e.g. Cut 2
76
+ * ships claude only; codex nativeRefs would still 404-equivalent to [] until Cut 3), not a bug.
77
+ */
78
+ export declare function handleSessionLoadNativeTranscript(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
36
79
  export declare function handleSessionGet(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
37
80
  export declare function handleSessionGetMessages(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
38
81
  export declare function handleSessionUpdate(this: SessionHandlerHost, msg: AgentRpcRequest): Promise<void>;
@@ -18,7 +18,19 @@ export interface TurnControlHandlerHost {
18
18
  configureTurnMedia?(config: Record<string, unknown> | undefined, turnId: string): void;
19
19
  log(message: string): void;
20
20
  sendResponse(id: string | number, result?: unknown, error?: unknown): void;
21
+ /** Mid-turn guidance queue (运行中引导) — drained by the tool loop each round. */
22
+ pendingTurnGuidance?: string[];
23
+ currentSessionId?: string;
24
+ currentTurnId?: string;
25
+ getActiveProjectRoot?(): string;
21
26
  }
27
+ /**
28
+ * turn.guide — queue user guidance INTO the running turn (队列"运行中引导").
29
+ * The text lands in the tool loop's next round as a user message, and is
30
+ * persisted to the session transcript so later turns keep it in history.
31
+ * Fails when no turn is active (caller falls back to the plain queue).
32
+ */
33
+ export declare function handleTurnGuide(this: TurnControlHandlerHost, msg: AgentRpcRequest): void;
22
34
  /**
23
35
  * First-turn session-title generation, extracted so the caller can run it
24
36
  * fire-and-forget (see run()). Behavior is identical to the previous inline
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Persist "始终允许此文件夹" approvals into the USER-level settings.json
3
+ * (`~/.qlogicagent/profiles/<owner>/settings.json` → permissions.rules).
4
+ *
5
+ * Same file config.update writes and the settings-watcher's user layer reads,
6
+ * so a persisted rule survives restarts via loadInitialSettings/watchSettings.
7
+ * turn-permission-sync leaves rules alone when a frame carries only `{ mode }`,
8
+ * so desktop turns cannot wipe these entries.
9
+ */
10
+ /**
11
+ * Append an allow rule for `pathPrefix` to the user settings, preserving the rest
12
+ * of the file. Idempotent: an existing allow rule with the same prefix is kept as-is.
13
+ */
14
+ export declare function persistUserDirectoryAllowRule(rule: {
15
+ pathPrefix: string;
16
+ }): Promise<void>;
@@ -3,6 +3,9 @@ import type { ModelPurpose } from "../runtime/infra/model-registry.js";
3
3
  import type { RpcHandlerHosts } from "./rpc-registry.js";
4
4
  type DirectStdioRpcHost = RpcHandlerHosts["config"] & RpcHandlerHosts["control"] & RpcHandlerHosts["files"] & RpcHandlerHosts["skills"] & RpcHandlerHosts["project"] & RpcHandlerHosts["community"] & RpcHandlerHosts["settings"] & RpcHandlerHosts["petConfirm"] & {
5
5
  currentTurnId?: string;
6
+ /** Mid-turn guidance queue (运行中引导) — shared between handleTurnGuide and the tool loop's drain. */
7
+ pendingTurnGuidance?: string[];
8
+ getActiveProjectRoot(): string;
6
9
  readonly multiAgentHandlerHost: RpcHandlerHosts["multiAgent"];
7
10
  readonly workflowHandlerHost: RpcHandlerHosts["workflow"];
8
11
  };
@@ -39,6 +42,7 @@ type StdioRpcAdapterSource = DirectStdioRpcHost & {
39
42
  sendError: NonNullable<RpcHandlerHosts["session"]["sendError"]>;
40
43
  createDreamHandlerHost(): RpcHandlerHosts["dream"];
41
44
  };
45
+ export declare function createTurnHost(host: StdioRpcAdapterSource): RpcHandlerHosts["turn"];
42
46
  /**
43
47
  * Build the per-domain handler-host bundle the RPC registry binds to.
44
48
  *
@@ -25,7 +25,7 @@ import { type MultiAgentHandlerHost } from "./multi-agent-handler-host.js";
25
25
  import type { WorkflowHandlerHost } from "./handlers/workflow-handler.js";
26
26
  import { SessionHistoryCoordinator } from "./session-history-coordinator.js";
27
27
  import { PetConfirmCoordinator } from "./pet-confirm-coordinator.js";
28
- import type { MemoryDreamRuntimeProvider, MemoryHandlerProvider, ProjectMemoryStore } from "../runtime/ports/index.js";
28
+ import type { MemoryDreamRuntimeProvider, MemoryHandlerProvider, PermissionMode, ProjectMemoryStore } from "../runtime/ports/index.js";
29
29
  export interface StdioServerConfig {
30
30
  verbose: boolean;
31
31
  transport?: Transport;
@@ -106,6 +106,13 @@ export declare class StdioServer {
106
106
  set currentSessionId(v: string);
107
107
  get currentTurnId(): string;
108
108
  set currentTurnId(v: string);
109
+ /**
110
+ * Mid-turn guidance queue (运行中引导). Reset per turn by runTurnPipeline, pushed by
111
+ * handleTurnGuide (via the turn host proxy), drained each round by the tool loop.
112
+ * Declared as a real field so the proxy in createTurnHost has a shared backing store
113
+ * (turn pipeline and handleTurnGuide must see the SAME array).
114
+ */
115
+ pendingTurnGuidance: string[];
109
116
  /** LH-10: an explicit cancel of a session stops the background tasks that
110
117
  * session launched — the forked agent + the exec processes it spawned —
111
118
  * instead of leaving them running as zombies. Keyed by session id, which is
@@ -121,6 +128,14 @@ export declare class StdioServer {
121
128
  get currentHooks(): import("../contracts/hooks.js").HookRegistry | null;
122
129
  get permissionChecker(): AgentRuntimeSessionState["permissionChecker"];
123
130
  private set permissionChecker(value);
131
+ /**
132
+ * config.update 权限热应用:写盘的同时立即更新活的 rule engine,
133
+ * 当前会话(乃至运行中 turn)的下一次工具调用就按新模式判定——
134
+ * 不再等下一轮 _meta 或重启。settings-watcher 仍监听文件作为外部改动兜底。
135
+ */
136
+ applyPermissionConfig(perm: {
137
+ mode: PermissionMode;
138
+ }): void;
124
139
  private set mcpManager(value);
125
140
  get memoryProvider(): MemoryHandlerProvider | null;
126
141
  set memoryProvider(value: MemoryHandlerProvider | null);
@@ -16,6 +16,8 @@ export interface TurnCoreRequest {
16
16
  tools: ToolDefinition[];
17
17
  /** Live tool-manifest rebuild for same-turn activation (forwarded to the loop). */
18
18
  refreshTools?: () => ToolDefinition[];
19
+ /** Mid-turn guidance drain (运行中引导) — forwarded to the tool loop. */
20
+ drainPendingGuidance?: () => string[];
19
21
  systemPrompt: string;
20
22
  config: TurnConfig;
21
23
  }
@@ -121,6 +121,10 @@ export declare class ProductOrchestrator {
121
121
  */
122
122
  replayTask(productId: string, taskId: string): Promise<void>;
123
123
  /** Get product status. */
124
+ /** P2-1: the native (codex/claude) session id captured for a delegation's single worker, so the
125
+ * DelegationCoordinator can resume it on the next same-(chat×agent) delegation. undefined for a
126
+ * multi-instance product, an own agent, or before the worker's first task settles. */
127
+ getDelegationNativeSessionId(productId: string): string | undefined;
124
128
  getStatus(productId: string): ProductStatus | null;
125
129
  /** Live DAG progress snapshot for a product (for the planner's execution-phase leader). */
126
130
  getProgressSnapshot(productId: string): {
@@ -15,6 +15,16 @@ type DelegationStatus = "running" | "completed" | "failed" | "cancelled";
15
15
  export declare class DelegationCoordinator {
16
16
  private readonly deps;
17
17
  private readonly active;
18
+ /** Per-(chat×agent) tail promise — the next same-conversation delegation chains after it so they run
19
+ * strictly sequentially (single writer to the shared reused worktree + native session). One entry per
20
+ * active (chat×agent) pair (keyed, overwritten — not per-delegation), so it stays bounded. */
21
+ private readonly convChains;
22
+ /** Per-(chat×agent) native (codex/claude) session id — the background analogue of the foreground U5
23
+ * convergence. Seeded from the first delegation's captured session id; passed as resumeNativeSessionId on
24
+ * every subsequent same-conversation delegation so context accumulates (git AND non-git projects).
25
+ * In-memory (one entry per active pair); an engine restart resets it → the next delegation starts fresh,
26
+ * same as a cold foreground turn whose pool was evicted. */
27
+ private readonly nativeSessions;
18
28
  constructor(deps: DelegationDeps);
19
29
  /** Active + recent delegations for this project (x/mention.list). */
20
30
  list(): {
@@ -30,6 +30,10 @@ export interface PersistedProductState {
30
30
  * PlanningSession is strings / arrays / plain objects (no functions, dates, or class instances).
31
31
  */
32
32
  leaderDialogue?: PlanningSession;
33
+ /** P2-1: run tasks in the project cwd instead of per-task worktrees (see
34
+ * ProductCreateParams.runInProjectCwd) — persisted so a delegation resumed after an engine restart
35
+ * keeps running in the project cwd, not a fresh isolated worktree. */
36
+ runInProjectCwd?: boolean;
33
37
  }
34
38
  export interface ProductPersistenceOptions {
35
39
  pathService?: PathService;
@@ -337,6 +337,22 @@ export interface ProductCreateParams {
337
337
  maxTotalTokens?: number;
338
338
  maxDuration?: number;
339
339
  };
340
+ /**
341
+ * P2-1: run ALL tasks directly in the project cwd (`cwd`) instead of isolated per-task git worktrees.
342
+ * Set by DelegationCoordinator so a background @mention shares the project workspace exactly like
343
+ * foreground @codex — its native session then lives under a STABLE cwd (git or not) and resumes across
344
+ * delegations. Omitted everywhere else (per-task worktree isolation stays the default for parallel
345
+ * product/goal/solo tasks).
346
+ */
347
+ runInProjectCwd?: boolean;
348
+ /**
349
+ * P2-1: native (codex/claude) session id to RESUME for this delegation (ACP session/load), so repeated
350
+ * background @mention to the same (chat×agent) accumulates context — the background analogue of the
351
+ * foreground U4 resume. The DelegationCoordinator tracks it per (chat×agent): undefined on the first
352
+ * delegation (→ fresh session, whose id it then captures), the prior id thereafter. Validated by `has()`
353
+ * before use, so a stale id falls back to a fresh session. Git-independent (id, not cwd, drives reuse).
354
+ */
355
+ resumeNativeSessionId?: string;
340
356
  }
341
357
  /**
342
358
  * product.plan RPC params — starts an interactive planning session.
@@ -20,6 +20,8 @@ export interface AgentCapabilities {
20
20
  supportsResume: boolean;
21
21
  supportsUsageUpdate: boolean;
22
22
  skillsDirs?: string[];
23
+ /** 会话历史来源:native-cwd=有本地按 cwd 可发现的原生会话;省略/own=只显示自有会话 */
24
+ sessionHistory?: "native-cwd" | "own";
23
25
  }
24
26
  /** Descriptor for a discovered or registered agent. */
25
27
  export interface AgentDescriptor {
@@ -719,6 +721,7 @@ export interface AgentRpcMethodMap extends GatewayRpcMethodMap {
719
721
  params: {
720
722
  modelId: string;
721
723
  enabled: boolean;
724
+ purpose?: string;
722
725
  };
723
726
  result: {
724
727
  ok: true;
@@ -6,13 +6,14 @@
6
6
  *
7
7
  * Reference: AionUI AcpDetector + ACP_BACKENDS_ALL registry.
8
8
  */
9
- import type { AcpBackendConfig, AgentDescriptor, AgentConfigStoreData, AuthPreference } from "../../protocol/wire/acp-agent-management.js";
9
+ import type { AcpBackendConfig, AgentDescriptor, CustomAgentDef, AgentConfigStoreData, AuthPreference } from "../../protocol/wire/acp-agent-management.js";
10
10
  export declare const ACP_BACKENDS: Record<string, AcpBackendConfig>;
11
11
  export declare const AGENT_CATALOG_IDS: readonly ["claude", "codex", "gemini", "copilot", "cursor", "qwen", "kimi", "glm", "opencode", "kiro", "qoder", "openclaw", "hermes"];
12
12
  /** Merged catalog entry (base ACP config + grid/install metadata) for one id. */
13
13
  export declare function getCatalogEntry(id: string): AcpBackendConfig | undefined;
14
14
  /** Ordered curated catalog: base ACP config merged with grid/install metadata. */
15
15
  export declare function getAgentCatalog(): AcpBackendConfig[];
16
+ export declare function buildInternalAgentDescriptors(): AgentDescriptor[];
16
17
  export declare function resolveWindowsCopilotLoader(): string | null;
17
18
  export declare function getNpmInstallPackageName(command: string | undefined): string | null;
18
19
  export declare function compareSemverVersions(a: string, b: string): number;
@@ -134,6 +135,24 @@ export declare function resolveCatalogKeyEnv(entry: AcpBackendConfig | undefined
134
135
  * Returns [] when not in account mode (or the agent has no account path — claude is coerced to auto).
135
136
  */
136
137
  export declare function resolveSuppressEnvKeys(entry: AcpBackendConfig | undefined, mode: AuthPreference, sources?: CatalogKeySources): string[];
138
+ /**
139
+ * Build a backend's descriptor from already-resolved cliPath/version (+ the caller-resolved
140
+ * hasConfig flag). Shared by AcpDetector's sync and async detection paths so they can never drift
141
+ * (status rules, copilot's version-required gate, etc.) — extracted to a standalone, IO-free, pure
142
+ * function (not a class method) so it is directly unit-testable without spawning a real CLI.
143
+ *
144
+ * sessionHistory (Task 1.2, third-party native session discovery): "native-cwd" only for an
145
+ * INSTALLED (status "available") codex or claude backend — the two agents whose native CWD-scoped
146
+ * session stores we've actually adapted to. Every other case — not-installed codex/claude, any other
147
+ * backend regardless of install state — falls back to "own". Unadapted agents ALWAYS get "own"; this
148
+ * is a hard invariant, not a default that later widens implicitly.
149
+ */
150
+ export declare function assembleBackendDescriptor(backend: AcpBackendConfig, cliPath: string | null, version: string | null, hasConfig: boolean): AgentDescriptor;
151
+ /** Build a user-registered custom agent's descriptor. Extracted to a standalone, IO-free, pure
152
+ * function for the same testability reason as {@link assembleBackendDescriptor}. Custom agents are
153
+ * never adapted for native session discovery — regardless of what id/name the user gives them —
154
+ * so sessionHistory is always "own" (the hard own-by-default invariant). */
155
+ export declare function assembleCustomAgentDescriptor(custom: CustomAgentDef, cliPath: string | null, hasConfig: boolean): AgentDescriptor;
137
156
  export declare class AcpDetector {
138
157
  private cache;
139
158
  private configStore;
@@ -176,13 +195,9 @@ export declare class AcpDetector {
176
195
  private detectBackend;
177
196
  /** Async, NON-BLOCKING twin of {@link detectBackend} — identical detection, async probes. */
178
197
  private detectBackendAsync;
179
- /** Build a backend's descriptor from already-resolved cliPath/version. Shared by the sync and async
180
- * detection paths so they can never drift (status rules, copilot's version-required gate, etc.). */
181
- private assembleBackendDescriptor;
182
198
  private detectCustomAgent;
183
199
  /** Async, NON-BLOCKING twin of {@link detectCustomAgent}. */
184
200
  private detectCustomAgentAsync;
185
- private assembleCustomAgentDescriptor;
186
201
  private hasAgentConfig;
187
202
  /**
188
203
  * Descriptor for qlogicagent participating in Solo/Product as a self-hosted ACP agent.
@@ -201,6 +216,21 @@ export declare class AcpDetector {
201
216
  * Returns null if the agent is not available.
202
217
  */
203
218
  buildExternalDescriptor(agentId: string): import("../../protocol/wire/acp-agent-management.js").ExternalAgentDescriptor | null;
219
+ /**
220
+ * Async, NON-BLOCKING twin of {@link buildExternalDescriptor} — identical output for the same
221
+ * detector state, but resolves the agent list via {@link scanAsync} (a real probe, awaited, when
222
+ * the cache is cold) instead of the sync {@link list}'s cache-or-empty shortcut. Load-bearing for
223
+ * `agents.warm` (agents-handler.ts): the sync path's detect calls are cache-fronted today, but
224
+ * that non-blocking guarantee is an implementation detail of `list()` — routing the RPC path
225
+ * through this method instead makes "never synchronously probe on this path" an explicit
226
+ * contract that can't silently regress if `list()`'s internals change later.
227
+ */
228
+ buildExternalDescriptorAsync(agentId: string): Promise<import("../../protocol/wire/acp-agent-management.js").ExternalAgentDescriptor | null>;
229
+ /** Shared descriptor-assembly logic for {@link buildExternalDescriptor} and
230
+ * {@link buildExternalDescriptorAsync} — everything AFTER "locate this agentId's AgentDescriptor
231
+ * in an already-resolved list", so the sync/async twins can never drift on behavior, only on how
232
+ * they obtain `agents`. */
233
+ private assembleExternalDescriptorFromAgents;
204
234
  /**
205
235
  * Whether an agent is ready to compete in Solo/Product: installed/available AND configured.
206
236
  * qlogicagent self-resolves from the owner profile (always ready). External agents need a usable
@@ -151,7 +151,20 @@ export declare class AcpProtocolAdapter {
151
151
  mcpServers?: McpServerConfig[];
152
152
  additionalDirectories?: string[];
153
153
  systemPrompt?: string;
154
+ /** Platform/managed model to select over ACP after session/new (see selectSessionModel). */
155
+ model?: string;
154
156
  }): Promise<AcpSessionResult>;
157
+ /**
158
+ * Select the platform/managed model over ACP after session/new. External CLIs (notably
159
+ * claude-code-acp) default to their OWN model (e.g. Sonnet 4.5) and merely EXPOSE the injected
160
+ * ANTHROPIC_MODEL/OPENAI_MODEL as an "available" option — they do NOT select it. Left on the default,
161
+ * the first prompt fails ("issue with the selected model …") because the platform shelf has no such
162
+ * model, and it surfaces as a cryptic "Agent process stdio closed". Best-effort: when the agent
163
+ * advertised its models, only switch if it offers `desired` and isn't already on it; otherwise attempt
164
+ * anyway and swallow rejection (the session still exists on its default model — the wrong-model prompt
165
+ * error is the backstop). Uses the generic sendRequest because the SDK has no typed set_model yet.
166
+ */
167
+ private selectSessionModel;
155
168
  /**
156
169
  * Send a prompt to a running ACP session (session/prompt). Content streams back via session/update
157
170
  * notifications during execution; the response carries only { stopReason, usage? }. On timeout the
@@ -165,6 +178,7 @@ export declare class AcpProtocolAdapter {
165
178
  resumeSession(_child: ChildProcess, sessionId: string, options?: {
166
179
  cwd?: string;
167
180
  mcpServers?: McpServerConfig[];
181
+ model?: string;
168
182
  }): Promise<AcpSessionResult>;
169
183
  /**
170
184
  * Translate an ACP notification into qlogicagent's internal format.
@@ -52,6 +52,14 @@ export interface AgentProcessConfig {
52
52
  mcpServers?: McpServerConfig[];
53
53
  /** System prompt to inject into ACP session. */
54
54
  systemPrompt?: string;
55
+ /**
56
+ * Cut 5 — RESUME a third-party native session (ACP session/load, replays history) instead of
57
+ * creating a fresh one. The id is the agent's OWN native session id (codex rollout id / claude
58
+ * UUID). Only honored when the agent advertised loadSession at initialize (handle.supportsResume);
59
+ * otherwise spawn throws — we never silently open a blank session ("fake continuation" is forbidden
60
+ * by the Phase 2 design).
61
+ */
62
+ resumeSessionId?: string;
55
63
  }
56
64
  export declare function buildExternalAcpSpawnCommand(cliPath: string, acpArgs: string[]): {
57
65
  command: string;
@@ -296,6 +304,13 @@ export declare class AgentProcessManager {
296
304
  isAlive(memberId: string): boolean;
297
305
  /** Get usage tracker for an ACP agent (returns null for internal agents). */
298
306
  getUsageTracker(memberId: string): AcpUsageTracker | null;
307
+ /**
308
+ * The agent-assigned native session id (from ACP session/new or session/load) for a pooled member,
309
+ * once its session exists — undefined before creation. For native-cwd agents (codex/claude) this is
310
+ * the id under which the agent persists its OWN transcript (e.g. claude's ~/.claude uuid), distinct
311
+ * from the engine/stream id; used to converge on a single session identity (own/native unification).
312
+ */
313
+ getNativeSessionId(memberId: string): string | undefined;
299
314
  /**
300
315
  * Feed token usage into the tracker from a child notification (Tier 1). Handles all three shapes a
301
316
  * child may use so the tracker is actually populated: