@truesayer/node 0.1.0
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/README.md +37 -0
- package/dist/node.js +34 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @truesayer/node
|
|
2
|
+
|
|
3
|
+
`truesayer-node` is the local execution runtime for Truesayer. It runs on your
|
|
4
|
+
machine, connects outbound to a trusted Truesayer Server, and executes assigned
|
|
5
|
+
agent runs with Node-local provider credentials.
|
|
6
|
+
|
|
7
|
+
Requires Node.js 22 or newer.
|
|
8
|
+
|
|
9
|
+
Install and run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @truesayer/node
|
|
13
|
+
|
|
14
|
+
truesayer-node \
|
|
15
|
+
--server https://truesayer.ai \
|
|
16
|
+
--api-key sk_node_xxx \
|
|
17
|
+
--node-name office-mac-mini \
|
|
18
|
+
--node-home ~/.truesayer-node
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The same configuration can be supplied with environment variables:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
SERVER_URL=https://truesayer.ai
|
|
25
|
+
NODE_API_KEY=sk_node_xxx
|
|
26
|
+
NODE_NAME=office-mac-mini
|
|
27
|
+
TRUESAYER_NODE_HOME=~/.truesayer-node
|
|
28
|
+
truesayer-node
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Only connect this runtime to a Truesayer Server you trust. Provider API keys,
|
|
32
|
+
provider base URLs, login state, worktrees, and runtime cache stay local to the
|
|
33
|
+
Node process and must not be uploaded to Server.
|
|
34
|
+
|
|
35
|
+
The Node runtime creates and owns `<node-home>/provider.json` as a local secret
|
|
36
|
+
registry for custom providers. Native local providers use login state under
|
|
37
|
+
`<node-home>/runtime-home`.
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";var Nt=Object.create;var ke=Object.defineProperty;var It=Object.getOwnPropertyDescriptor;var Pt=Object.getOwnPropertyNames;var At=Object.getPrototypeOf,xt=Object.prototype.hasOwnProperty;var St=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of Pt(t))!xt.call(e,o)&&o!==r&&ke(e,o,{get:()=>t[o],enumerable:!(n=It(t,o))||n.enumerable});return e};var J=(e,t,r)=>(r=e!=null?Nt(At(e)):{},St(t||!e||!e.__esModule?ke(r,"default",{value:e,enumerable:!0}):r,e));var Rt=require("fs"),Tt=require("path");var ht=J(require("os")),N=require("fs"),vt=require("path");var Ee=require("crypto"),S=require("fs"),D=J(require("path")),_e=J(require("simple-git")),Mt=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,z=new Map;function Ct(){let{PAGER:e,GIT_PAGER:t,...r}=process.env;return{...r,GIT_TERMINAL_PROMPT:"0",GIT_AUTHOR_NAME:process.env.TRUESAYER_GIT_AUTHOR_NAME||process.env.GIT_AUTHOR_NAME||"Truesayer",GIT_AUTHOR_EMAIL:process.env.TRUESAYER_GIT_AUTHOR_EMAIL||process.env.GIT_AUTHOR_EMAIL||"truesayer@example.invalid",GIT_COMMITTER_NAME:process.env.TRUESAYER_GIT_COMMITTER_NAME||process.env.GIT_COMMITTER_NAME||"Truesayer",GIT_COMMITTER_EMAIL:process.env.TRUESAYER_GIT_COMMITTER_EMAIL||process.env.GIT_COMMITTER_EMAIL||"truesayer@example.invalid"}}function Ot(e){return(0,_e.default)(e,{trimmed:!0}).env(Ct())}async function I(e,t){return(await Ot(t).raw(e)).trim()}async function Re(e){return I(["rev-parse","--show-toplevel"],e)}function Dt(e){return`agent/work/${Mt.test(e)?e:`action-${e.replace(/[^A-Za-z0-9_-]/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")||"unknown"}`}`}async function Te(e,t){let r=z.get(e)??Promise.resolve(),n=()=>{},o=r.catch(()=>{}).then(()=>new Promise(s=>{n=s}));z.set(e,o),await r.catch(()=>{});try{return await t()}finally{n(),z.get(e)===o&&z.delete(e)}}async function Ut(e,t){try{return await I(["rev-parse","--verify",`refs/heads/${e}`],t),!0}catch{return!1}}async function Ne(e){try{return await I(["rev-parse","--is-inside-work-tree"],e)==="true"}catch{return!1}}function Ft(e){return e instanceof Error?e.message:String(e)}async function we(e,t,r){try{await I(e,r);return}catch(n){if(!Ft(n).includes("already exists")||!(0,S.existsSync)(t))throw n;if(await Ne(t))return;(0,S.rmSync)(t,{recursive:!0,force:!0}),await I(e,r)}}async function Ie(e){let t=Dt(e.actionId),r=await Re(e.repoPath),n=D.default.resolve(e.worktreePath);return Te(r,async()=>{(0,S.existsSync)(n)&&!await Ne(n)&&(0,S.rmSync)(n,{recursive:!0,force:!0}),(0,S.mkdirSync)(D.default.dirname(n),{recursive:!0}),(0,S.existsSync)(n)||(await Ut(t,r)?await we(["worktree","add",n,t],n,r):await we(["worktree","add","-b",t,n,e.baseBranch],n,r));let o=await I(["merge-base",e.baseBranch,t],n);return{branch:t,path:n,baseSha:o,baseRef:e.baseBranch}})}async function Pe(e){return I(["rev-parse","HEAD"],e)}function Y(e){return(0,Ee.createHash)("sha256").update(e).digest("hex")}function X(e){return e.split(`
|
|
3
|
+
`).map(t=>t.trimEnd()).filter(Boolean)}async function Ht(e){return X(await I(["diff","--cached","--name-only"],e)).length>0}async function Z(e,t){let r=["ls-files","--others","--exclude-standard"];return t&&r.push("--",t),X(await I(r,e))}function de(e,t){if(D.default.isAbsolute(t))throw new Error(`Absolute git file paths are not allowed: ${t}`);let r=D.default.resolve(e),n=D.default.resolve(r,t),o=D.default.relative(r,n);if(!(o&&o!==".."&&!o.startsWith(`..${D.default.sep}`)&&!D.default.isAbsolute(o)))throw new Error(`Unsafe git file path outside worktree: ${t}`);return n}function Ae(e){if(e.includes(0))return null;let t=e.toString("utf8");return t.length===0?0:t.endsWith(`
|
|
4
|
+
`)?t.slice(0,-1).split(/\r\n|\r|\n/).length:t.split(/\r\n|\r|\n/).length}function Lt(e,t){let r=Ae(t);if(r===null)return[`diff --git a/${e} b/${e}`,"new file mode 100644","Binary files /dev/null and b/"+e+" differ"].join(`
|
|
5
|
+
`);let n=t.toString("utf8"),o=n.length===0?[]:n.replace(/\n$/,"").split(/\r\n|\r|\n/);return[`diff --git a/${e} b/${e}`,"new file mode 100644","--- /dev/null",`+++ b/${e}`,`@@ -0,0 +1,${r} @@`,...o.map(s=>`+${s}`)].join(`
|
|
6
|
+
`)}async function $t(e,t){let r=X(await I(["diff","--name-status",e],t)),n=new Set(r.map(s=>s.split(" ").at(-1)??"")),o=(await Z(t)).filter(s=>!n.has(s)).map(s=>`A ${s}`);return[...r,...o].join(`
|
|
7
|
+
`)}async function jt(e,t){let r=X(await I(["diff","--numstat",e],t)),n=new Set(r.map(s=>s.split(" ").slice(2).join(" ")).filter(Boolean)),o=await Promise.all((await Z(t)).filter(s=>!n.has(s)).map(s=>{let a=(0,S.readFileSync)(de(t,s));return`${Ae(a)??"-"} 0 ${s}`}));return[...r,...o].join(`
|
|
8
|
+
`)}async function qt(e,t,r){return new Set(await Z(t,r)).has(r)?Lt(r,(0,S.readFileSync)(de(t,r))):I(["diff",e,"--",r],t)}function Bt(e){return e.replace(/^"(.*)"$/,"$1").replace(/^a\//,"").replace(/^b\//,"")}function Wt(e){let t=/^diff --git a\/(.+?) b\/(.+)$/.exec(e.trim());return t?Bt(t[2]??t[1]??""):null}function Kt(e){let t=new Map,r=null,n=[],o=()=>{if(!r)return;let s=n.join(`
|
|
9
|
+
`);t.set(r,s.endsWith(`
|
|
10
|
+
`)?s:`${s}
|
|
11
|
+
`)};for(let s of e.split(`
|
|
12
|
+
`)){let a=Wt(s);if(a){o(),r=a,n=[s];continue}r&&n.push(s)}return o(),t}function be(e){let t=Number(e);return Number.isFinite(t)?t:0}function Gt(e){let t=e.trim();if(!t.includes(" => "))return t;let r=/^(.*)\{.* => (.*)\}(.*)$/.exec(t);return r?`${r[1]}${r[2]}${r[3]}`:t.slice(t.lastIndexOf(" => ")+4).trim()}function Jt(e){let t=new Map;for(let r of e.split(`
|
|
13
|
+
`).filter(Boolean)){let[n,o,...s]=r.split(" "),a=Gt(s.join(" "));a&&t.set(a,{additions:be(n),deletions:be(o)})}return t}function zt(e){let[t,...r]=e.split(" "),n=t.charAt(0),o=n==="R"||n==="C"?r.at(-1)?.trim():r.join(" ").trim();return o?{path:o,status:n==="A"?"added":n==="D"?"deleted":n==="R"?"renamed":"modified"}:null}function Yt(e,t){let r=Jt(t);return e.split(`
|
|
14
|
+
`).filter(Boolean).map(zt).filter(n=>n!==null).map(n=>({...n,additions:r.get(n.path)?.additions??0,deletions:r.get(n.path)?.deletions??0}))}function Vt(e,t){return Y((0,S.readFileSync)(de(e,t)))}function Xt(e,t){if(t.size===0)return Y(e);let r=Array.from(t.entries()).sort(([n],[o])=>n.localeCompare(o)).map(([n,o])=>`${n}\0${o}`).join(`
|
|
15
|
+
`);return Y(`${e}\0untracked-content\0${r}`)}function Zt(e){return Yt(e.fileListRaw,e.numstatRaw).map(t=>{let r=e.diffByFile.get(t.path)??"",n=e.untrackedContentHashes.get(t.path);return{path:t.path,status:t.status,additions:t.additions,deletions:t.deletions,patchHash:Y(n?`${r}\0untracked-content\0${n}`:r),untracked:e.untracked.has(t.path)}})}async function V(e){let t=await Re(e),r=await I(["rev-parse","HEAD"],t),[n,o,s,a,i]=await Promise.all([$t(r,t),jt(r,t),I(["diff","--no-textconv","--no-ext-diff",r],t),Z(t),Ht(t)]),c=await Promise.all(a.map(y=>qt(r,t,y))),p=new Map(a.map(y=>[y,Vt(t,y)])),k=[s,...c].filter(Boolean).join(`
|
|
16
|
+
`),b=k&&!k.endsWith(`
|
|
17
|
+
`)?`${k}
|
|
18
|
+
`:k,m=Kt(b);return{repoRoot:t,baseSha:r,diff:b,diffHash:Xt(b,p),hasStagedChanges:i,files:Zt({fileListRaw:n,numstatRaw:o,diffByFile:m,untracked:new Set(a),untrackedContentHashes:p})}}function Qt(e,t,r){let n=e.files.find(o=>o.path===t);if(!n)throw new Error("file is not in current git diff");if(r&&n.patchHash!==r)throw new Error("git diff changed; refresh before restoring");return n}async function xe(e){let t=await V(e.worktreePath);if(t.baseSha!==e.baseSha)throw new Error("git base changed; refresh before restoring");if(t.hasStagedChanges)throw new Error("staged changes are not supported");let r=e.filePath?[Qt(t,e.filePath,e.expectedPatchHash)]:t.files;if(!e.filePath&&e.expectedDiffHash&&t.diffHash!==e.expectedDiffHash)throw new Error("git diff changed; refresh before restoring");let n=r.filter(a=>!a.untracked).map(a=>a.path),o=r.filter(a=>a.untracked).map(a=>a.path),s=t.repoRoot;return await Te(s,async()=>{n.length>0&&await I(["restore",`--source=${e.baseSha}`,"--worktree","--",...n],s),o.length>0&&await I(["clean","-f","--",...o],s)}),V(s)}var Q=require("fs"),Se=require("os"),A=require("path");function ce(){return process.env.TRUESAYER_NODE_HOME?.trim()||(0,A.join)((0,Se.homedir)(),".truesayer-node")}function Me(e){return(0,A.join)(e?.trim()||ce(),"provider.json")}function L(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,120)||"unknown"}function Ce(e){return(0,Q.mkdirSync)(e,{recursive:!0,mode:448}),(0,Q.chmodSync)(e,448),e}function er(e){return{HOME:e,CLAUDE_CONFIG_DIR:(0,A.join)(e,".claude"),CODEX_HOME:(0,A.join)(e,".codex")}}function ue(e){let t=e.truesayerNodeHome?.trim()||ce(),r=L(e.agentId),n=(0,A.join)(t,"agents",r),o=(0,A.join)(t,"runtime-home"),s=(0,A.join)(n,"space"),a=(0,A.join)(s,"profile");for(let i of[o,(0,A.join)(o,".claude"),(0,A.join)(o,".codex"),s,a])Ce(i);return{agentRoot:n,providerHome:o,spaceRoot:s,profileDir:a,providerEnv:er(o)}}function Oe(e){let t=e.truesayerNodeHome?.trim()||ce(),r=L(e.workspaceId),n=L(e.agentSessionId),o=L(e.agentRunId),s=L(e.repoAlias),a=ue({truesayerNodeHome:t,workspaceId:e.workspaceId,agentId:e.agentId}),{agentRoot:i,providerHome:c,spaceRoot:p,profileDir:k}=a,b=(0,A.join)(p,"context",n),m=(0,A.join)(p,"agent-run",o),y=(0,A.join)(p,"worktree",r,s,o);for(let d of[b,m,y])Ce(d);return{agentRoot:i,providerHome:c,spaceRoot:p,profileDir:k,contextDir:b,agentRunDir:m,worktreeDir:y,fileToolRoot:p,providerEnv:a.providerEnv}}var te=require("fs/promises"),je=require("os"),qe=require("path"),le=require("@anthropic-ai/claude-agent-sdk");var Ue=J(require("os")),$=require("fs"),M=require("@anthropic-ai/claude-agent-sdk"),u=require("zod/v4"),tr=16*1024,rr=12e3;function nr(e){return{content:[{type:"text",text:JSON.stringify(e??{})}]}}var De=u.z.object({id:u.z.string().describe("Stable slug for this task item, such as implement-todo-app."),label:u.z.string().trim().min(1).describe('Descriptive task title, such as "\u5B9E\u73B0\u5168\u6808 Todo app"; do not use only "1", "step 1", or "phase 1".'),ownerAgentId:u.z.string().describe("Target Truesayer teammate id from list_channel_agents or resolve_channel_agent. Do not put responsibilities, roles, names, or mention text here."),status:u.z.enum(["todo","active","done","blocked"]).optional(),estMinutes:u.z.number().optional(),dependsOn:u.z.array(u.z.string()).optional()}).passthrough(),or=u.z.object({creationCheckId:u.z.string().uuid(),name:u.z.string().optional(),description:u.z.string().optional(),providerName:u.z.string().optional(),providerId:u.z.string().optional(),modelId:u.z.string().optional(),selectedModel:u.z.string().optional(),reasoningEffort:u.z.enum(["default","none","minimal","low","medium","high","xhigh","max"]).optional(),thinkingMode:u.z.enum(["default","disabled","adaptive"]).optional(),serviceTier:u.z.enum(["default","fast","flex"]).optional(),responsibility:u.z.string().optional()}).passthrough();function sr(e){if(!e.startsWith("/tmp/")&&!e.startsWith(`${Ue.default.tmpdir()}/`))return!1;try{let t=(0,$.statSync)(e);return t.isFile()&&t.size>0}catch{return!1}}function ir(e){try{let t=(0,$.statSync)(e),r=Math.max(0,t.size-tr);return(0,$.readFileSync)(e).subarray(r).toString("utf8").replace(/\0/g,"").trim().slice(-rr)}catch{return""}}function ar(e){if(typeof e.logTail=="string"&&e.logTail.trim())return e;let t=typeof e.logPath=="string"?e.logPath.trim():"";if(!t||!sr(t))return e;let r=ir(t);return r?{...e,logTail:r}:e}function dr(e,t,r={}){let n=async(s,a)=>{let i=s==="record_background_service"?ar(a):a;return nr(await t({agentRunId:e,toolName:s,args:i}))},o=(0,M.tool)("reply_to_channel","Reply visibly in the current Truesayer channel as this agent.",{body:u.z.string(),attachmentIds:u.z.array(u.z.string()).optional(),messageId:u.z.string().optional()},async s=>n("reply_to_channel",s),{alwaysLoad:!0});return r.mode==="silent_relevance"?[o]:[o,(0,M.tool)("create_agent","Create an active Truesayer teammate profile and bind it to a Node runtime provider/model. This is a protected action that blocks for human approval. It does not add the new teammate to the current channel; channel membership must be granted by the frontend, an admin API, setup seed, or an explicit Server policy.",{creationCheckId:u.z.string().uuid(),name:u.z.string(),description:u.z.string(),providerId:u.z.string().optional(),providerName:u.z.string().optional(),modelId:u.z.string().optional(),selectedModel:u.z.string().optional(),reasoningEffort:u.z.enum(["default","none","minimal","low","medium","high","xhigh","max"]).optional(),thinkingMode:u.z.enum(["default","disabled","adaptive"]).optional(),serviceTier:u.z.enum(["default","fast","flex"]).optional(),systemPrompt:u.z.string().optional(),soul:u.z.string().optional(),tone:u.z.number().optional()},async s=>n("create_agent",s),{alwaysLoad:!0}),(0,M.tool)("create_agents","Create multiple active Truesayer teammate profiles in one protected action. This blocks for human approval before the Server creates the profiles. Each agents[] item must include a stable creationCheckId UUIDv7; reuse the same creationCheckId when retrying the same logical agent creation request. It does not add the new teammates to the current channel; channel membership must be granted by the frontend, an admin API, setup seed, or an explicit Server policy.",{summary:u.z.string().describe("Human-readable summary shown on the approval activity card."),agents:u.z.array(or.extend({name:u.z.string(),description:u.z.string()})).min(1)},async s=>n("create_agents",s),{alwaysLoad:!0}),(0,M.tool)("message_agent",'Send a work request or update to another Truesayer agent. This is only for immediately runnable work. If the message continues or refers to a structured task-card item, include planItemId so the Server can reuse the existing handoff instead of creating duplicate work. Do not use it to pre-send future or dependency-blocked work such as "audit after implementation"; represent that as blocked items in delegate_plan instead. Use this to delegate execution instead of visibly @mentioning that agent. After a successful delegation, end the current run. Truesayer may wake the current agent with downstream results and evidence; decide the next step yourself.',{toAgentId:u.z.string(),body:u.z.string(),refTaskId:u.z.string().optional(),planItemId:u.z.string().optional()},async s=>n("message_agent",s),{alwaysLoad:!0}),(0,M.tool)("resolve_channel_agent","Resolve an agent name, mention, or id to a unique teammate in the current Truesayer channel. This is read-only and returns blockers for not_found, not_bound, or ambiguous matches.",{query:u.z.string()},async s=>n("resolve_channel_agent",s),{alwaysLoad:!0}),(0,M.tool)("list_channel_agents","List all Truesayer agents in this workspace and mark whether each one can access the current Truesayer channel. Use this before delegate_plan or message_agent when agent ids or channel access may have changed.",{},async s=>n("list_channel_agents",s),{alwaysLoad:!0}),(0,M.tool)("delegate_plan","Create a channel task card and delegate active items to specific Truesayer agents. Active items are immediately sent to their owner agents as executable work when this tool succeeds. Do not call message_agent to notify the same owner about the same active item. Each item label must describe the actual assigned work in enough detail for the owner to execute it, not only an ordinal. Use blocked items for future or dependency-blocked work, and only make an item active when it should run now. Put all instructions for that phase in the item label/status/dependencies before calling this tool. Do not create plan items owned by yourself or the current agent; coordinator-owned follow-up work such as final review, deciding whether to continue, summary, or reply must stay outside the task card. Do not render coordinator-owned follow-up work as task table rows in visible replies. Use a short sentence instead. Call list_channel_agents first when you need current agent ids or channel access. When the user asks to assign, delegate, create task cards, or start a phase, use this before Bash/Edit/Read unless the user asked you to implement the work yourself. After successful delegation, end the current run. Truesayer may wake the current agent with downstream results and evidence; decide the next step yourself, such as verify, delegate more work, update the plan, report a blocker, or reply to the channel.",{items:u.z.array(De),replace:u.z.boolean().optional()},async s=>n("delegate_plan",s),{alwaysLoad:!0}),(0,M.tool)("emit_plan","Create or update the visible channel task card for this agent run. Each item label must describe the actual work, not only an ordinal. Do not create plan items owned by yourself or the current agent; coordinator-owned follow-up work such as final review, deciding whether to continue, summary, or reply must stay outside the task card. Do not render coordinator-owned follow-up work as task table rows in visible replies. Use a short sentence instead. Existing unfinished downstream items are preserved unless replace is true. Use delegate_plan instead when active items should be assigned to other agents. Call list_channel_agents first when ownerAgentId values need current agent ids or channel access.",{items:u.z.array(De),replace:u.z.boolean().optional()},async s=>n("emit_plan",s),{alwaysLoad:!0}),(0,M.tool)("update_plan_item","Update one item on the current canonical task card. Do not provide or guess a root task id; the Truesayer Server chooses the writable plan from this agent run context.",{itemId:u.z.string(),status:u.z.enum(["todo","active","done","blocked"])},async s=>n("update_plan_item",s),{alwaysLoad:!0}),(0,M.tool)("record_background_service","Record a visible background service started for human inspection. Use this after starting a keep-alive dev/server process in the background and after a bounded health check succeeds. Include PID, port, URL, and log path so Activity can show a visible background service row. This records observability only; it does not start, stop, or clean up the process.",{label:u.z.string().optional(),pid:u.z.number().optional(),port:u.z.number().optional(),url:u.z.string().optional(),healthUrl:u.z.string().optional(),logPath:u.z.string().optional(),cwd:u.z.string().optional()},async s=>n("record_background_service",s),{alwaysLoad:!0})]}function Fe(e,t,r={}){return(0,M.createSdkMcpServer)({name:"truesayer-platform",version:"0.1.0",tools:dr(e,t,r),alwaysLoad:!0})}var He=["Truesayer platform tools are available as local tools. They send JSON-RPC requests from the Node process to the Truesayer Server over the existing WebSocket.","Use reply_to_channel for visible channel replies.","Use create_agents to create long-lived teammates that do not yet exist. Put what the teammate does in description, not role. Every agents[] item must include a stable creationCheckId UUIDv7; reuse it when retrying the same logical agent creation. create_agents blocks for human approval before the Server creates the profiles, and returns agentId plus mentionName values to use in message_agent and task card ownerAgentId.","Use create_agent only when creating exactly one teammate. It is also protected and blocks for human approval.","create_agent and create_agents do not add new teammates to the current channel. Channel membership/invitation must be granted by the frontend, an admin API, setup seed, or an explicit Server policy, never by agent authority.","Use list_channel_agents to get the realtime list of all Truesayer agents in this workspace and whether each can access the current channel. Use it before delegate_plan or message_agent when IDs, names, or channel membership may have changed.","Use resolve_channel_agent to turn a teammate name or @mention in the current channel into the agentId required by message_agent and task card ownerAgentId. If it returns not_bound, ambiguous, or not_found, report that blocker instead of guessing an id.","message_agent is only for immediately runnable work. If continuing a structured task-card item, include planItemId. The recipient may start as soon as the tool succeeds, so do not use message_agent to pre-send future, conditional, or dependency-blocked work.","Use delegate_plan or emit_plan for visible channel task cards, and update_plan_item to update item status. Task item labels must be descriptive enough for the assigned agent to execute from the item alone.","delegate_plan active items with valid ownerAgentId values are already sent to their owner agents as executable work when the tool succeeds. Do not follow delegate_plan with message_agent for the same active item; put the full task instructions into the active plan item before delegating.","Future or dependency-blocked work must be represented as blocked task card items with delegate_plan or emit_plan. Do not send those items through message_agent until they are actually runnable.","Do not create plan items owned by yourself or the current agent. Coordinator-owned follow-up work such as final review, deciding whether to continue, summary, or reply is not a task-card item. After delegated work finishes, Truesayer may wake the current agent with downstream results and evidence, and you decide the next step yourself: verify, delegate more work, update the plan, report a blocker, or reply to the channel.","If a prompt asks you to create a collaboration team, call create_agents once with the whole team so the human sees one approval card. If the tool is rejected or blocked, report that blocker instead of retrying.","If a prompt asks you to assign, delegate, create task cards, update a plan, or start a phased handoff with existing teammates, your first tool call must be delegate_plan or message_agent before any file, git, shell, package-manager, or code-edit tool.","A coordinating agent must not do another agent's assigned implementation, audit, or optimization work itself. Create the task card or agent message, then end the current run. Truesayer may wake the current agent with a continuation when downstream work reports back.","Never send message_agent to yourself as an availability test. If no suitable teammate exists, call create_agent first; if provider capability is missing, reply with the blocker.","Only use message_agent after delegate_plan for genuinely new follow-up communication, not as a notification that the delegated active item exists.","Do not use provider-native Agent, Task, TodoRead, or TodoWrite for product coordination. Truesayer task cards, plan updates, and teammate handoffs must go through Truesayer platform tools so the Server can audit every agent run.","If the provider UI prefixes Truesayer tools as MCP tools, select the matching tool whose name ends with create_agents, create_agent, resolve_channel_agent, reply_to_channel, message_agent, delegate_plan, emit_plan, update_plan_item, or record_background_service.","Long-running or continuously-running service/watch commands, such as dev servers, file watchers, npm start, npm run start, npm run dev, vite, next dev, nodemon, tsx --watch, tail -f, database servers, or queue workers, must not be run as foreground blocking commands. Start them in the background, redirect logs to a file, record the PID, and run a bounded health check with timeout. Stop or clean up the process when validation is complete unless the task explicitly asks to keep the service available for human inspection; in that case keep it running and call record_background_service with PID, port, URL, and log path. Finite commands such as install, build, test, lint, migration, and one-shot scripts should run in the foreground and must return an exit code.","Do not try to access the Truesayer Server database, provider secrets, or Server-only environment variables from Node."].join(`
|
|
19
|
+
`);function Le(e){let t=Object.entries(e??{});return t.length===0?"":["Available Node runtime providers for create_agent/create_agents runtime selection:",...t.map(([r,n])=>`- ${r} (${n.kind}): ${n.models.join(", ")}`)].join(`
|
|
20
|
+
`)}var cr=["DATABASE_URL","TRUESAYER_API_KEY","TRUESAYER_ADMIN_API_KEY","BETTER_AUTH_SECRET","ASSET_STORAGE_DRIVER","S3_ENDPOINT","S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY","S3_REGION","S3_FORCE_PATH_STYLE"];function ur(e){let t={...e};return!t.ANTHROPIC_API_KEY&&t.AGENT_API_KEY&&(t.ANTHROPIC_API_KEY=t.AGENT_API_KEY),t.ANTHROPIC_BASE_URL||(t.ANTHROPIC_BASE_URL=t.ANTHROPIC_API_URL||t.AGENT_BASE_URL||void 0),t}function j(e={}){let t={...process.env};for(let r of cr)delete t[r];return ur({...t,...e})}function ee(){return j()}var lr=["Agent","Task","TodoRead","TodoWrite"];async function*pr(){}function mr(e){return Array.from(new Set(e.map(t=>t.trim()).filter(Boolean)))}function gr(e){return mr((e??[]).map(t=>t.value))}function fr(e,t,r,n){let o;return new Promise((s,a)=>{o=setTimeout(()=>{r(),a(new Error(n))},t),o.unref?.(),e.then(i=>{o&&clearTimeout(o),s(i)},i=>{o&&clearTimeout(o),a(i)})})}async function Be(e={}){let t=e.cwd??await(0,te.mkdtemp)((0,qe.join)((0,je.tmpdir)(),"truesayer-claude-model-probe-")),r=!e.cwd,n=new AbortController,o=(0,le.query)({prompt:pr(),options:{cwd:t,env:ee(),abortController:n,maxTurns:1}});try{let s=await fr(o.initializationResult(),e.timeoutMs??3e4,()=>n.abort(),"Claude Code model discovery timed out");return gr(s.models)}finally{o.close(),r&&await(0,te.rm)(t,{recursive:!0,force:!0})}}function hr(e,t,r){let n=[e?.trim(),t?He:"",t?Le(r):""].filter(Boolean).join(`
|
|
21
|
+
|
|
22
|
+
`);return n?{type:"preset",preset:"claude_code",append:n}:void 0}function P(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function vr(e){return typeof e=="string"&&e.trim()?e.trim():""}function yr(e){return typeof e=="string"?e.trim():Array.isArray(e)?e.filter(r=>typeof r=="string"&&r.trim().length>0).map(r=>r.trim()).join(" "):""}function We(...e){for(let t of e){let r=yr(t);if(r)return r}return""}function F(...e){for(let t of e){let r=vr(t);if(r)return r}return""}function Ke(e,t=""){let r=e.toLowerCase(),n=t.toLowerCase();return["read","grep","glob","ls"].includes(r)?"read":["write","edit","multiedit"].includes(r)?"write":r==="update"?"update":r==="bash"||r==="exec"||r==="exec_command"||r==="codex_command"?/(^|[;&|]\s*)apply_patch\b/.test(n)?"write":/^\s*(sed\s+-n\b|cat\b|head\b|tail\b|nl\b|find\b|ls\b|rg\b|grep\b|pwd\b)/.test(n)?"read":"exec":r.startsWith("mcp__truesayer__")?"platform_tool":"exec"}function Ge(e){return e==="read"?"read":e==="write"?"write":e==="update"?"update":e==="platform_tool"?"platform_tool":"exec"}function kr(e){return P(e)}function wr(e,t){let r=F(e.name);if(!r)return null;let n=kr(e.input),o=We(n.command,n.cmd,n.commandLine),s=F(n.file_path,n.path,n.filePath),a=Ke(r,o),i=F(e.id);return i&&t?.set(i,{rawToolName:r,command:o,path:s,operation:a}),{type:"agent_run.tool.started",payload:{provider:"claude",toolName:Ge(a),toolCallId:i,...o?{command:o}:{},...s?{path:s}:{},operation:a}}}function br(e,t){let n=(Array.isArray(P(e.message).content)?P(e.message).content:[]).map(P).find(m=>m.type==="tool_result");if(!n)return null;let o=P(e.toolUseResult),s=P(o.file),a=F(n.tool_use_id,n.toolUseId),i=t?.get(a)??{},c=F(s.filePath,s.file_path,o.filePath,o.file_path,i.path),p=We(o.command,o.cmd,i.command),k=F(i.operation)||(c?"read":Ke(p?"Bash":"tool",p)),b=c&&typeof s.numLines=="number"?`${s.numLines} lines`:F(o.stdout,o.output,n.content);return{type:"agent_run.tool.completed",payload:{provider:"claude",toolName:Ge(k),toolCallId:a,...p?{command:p}:{},...c?{path:c}:{},operation:k,...b?{output:b}:{}}}}function Er(e,t=new Map){let r=P(e),n=P(r.message),o=Array.isArray(n.content)?n.content:[],s=[];for(let i of o.map(P))if(i.type==="tool_use"){let c=wr(i,t);c&&s.push(c)}let a=br(r,t);return a&&s.push(a),s}function _r(e){if(e.type==="result"){let t=e.result;return typeof t=="string"?t:null}return null}function q(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function $e(...e){for(let t of e){let r=q(t);if(r!==void 0)return Math.max(0,Math.trunc(r))}}function Rr(e){let t=P(e),r=P(t.usage),n=P(t.tokenUsage),o=P(t.token_usage),s=P(t.tokens),a=P(t.usageSummary);return{inputTokens:$e(r.input_tokens,r.inputTokens,r.prompt_tokens,r.promptTokens,n.input_tokens,n.inputTokens,n.prompt_tokens,n.promptTokens,o.input_tokens,o.inputTokens,o.prompt_tokens,o.promptTokens,s.input_tokens,s.inputTokens,s.prompt_tokens,s.promptTokens,a.input_tokens,a.inputTokens,t.input_tokens,t.inputTokens,t.total_input_tokens,t.prompt_tokens,t.promptTokens),outputTokens:$e(r.output_tokens,r.outputTokens,r.completion_tokens,r.completionTokens,n.output_tokens,n.outputTokens,n.completion_tokens,n.completionTokens,o.output_tokens,o.outputTokens,o.completion_tokens,o.completionTokens,s.output_tokens,s.outputTokens,s.completion_tokens,s.completionTokens,a.output_tokens,a.outputTokens,t.output_tokens,t.outputTokens,t.total_output_tokens,t.completion_tokens,t.completionTokens),costUsd:q(t.total_cost_usd)??q(t.cost_usd)??q(r.total_cost_usd)??q(r.cost_usd)}}function Tr(e){return e.type!=="result"?{}:Rr(e)}async function Nr(e,t){let r="",n={},o=2,s=[],a=new Map;try{for await(let i of t){for(let c of Er(i,a))s.push({eventId:`${e}:claude:${o}`,eventSeq:o,type:c.type,payload:c.payload}),o+=1;r=_r(i)??r,n={...n,...Tr(i)}}return{output:r.trim()||"Agent run completed",...n,events:s}}catch(i){let c=i instanceof Error?i:new Error(String(i));throw c.events=s,c.emittedEventIds=new Set,c}finally{t.close()}}async function Je(e){if(e.provider.kind!=="claude-code"&&e.provider.kind!=="claude-compatible")throw new Error(`unsupported node provider kind: ${e.provider.kind}`);let t=new AbortController,r=e.invokeTool?{truesayer:Fe(e.agentRunId,e.invokeTool,{mode:e.toolMode})}:void 0,n=(0,le.query)({prompt:e.prompt,options:{cwd:e.cwd,env:e.env,model:e.selectedModel,systemPrompt:hr(e.systemPrompt,!!e.invokeTool,e.availableProviders),mcpServers:r,maxTurns:e.maxTurns??80,permissionMode:"bypassPermissions",allowDangerouslySkipPermissions:!0,disallowedTools:[...lr],abortController:t}});return Nr(e.agentRunId,n)}var H=require("fs"),Qe=require("child_process"),et=require("readline"),tt=require("module"),W=require("path");function ze(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function Ye(e){return typeof e=="string"?e.trim():""}function Ir(e){let t=ze(e),r=Ye(t.type);return r==="add"||r==="delete"?{type:r}:r==="update"?{type:r,move_path:typeof t.move_path=="string"?t.move_path:null}:null}function Pr(e){let t=ze(e),r=Ye(t.path),n=Ir(t.kind);return!r||!n?null:{path:r,kind:n,diff:typeof t.diff=="string"?t.diff:""}}function pe(e){return Array.isArray(e)?e.map(Pr).filter(t=>t!==null).map(t=>{let r=Ar(t.kind);return{path:r==="renamed"&&t.kind.type==="update"&&t.kind.move_path?t.kind.move_path:t.path,status:r,...t.diff?{diff:t.diff}:{}}}):[]}function Ar(e){return e.type==="add"?"added":e.type==="delete"?"deleted":e.move_path?"renamed":"modified"}var ge=6e4,xr=(0,tt.createRequire)(__filename);function Sr(){let e=xr.resolve("@openai/codex/package.json");return(0,W.join)((0,W.dirname)(e),"bin","codex.js")}function R(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function _(e){return typeof e=="string"&&e.trim()?e.trim():""}function Ve(e){return typeof e=="string"?e:""}function E(...e){for(let t of e){let r=_(t);if(r)return r}return""}function me(...e){for(let t of e)if(typeof t=="number"&&Number.isFinite(t))return t}function Mr(e){return typeof e=="string"?e.trim():Array.isArray(e)?e.filter(r=>typeof r=="string"&&r.trim().length>0).map(r=>r.trim()).join(" "):""}function Cr(...e){for(let t of e){let r=Mr(t);if(r)return r}return""}function Or(e){return e.some(t=>["read","search","listFiles"].includes(E(t.type)))?"read":"exec"}function Xe(e){return e==="read"?"read":e==="write"?"write":e==="update"?"update":e==="platform_tool"?"platform_tool":"exec"}function Dr(e){return Array.isArray(e)?e.map(R).filter(t=>E(t.type)):[]}function Ur(e){return Array.from(new Set(e.map(t=>E(t.path)).filter(Boolean)))}function Fr(e){return e.some(t=>t.status!=="added")?"update":"write"}function Hr(e){let t=E(e.tool);if(e.type!=="mcpToolCall")return t;let r=E(e.server);return r&&t?`${r}.${t}`:t}function Lr(e){let t=e.method,r=e.params??{},n=R(r.item),o=_(n.type),s=t==="item/completed"?"agent_run.tool.completed":"agent_run.tool.started";if(t!=="item/started"&&t!=="item/completed")return null;if(o==="commandExecution"){let a=Cr(n.command),i=Dr(n.commandActions),c=Or(i),p=Ur(i),k=E(n.status),b=E(n.aggregatedOutput),m=me(n.exitCode),y=me(n.durationMs);return{type:s,payload:{provider:"codex",toolName:Xe(c),toolCallId:E(n.id),...a?{command:a}:{},...E(n.cwd)?{cwd:E(n.cwd)}:{},...p[0]?{path:p[0]}:{},...p.length>0?{paths:p}:{},operation:c,...k?{status:k}:{},...m===void 0?{}:{exitCode:m},...b?{output:b}:{},...y===void 0?{}:{durationMs:y},...i.length>0?{commandActions:i}:{}}}}if(o==="fileChange"){let a=pe(n.changes);if(a.length===0)return null;let i=a.map(k=>k.path),c=Fr(a),p=E(n.status);return{type:s,payload:{provider:"codex",toolName:Xe(c),toolCallId:E(n.id),command:"fileChange",path:i[0],paths:i,operation:c,...p?{status:p}:{},fileChanges:a,...a[0]?.diff?{diff:a[0].diff}:{}}}}if(o==="dynamicToolCall"||o==="mcpToolCall"){let a=Hr(n),i=me(n.durationMs);return{type:s,payload:{provider:"codex",toolName:"platform_tool",toolCallId:E(n.id),platformToolName:a,...n.arguments===void 0?{}:{arguments:n.arguments},operation:"platform_tool",...E(n.status)?{status:E(n.status)}:{},...typeof n.success=="boolean"?{success:n.success}:{},...n.contentItems===void 0||n.contentItems===null?{}:{contentItems:n.contentItems},...n.result===void 0||n.result===null?{}:{result:n.result},...n.error===void 0||n.error===null?{}:{error:n.error},...i===void 0?{}:{durationMs:i}}}}return null}function $r(e){if(e.method!=="item/fileChange/patchUpdated")return null;let t=e.params??{},r=pe(t.changes);if(r.length===0)return null;let n=r.map(o=>o.path);return{type:"agent_run.provider_event",payload:{provider:"codex",method:e.method,...E(t.itemId)?{itemId:E(t.itemId)}:{},paths:n,changes:r,...E(t.threadId)?{threadId:E(t.threadId)}:{},...E(t.turnId)?{turnId:E(t.turnId)}:{}}}}function jr(e){let t=e.params??{};if(e.method==="item/completed"){let r=R(t.item);if(r.type==="agentMessage"){let n=_(r.text);return n?{type:"agent_run.message.completed",payload:{provider:"codex",text:n,..._(r.phase)?{phase:_(r.phase)}:{}}}:null}}return e.method==="error"?{type:"agent_run.provider_event",payload:{provider:"codex",method:e.method,params:t}}:e.method==="turn/diff/updated"?{type:"agent_run.provider_event",payload:{provider:"codex",method:e.method,...E(t.threadId)?{threadId:E(t.threadId)}:{},...E(t.turnId)?{turnId:E(t.turnId)}:{},...Ve(t.diff)?{diff:Ve(t.diff)}:{}}}:null}function qr(e){return Lr(e)??$r(e)??jr(e)}function rt(e){if(e.method!=="error")return null;let t=e.params??{};if(t.willRetry===!0)return null;let r=R(t.error),n=R(r.codexErrorInfo),o=_(t.codexErrorInfo)||_(r.codexErrorInfo)||Object.keys(n)[0]||_(t.code)||_(t.errorCode)||_(r.code),s=_(t.message)||_(r.message)||_(t.error)||"Codex app-server provider error";return new Error(`Codex app-server error${o?` ${o}`:""}: ${s}`)}function Br(e){let t=e.TRUESAYER_CODEX_REQUEST_TIMEOUT_MS?.trim();if(!t)return ge;let r=Number(t);return Number.isFinite(r)&&r>0?Math.trunc(r):ge}function Wr(e){if(e.exitCode!==null||e.killed)return;let t=e.pid;if(t&&process.platform!=="win32")try{process.kill(-t,"SIGTERM");return}catch{}e.kill("SIGTERM")}function nt(e){let t=e.env.TRUESAYER_CODEX_BIN||Sr(),r=(0,Qe.spawn)(process.execPath,[t,"app-server","--listen","stdio://",...e.args??[]],{cwd:e.cwd,env:e.env,stdio:["pipe","pipe","pipe"],detached:e.detached===!0}),n=new Map,o=[],s=e.requestTimeoutMs??ge,a=1,i=!1;function c(){return o.join("").slice(-1e3).trim()}function p(m,y){return new Error(`Codex app-server request timed out after ${s}ms (method ${y.method}, requestId ${m}, stderr: ${c()||"no stderr"})`)}function k(m,y){for(let[d,g]of n)n.delete(d),g.timeout&&clearTimeout(g.timeout),e.onRequestEvent?.("failed",{method:g.method,requestId:d,elapsedMs:Date.now()-g.startedAt,reason:y,error:m.message}),g.reject(m)}let b=(0,et.createInterface)({input:r.stdout});return b.on("line",m=>{let y=m.trim();if(!y)return;let d;try{d=JSON.parse(y)}catch{return}let g=R(d),h=g.id;if((typeof h=="number"||typeof h=="string")&&n.has(h)){let l=n.get(h);n.delete(h),l?.timeout&&clearTimeout(l.timeout);let w=g;w.error?(e.onRequestEvent?.("failed",{method:l?.method??"unknown",requestId:h,elapsedMs:l?Date.now()-l.startedAt:void 0,reason:"error",error:w.error.message??"Codex app-server request failed"}),l?.reject(new Error(w.error.message??"Codex app-server request failed"))):(e.onRequestEvent?.("completed",{method:l?.method??"unknown",requestId:h,elapsedMs:l?Date.now()-l.startedAt:void 0}),l?.resolve(w.result));return}let f=_(g.method);if(f){let l={method:f,params:R(g.params)};e.onNotification?.(l);let w=rt(l);w&&k(w,"provider_error")}}),r.stderr.on("data",m=>{o.push(m.toString("utf8")),o.length>25&&o.shift()}),r.on("exit",(m,y)=>{if(i)return;let d=`Codex app-server exited (code ${m??"null"}, signal ${y??"null"}): ${c()||"no stderr"}`,g=new Error(d);k(g,"app_server_exit"),e.onExit?.(g)}),{stderrTail:c,request(m,y){let d=a;a+=1,e.onRequestEvent?.("started",{method:m,requestId:d});let g=new Promise((h,f)=>{let l={method:m,startedAt:Date.now(),resolve:h,reject:f},w=setTimeout(()=>{if(!n.delete(d))return;let C=p(d,l);e.onRequestEvent?.("failed",{method:m,requestId:d,elapsedMs:Date.now()-l.startedAt,reason:"timeout"}),f(C)},s);typeof w.unref=="function"&&w.unref(),l.timeout=w,n.set(d,l)});return r.stdin.write(`${JSON.stringify({id:d,method:m,params:y})}
|
|
23
|
+
`),g},notify(m,y){r.stdin.write(`${JSON.stringify({method:m,...y===void 0?{}:{params:y}})}
|
|
24
|
+
`)},close(){i=!0,b.close(),Wr(r)}}}function Kr(e){let t=R(e).data;if(!Array.isArray(t))return[];let r=t.flatMap(n=>{let o=R(n),s=_(o.model)||_(o.id);return s?[s]:[]});return Array.from(new Set(r.map(n=>n.trim()).filter(Boolean)))}async function ot(e={}){let t=nt({env:e.env??j(),detached:process.platform!=="win32",requestTimeoutMs:e.timeoutMs??3e4});try{return await t.request("initialize",{clientInfo:{name:"truesayer",title:"Truesayer",version:"0.1.0"},capabilities:{experimentalApi:!0,optOutNotificationMethods:[]}}),t.notify("initialized"),Kr(await t.request("model/list",{includeHidden:!1,limit:100}))}finally{t.close()}}function st(e){return e?(0,W.join)(e,"codex-app-server-session.json"):void 0}function Gr(e){let t=st(e);if(!(!t||!(0,H.existsSync)(t)))try{let r=JSON.parse((0,H.readFileSync)(t,"utf8"));return _(R(r).threadId)||void 0}catch{return}}function Jr(e,t){let r=st(e);r&&(0,H.writeFileSync)(r,`${JSON.stringify({threadId:t})}
|
|
25
|
+
`,{mode:384})}var zr=["Long-running or continuously-running service/watch commands, such as dev servers, file watchers, npm start, npm run start, npm run dev, vite, next dev, nodemon, tsx --watch, tail -f, database servers, or queue workers, must not be run as foreground blocking commands. Start them in the background, redirect logs to a file, record the PID, and run a bounded health check with timeout. Stop or clean up the process when validation is complete unless the task explicitly asks to keep the service available for human inspection; in that case keep it running in the background and report PID, port, URL, and log path. Finite commands such as install, build, test, lint, migration, and one-shot scripts should run in the foreground and must return an exit code.","Do not try to access the Truesayer Server database, provider secrets, or Server-only environment variables from Node.","All file creation and manual file edits MUST use apply_patch or Codex native fileChange. This overrides any base instruction that allows shell redirection, here-docs, cat, tee, scripts, or generated-file exceptions. Do not create or edit files with cat, tee, heredoc, sed -i, perl -pi, python/node scripts, or shell redirection, even for generated files like package.json or temporary scratch files under /tmp."].join(`
|
|
26
|
+
`);function Yr(e){return["Truesayer runtime instructions:",[e.systemPrompt?.trim(),zr].filter(Boolean).join(`
|
|
27
|
+
|
|
28
|
+
`)].join(`
|
|
29
|
+
`)}function Vr(e){return e.prompt}function re(e){return JSON.stringify(e)}function Xr(e){return/^[A-Za-z0-9_-]+$/.test(e)?e:re(e)}function Zr(e){if(e.provider.source!=="provider-file"||e.provider.kind!=="codex"&&e.provider.kind!=="openai-compatible")return[];let t=e.provider.baseUrl?.trim()||e.env.OPENAI_BASE_URL?.trim();if(!t)return[];let r=e.providerName.trim();if(!r)return[];let n=[`name=${re(e.provider.displayName||r)}`,`base_url=${re(t)}`,'wire_api="responses"','env_key="OPENAI_API_KEY"',"requires_openai_auth=false","supports_websockets=false"].join(",");return["-c",`model_provider=${re(r)}`,"-c",`model_providers.${Xr(r)}={${n}}`]}function Qr(e){let t=R(e),r=R(t.last),n=R(t.total);return{inputTokens:ne(r.input_tokens,r.inputTokens,r.prompt_tokens,r.promptTokens,n.input_tokens,n.inputTokens,n.prompt_tokens,n.promptTokens,t.input_tokens,t.inputTokens,t.prompt_tokens,t.promptTokens),outputTokens:ne(r.output_tokens,r.outputTokens,r.completion_tokens,r.completionTokens,n.output_tokens,n.outputTokens,n.completion_tokens,n.completionTokens,t.output_tokens,t.outputTokens,t.completion_tokens,t.completionTokens)}}function B(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function ne(...e){for(let t of e){let r=B(t);if(r!==void 0)return Math.max(0,Math.trunc(r))}}function en(e){let t=R(e),r=R(t.usage),n=R(t.tokenUsage),o=R(t.token_usage),s=R(t.tokens),a=R(t.usageSummary);return{inputTokens:ne(r.input_tokens,r.inputTokens,r.prompt_tokens,r.promptTokens,n.input_tokens,n.inputTokens,n.prompt_tokens,n.promptTokens,o.input_tokens,o.inputTokens,o.prompt_tokens,o.promptTokens,s.input_tokens,s.inputTokens,s.prompt_tokens,s.promptTokens,a.input_tokens,a.inputTokens,t.input_tokens,t.inputTokens,t.total_input_tokens,t.prompt_tokens,t.promptTokens),outputTokens:ne(r.output_tokens,r.outputTokens,r.completion_tokens,r.completionTokens,n.output_tokens,n.outputTokens,n.completion_tokens,n.completionTokens,o.output_tokens,o.outputTokens,o.completion_tokens,o.completionTokens,s.output_tokens,s.outputTokens,s.completion_tokens,s.completionTokens,a.output_tokens,a.outputTokens,t.output_tokens,t.outputTokens,t.total_output_tokens,t.completion_tokens,t.completionTokens),costUsd:B(t.total_cost_usd)??B(t.cost_usd)??B(r.total_cost_usd)??B(r.cost_usd)}}function Ze(...e){let t={};for(let r of e)r&&(r.inputTokens!==void 0&&(t.inputTokens=r.inputTokens),r.outputTokens!==void 0&&(t.outputTokens=r.outputTokens),"costUsd"in r&&r.costUsd!==void 0&&(t.costUsd=r.costUsd));return t}function tn(e){let t=[],r=new Set,n=new Map,o=new Map,s=new Map,a=new Map,i=2,c="",p=null,k=null;function b(l){t.push(l),!r.has(l.eventId)&&(r.add(l.eventId),e.emitEvent?.(l))}function m(l,w){b({eventId:`${e.agentRunId}:codex:${i}`,eventSeq:i,type:`agent_run.provider_request.${l}`,payload:{provider:"codex",...w}}),i+=1}function y(l){let w=qr(l);w&&(b({eventId:`${e.agentRunId}:codex:${i}`,eventSeq:i,type:w.type,payload:w.payload}),i+=1)}function d(l,w={}){let T={output:c.trim()||"Codex run completed",...w.inputTokens===void 0?{}:{inputTokens:w.inputTokens},...w.outputTokens===void 0?{}:{outputTokens:w.outputTokens}};s.set(l,T);let x=a.get(l)??[];a.delete(l);for(let O of x)O.resolve(T)}function g(l){for(let w of a.values())for(let C of w)C.reject(l);a.clear()}function h(l){let w=l.params??{},C=rt(l);if(l.method==="item/agentMessage/delta"){let T=_(w.itemId),x=_(w.delta);if(T&&x){let O=`${n.get(T)??""}${x}`;n.set(T,O),c=O}}if(l.method==="item/completed"){let T=R(w.item);if(T.type==="agentMessage"){let x=_(T.text);x&&(c=x)}}if(l.method==="turn/completed"){let T=R(w.turn),x=_(T.id),O=_(T.status);x&&O==="completed"&&d(x,Ze(o.get(x),en(T)))}if(l.method==="thread/tokenUsage/updated"){let T=_(w.turnId);T&&o.set(T,Ze(o.get(T),Qr(w.tokenUsage)))}y(l),C&&(p=C,g(p))}let f=nt({cwd:e.cwd,env:e.env,args:Zr(e),requestTimeoutMs:Br(e.env),onNotification:h,onRequestEvent:m,onExit(l){k=l,g(l)}});return{events:t,emittedEventIds:r,stderrTail:f.stderrTail,request:f.request,notify:f.notify,close:f.close,waitForTurnCompleted(l){let w=s.get(l);return w!==void 0?Promise.resolve(w):p?Promise.reject(p):k?Promise.reject(k):(m("started",{method:"turn/completed",turnId:l}),new Promise((C,T)=>{let x={resolve:C,reject:T},O=a.get(l)??[];O.push(x),a.set(l,O)}))}}}function rn(e){let t=_(R(R(e).thread).id);if(!t)throw new Error("Codex app-server did not return a thread id");return t}function nn(e){let t=_(R(R(e).turn).id);if(!t)throw new Error("Codex app-server did not return a turn id");return t}async function it(e){if(e.provider.kind!=="codex"&&e.provider.kind!=="openai-compatible")throw new Error(`unsupported node provider kind: ${e.provider.kind}`);let t=tn(e),r=e.provider.source==="provider-file"?e.providerName:void 0,n={...e.reasoningEffort&&e.reasoningEffort!=="default"?{reasoningEffort:e.reasoningEffort}:{},...e.serviceTier&&e.serviceTier!=="default"?{serviceTier:e.serviceTier}:{}},o=Yr(e);try{await t.request("initialize",{clientInfo:{name:"truesayer-node",title:"Truesayer Node",version:"0.1.0"},capabilities:{experimentalApi:!0,optOutNotificationMethods:[]}}),t.notify("initialized");let s=Gr(e.sessionStateDir);if(s)await t.request("thread/resume",{threadId:s,model:e.selectedModel,...r?{modelProvider:r}:{},cwd:e.cwd,approvalPolicy:"never",sandbox:"danger-full-access",experimentalRawEvents:!1,excludeTurns:!0,persistExtendedHistory:!0,developerInstructions:o,...n});else{let p=await t.request("thread/start",{model:e.selectedModel,...r?{modelProvider:r}:{},cwd:e.cwd,approvalPolicy:"never",sandbox:"danger-full-access",experimentalRawEvents:!1,persistExtendedHistory:!0,developerInstructions:o,...n});s=rn(p),Jr(e.sessionStateDir,s)}let a=await t.request("turn/start",{threadId:s,input:[{type:"text",text:Vr(e),text_elements:[]}],cwd:e.cwd,approvalPolicy:"never",model:e.selectedModel,...n}),i=nn(a),c=await t.waitForTurnCompleted(i);return{output:c.output.trim()||"Codex run completed",...c.inputTokens===void 0?{}:{inputTokens:c.inputTokens},...c.outputTokens===void 0?{}:{outputTokens:c.outputTokens},events:t.events,emittedEventIds:t.emittedEventIds}}catch(s){throw s instanceof Error&&(s.events=t.events,s.emittedEventIds=t.emittedEventIds),s}finally{t.close()}}function oe(e){return Array.from(new Set(e.map(t=>t.trim()).filter(Boolean)))}function fe(e){return typeof e=="object"&&e!==null?e:void 0}function at(e){return typeof e=="string"&&e.trim()?e.trim():void 0}function on(e){return e.replace(/\/+$/,"")}function dt(e,t){let r=on(e);return t==="v1/models"&&/\/v1$/i.test(r)?`${r}/models`:`${r}/${t}`}function sn(e){if(!e.baseUrl?.trim()||!e.apiKey?.trim())return[];let t=e.kind.toLowerCase(),r=e.baseUrl.trim(),n=[];return(t.includes("claude")||t.includes("anthropic"))&&n.push({url:dt(r,"v1/models"),auth:"anthropic"}),(t.includes("openai")||t==="codex")&&n.push({url:dt(r,"models"),auth:"bearer"}),n.filter((o,s,a)=>a.findIndex(i=>i.url===o.url)===s)}function an(e){let t=fe(e);return at(t?.id)??at(t?.model)}function dn(e){let t=fe(e)?.data;if(!Array.isArray(t))return[];let r=t.flatMap(s=>{let a=an(s);if(!a)return[];let i=fe(s),c=typeof i?.created=="number"?i.created:typeof i?.created_at=="number"?i.created_at:void 0;return[{id:a,created:c}]}),o=r.some(s=>s.created!==void 0)?[...r].sort((s,a)=>(a.created??Number.NEGATIVE_INFINITY)-(s.created??Number.NEGATIVE_INFINITY)):r;return oe(o.map(s=>s.id))}async function cn(e,t){let r={accept:"application/json"};e.auth==="anthropic"?(r["x-api-key"]=t,r["anthropic-version"]="2023-06-01"):r.authorization=`Bearer ${t}`;let n=await fetch(e.url,{method:"GET",headers:r});if(!n.ok)throw new Error(`HTTP ${n.status}`);let o=dn(await n.json().catch(()=>null));if(o.length===0)throw new Error("response did not contain model ids");return o}async function he(e,t){let r=t.apiKey?.trim();if(!r)throw new Error(`provider ${e} cannot discover models without an API key`);let n=sn(t);if(n.length===0)throw new Error(`provider ${e} cannot discover models without a supported baseUrl`);let o=[];for(let s of n)try{return await cn(s,r)}catch(a){o.push(`${s.url}: ${a instanceof Error?a.message:String(a)}`)}throw new Error(`provider ${e} model discovery failed: ${o.join("; ")}`)}async function ct(e,t){if(t.source==="provider-file"&&t.models?.length)return oe(t.models);if(t.source==="provider-file")try{return await he(e,t)}catch(r){return console.warn(`[node] provider ${e} model discovery unavailable:`,r instanceof Error?r.message:String(r)),[]}if(t.source!=="native")return[];try{if(e==="local-claude-code"&&t.kind==="claude-code")return await Be();if(e==="local-codex"&&t.kind==="codex")return await ot()}catch(r){console.warn(`[node] provider ${e} unavailable:`,r instanceof Error?r.message:String(r))}return[]}var un=15e3;function ln(e){let t=e.emittedEventIds??new Set;return(e.events??[]).filter(r=>!t.has(r.eventId))}var ut={"local-claude-code":{kind:"claude-code",displayName:"Local Claude Code",source:"native"},"local-codex":{kind:"codex",displayName:"Local Codex",source:"native"}};function pn(e){return e.split(/[-_\s]+/).filter(Boolean).map(t=>`${t[0]?.toUpperCase()??""}${t.slice(1)}`).join(" ")}function mn(e){if(!e?.trim()||!(0,N.existsSync)(e))return{};gn(e);let t;try{t=JSON.parse((0,N.readFileSync)(e,"utf8"))}catch{throw new Error(`provider file is not valid JSON: ${e}`)}let r=t&&typeof t=="object"&&!Array.isArray(t)&&t.providers&&typeof t.providers=="object"&&!Array.isArray(t.providers)?t.providers:{},n={};for(let[o,s]of Object.entries(r)){let a=o.trim();if(!a||!s||typeof s!="object"||Array.isArray(s))continue;let i=s,c=typeof i.kind=="string"&&i.kind.trim()?i.kind.trim():"openai-compatible",p=typeof i.displayName=="string"&&i.displayName.trim()?i.displayName.trim():pn(a),k=Array.isArray(i.models)?Array.from(new Set(i.models.filter(h=>typeof h=="string"&&h.trim().length>0).map(h=>h.trim()))):void 0,b=typeof i.apiKey=="string"&&i.apiKey.trim()?i.apiKey.trim():void 0,m=typeof i.baseUrl=="string"&&i.baseUrl.trim()?i.baseUrl.trim():void 0,y=typeof i.ANTHROPIC_DEFAULT_HAIKU_MODEL=="string"&&i.ANTHROPIC_DEFAULT_HAIKU_MODEL.trim()?i.ANTHROPIC_DEFAULT_HAIKU_MODEL.trim():void 0,d=typeof i.ANTHROPIC_DEFAULT_SONNET_MODEL=="string"&&i.ANTHROPIC_DEFAULT_SONNET_MODEL.trim()?i.ANTHROPIC_DEFAULT_SONNET_MODEL.trim():void 0,g=typeof i.ANTHROPIC_DEFAULT_OPUS_MODEL=="string"&&i.ANTHROPIC_DEFAULT_OPUS_MODEL.trim()?i.ANTHROPIC_DEFAULT_OPUS_MODEL.trim():void 0;n[a]={kind:c,displayName:p,source:"provider-file",...lt(c,k)?.length?{models:lt(c,k)}:{},...b?{apiKey:b}:{},...m?{baseUrl:m}:{},...y?{anthropicDefaultHaikuModel:y}:{},...d?{anthropicDefaultSonnetModel:d}:{},...g?{anthropicDefaultOpusModel:g}:{}}}return n}function gn(e){if(process.platform!=="win32")try{((0,N.statSync)(e).mode&63)!==0&&console.warn(`[node] provider file permissions are too broad; run chmod 600 ${e}`)}catch{return}}function yt(e){process.platform!=="win32"&&(0,N.chmodSync)(e,384)}function fn(e){return(0,N.mkdirSync)((0,vt.dirname)(e),{recursive:!0,mode:448}),(0,N.existsSync)(e)||(0,N.writeFileSync)(e,`${JSON.stringify({providers:{}},null,2)}
|
|
30
|
+
`,{mode:384}),yt(e),ye(e),e}function lt(e,t){if(!t?.length)return;if(!e.toLowerCase().includes("claude"))return t;let r=t.filter(n=>!["haiku","sonnet","opus"].includes(n.trim().toLowerCase()));return r.length>0?r:void 0}function K(e){if(!e)return;let t=e.trim();if(t)return t.length>8?`${t.slice(0,4)}***${t.slice(-4)}`:`${t.slice(0,4)}***`}function ie(e){let t=mn(e);for(let r of Object.keys(t))if(ut[r])throw new Error(`duplicate provider name: ${r}`);return{...ut,...t}}async function hn(e={}){let t=ie(e.providerFile),r={},n=e.probeProviderModels??ct,o=await Promise.all(Object.entries(t).map(async([s,a])=>[s,await n(s,a,{})]));for(let[s,a]of o){let i=t[s];i&&a.length!==0&&(r[s]={kind:i.kind,displayName:i.displayName,source:i.source,models:a,modelsDiscoveredBy:i.source==="provider-file"?i.models?.length?"provider-file":"api-probe":"sdk-probe",...i.source==="provider-file"&&i.baseUrl?{baseUrl:i.baseUrl}:{},...i.source==="provider-file"&&K(i.apiKey)?{apiKeyPreview:K(i.apiKey)}:{},...i.source==="provider-file"?{configEditable:!0}:{},...i.source==="provider-file"&&i.anthropicDefaultHaikuModel?{anthropicDefaultHaikuModel:i.anthropicDefaultHaikuModel}:{},...i.source==="provider-file"&&i.anthropicDefaultSonnetModel?{anthropicDefaultSonnetModel:i.anthropicDefaultSonnetModel}:{},...i.source==="provider-file"&&i.anthropicDefaultOpusModel?{anthropicDefaultOpusModel:i.anthropicDefaultOpusModel}:{}})}return r}function vn(e){return Object.fromEntries(Object.entries(e).map(([t,r])=>[t,{kind:r.kind,displayName:r.displayName,source:r.source,models:r.models,modelsDiscoveredBy:r.modelsDiscoveredBy}]))}function yn(e,t){return{type:"node.hello",payload:{nodeName:e.nodeName,nodeVersion:e.nodeVersion,platform:{os:process.platform,arch:process.arch,hostname:ht.default.hostname()},capabilities:{providers:vn(t),supportsWorktrees:!0,browserE2E:(e.probeBrowserE2E??wn)()}}}}var kn={"libnss3.so":["/usr/lib/x86_64-linux-gnu/libnss3.so","/usr/lib/aarch64-linux-gnu/libnss3.so","/usr/lib64/libnss3.so"],"libatk-bridge-2.0.so.0":["/usr/lib/x86_64-linux-gnu/libatk-bridge-2.0.so.0","/usr/lib/aarch64-linux-gnu/libatk-bridge-2.0.so.0","/usr/lib64/libatk-bridge-2.0.so.0"],"libcups.so.2":["/usr/lib/x86_64-linux-gnu/libcups.so.2","/usr/lib/aarch64-linux-gnu/libcups.so.2","/usr/lib64/libcups.so.2"],"libxkbcommon.so.0":["/usr/lib/x86_64-linux-gnu/libxkbcommon.so.0","/usr/lib/aarch64-linux-gnu/libxkbcommon.so.0","/usr/lib64/libxkbcommon.so.0"],"libgbm.so.1":["/usr/lib/x86_64-linux-gnu/libgbm.so.1","/usr/lib/aarch64-linux-gnu/libgbm.so.1","/usr/lib64/libgbm.so.1"],"libasound.so.2":["/usr/lib/x86_64-linux-gnu/libasound.so.2","/usr/lib/aarch64-linux-gnu/libasound.so.2","/usr/lib64/libasound.so.2"]};function wn(){let e=process.env.TRUESAYER_NODE_BROWSER_E2E?.trim().toLowerCase();if(e==="available"||e==="unavailable"||e==="unknown")return{status:e,diagnostics:["set by TRUESAYER_NODE_BROWSER_E2E"]};if(process.platform!=="linux")return{status:"unknown",diagnostics:["automatic browser dependency detection only supports linux"]};let t=Object.entries(kn).filter(([,r])=>!r.some(n=>(0,N.existsSync)(n))).map(([r])=>r);return t.length>0?{status:"unavailable",diagnostics:t.map(r=>`missing ${r}`)}:{status:"unknown",diagnostics:["linux chromium libraries found; browser binary availability not verified"]}}function U(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function v(e){return typeof e=="string"&&e.trim()?e.trim():""}function bn(e){return j(e)}function En(e,t){let r=e;if(!r?.trim())return{};let n=ie(r)[t];if(!n||n.source!=="provider-file")return{};let o={};return n.kind==="codex"||n.kind==="openai-compatible"?(n.apiKey&&(o.OPENAI_API_KEY=n.apiKey),n.baseUrl&&(o.OPENAI_BASE_URL=n.baseUrl),o):(n.apiKey&&(o.ANTHROPIC_API_KEY=n.apiKey),n.baseUrl&&(o.ANTHROPIC_BASE_URL=n.baseUrl),n.anthropicDefaultOpusModel&&(o.ANTHROPIC_DEFAULT_OPUS_MODEL=n.anthropicDefaultOpusModel,o.ANTHROPIC_DEFAULT_SONNET_MODEL=n.anthropicDefaultSonnetModel||n.anthropicDefaultOpusModel,o.ANTHROPIC_DEFAULT_HAIKU_MODEL=n.anthropicDefaultHaikuModel||n.anthropicDefaultOpusModel),o)}function _n(e){let t=U(e),r=t.id;return t.jsonrpc==="2.0"&&(typeof r=="string"||typeof r=="number"||r===null)&&("result"in t||"error"in t)}function Rn(e){let t=U(e),r=t.id;return t.jsonrpc==="2.0"&&(typeof r=="string"||typeof r=="number"||r===null)&&typeof t.method=="string"&&t.method.trim().length>0}function Tn(e,t){return JSON.stringify({jsonrpc:"2.0",id:e,result:t})}function Nn(e,t){return JSON.stringify({jsonrpc:"2.0",id:e,error:{code:-32e3,message:t}})}function In(e){let t=0,r=new Map;return{invokeTool(n){if(e.readyState!==1)throw new Error("node websocket is not open");let o=`tool:${++t}`,s={jsonrpc:"2.0",id:o,method:"tool.invoke",params:{...n,toolCallId:n.toolCallId??o}},a=new Promise((i,c)=>{r.set(o,{resolve:i,reject:c})});try{e.send(JSON.stringify(s))}catch(i){throw r.delete(o),i}return a},handleJsonRpcMessage(n){if(!_n(n)||n.id===null)return!1;let o=r.get(n.id);if(!o)return!1;if(r.delete(n.id),"error"in n&&n.error){let s=typeof n.error.message=="string"?n.error.message:"JSON-RPC tool call failed";return o.reject(new Error(s)),!0}return o.resolve(n.result),!0},rejectPending(n){for(let o of r.values())o.reject(n);r.clear()}}}function Pn(e){let t=U(e.runtimeSelection),r=v(t.providerName)||v(t.providerId),n=v(t.selectedModel)||v(t.modelId);if(!r||!n)throw new Error("agent.provision requires providerName and selectedModel");return{providerName:r,selectedModel:n}}var G=new Map;function ve(e){let t=v(e.repoPath);if(!t)throw new Error("repoPath is required");return t}function kt(e){let t=e.files.reduce((n,o)=>n+o.additions,0),r=e.files.reduce((n,o)=>n+o.deletions,0);return{repoRoot:e.repoRoot,baseSha:e.baseSha,diff:e.diff,diffHash:e.diffHash,hasStagedChanges:e.hasStagedChanges,files:e.files,totalAdditions:t,totalDeletions:r,updatedAt:new Date().toISOString()}}async function An(e){return kt(await V(ve(e)))}async function xn(e){let t=ve(e),r=v(e.baseSha);if(!r)throw new Error("baseSha is required");let n=v(e.scope);if(n!=="file"&&n!=="all")throw new Error("scope must be file or all");let o=n==="file"?v(e.filePath):void 0;if(n==="file"&&!o)throw new Error("filePath is required");return kt(await xe({worktreePath:t,baseSha:r,expectedDiffHash:v(e.diffHash)||void 0,filePath:o,expectedPatchHash:v(e.patchHash)||void 0}))}function Sn(e){let t=!1,r=null,n=new Set,o=()=>{if(r=null,t){n.clear();return}let i=Array.from(n).sort();n.clear(),e.emitMessage?.({type:"repo.changed",payload:{watchId:e.watchId,repoPath:e.repoPath,channelId:e.channelId,changedPaths:i,changedAt:new Date().toISOString()}})},s=(i,c)=>{t||(c&&n.add(String(c)),!r&&(r=setTimeout(o,750),typeof r.unref=="function"&&r.unref()))},a;try{a=(0,N.watch)(e.repoPath,{recursive:!0},s)}catch{a=(0,N.watch)(e.repoPath,s)}return{close(){t=!0,r&&(clearTimeout(r),r=null),n.clear(),a.close()}}}function Mn(e){let t=v(e.params.watchId)||"default",r=ve(e.params),n=v(e.params.channelId);if(!n)throw new Error("channelId is required");return G.get(t)?.close(),G.delete(t),G.set(t,Sn({watchId:t,repoPath:r,channelId:n,emitMessage:e.emitMessage})),{ok:!0,watchId:t,repoPath:r,channelId:n}}function Cn(e){let t=v(e.watchId)||"default";return G.get(t)?.close(),G.delete(t),{ok:!0,watchId:t}}async function On(e){let t=U(e.request.params);if(e.request.method==="provider.updateConfig")return Un({providerFile:e.providerFile,providers:e.providers,params:U(e.request.params)});if(e.request.method==="provider.discoverModels")return Dn({providerFile:e.providerFile,providers:e.providers,params:U(e.request.params)});if(e.request.method==="repo.snapshot")return An(t);if(e.request.method==="repo.restore")return xn(t);if(e.request.method==="repo.watch")return Mn({params:t,emitMessage:e.emitMessage});if(e.request.method==="repo.unwatch")return Cn(t);if(e.request.method!=="agent.provision")throw new Error(`unsupported node request method: ${e.request.method}`);let r=v(t.workspaceId),n=v(t.agentId);if(!r||!n)throw new Error("agent.provision requires workspaceId and agentId");let{providerName:o,selectedModel:s}=Pn(t),a=e.providers[o];if(!a||!a.models.includes(s))throw new Error(`provider/model is not available on this node: ${o}/${s}`);let i=ue({truesayerNodeHome:e.truesayerNodeHome,workspaceId:r,agentId:n});return{ok:!0,agentId:n,providerName:o,selectedModel:s,providerHome:i.providerHome,agentRoot:i.agentRoot,spaceRoot:i.spaceRoot,profileDir:i.profileDir,providerHomeExposedToFileTools:!1}}function wt(e,t){(0,N.writeFileSync)(e,`${JSON.stringify(t,null,2)}
|
|
31
|
+
`,{mode:384}),yt(e)}async function Dn(e){let t=v(e.params.providerName)||v(e.params.providerId);if(!t)throw new Error("provider.discoverModels requires providerName");let r=e.providers[t];if(!r)throw new Error(`provider is not available on this node: ${t}`);if(r.source!=="provider-file")throw new Error(`provider model discovery is not editable on this node: ${t}`);if(!e.providerFile?.trim())throw new Error("provider.discoverModels requires provider file");let o=ie(e.providerFile)[t];if(!o||o.source!=="provider-file")throw new Error(`provider not found in provider file: ${t}`);let s=await he(t,o),a=ye(e.providerFile),i=a.providers,c=i[t];if(!c||typeof c!="object"||Array.isArray(c))throw new Error(`provider not found in provider file: ${t}`);return i[t]={...c,models:s},wt(e.providerFile,a),e.providers[t]={...r,models:s,modelsDiscoveredBy:"api-probe"},{ok:!0,providerName:t,models:s,source:"api-probe"}}function ye(e){let t;try{t=JSON.parse((0,N.readFileSync)(e,"utf8"))}catch{throw new Error(`provider file is not valid JSON: ${e}`)}if(!t||typeof t!="object"||Array.isArray(t))throw new Error("provider file must be a JSON object");let r=t;if(!r.providers||typeof r.providers!="object"||Array.isArray(r.providers))throw new Error("provider file must contain a providers object");return r}function Un(e){let t=v(e.params.providerName)||v(e.params.providerId);if(!t)throw new Error("provider.updateConfig requires providerName");let r=e.providers[t];if(r&&(r.source!=="provider-file"||r.configEditable!==!0))throw new Error(`provider is not editable on this node: ${t}`);if(!e.providerFile?.trim())throw new Error("provider.updateConfig requires provider file");let n=Object.prototype.hasOwnProperty.call(e.params,"baseUrl"),o=Object.prototype.hasOwnProperty.call(e.params,"apiKey"),s=Object.prototype.hasOwnProperty.call(e.params,"kind"),a=Object.prototype.hasOwnProperty.call(e.params,"models"),i=Object.prototype.hasOwnProperty.call(e.params,"defaultModel"),c=Object.prototype.hasOwnProperty.call(e.params,"anthropicDefaultOpusModel"),p=Object.prototype.hasOwnProperty.call(e.params,"anthropicDefaultSonnetModel"),k=Object.prototype.hasOwnProperty.call(e.params,"anthropicDefaultHaikuModel");if(!s&&!n&&!o&&!a&&!i&&!c&&!p&&!k)throw new Error("provider.updateConfig requires at least one provider config field");let b=ye(e.providerFile),m=b.providers,y=m[t];if(y&&(typeof y!="object"||Array.isArray(y)))throw new Error(`provider not found in provider file: ${t}`);if(!r&&!s)throw new Error("provider.updateConfig requires kind when creating a provider");let d=y?{...y}:{kind:v(e.params.kind),displayName:v(e.params.displayName)||t};if(s){let f=v(e.params.kind);if(!["claude-compatible","openai-compatible"].includes(f))throw new Error("unsupported provider kind");d.kind=f}if(n){let f=v(e.params.baseUrl);if(!f)throw new Error("baseUrl cannot be empty");d.baseUrl=f}if(o){let f=v(e.params.apiKey);if(!f)throw new Error("apiKey cannot be empty");d.apiKey=f}if(a){let f=oe(Array.isArray(e.params.models)?e.params.models.filter(l=>typeof l=="string"):[]);if(f.length===0)throw new Error("models cannot be empty");d.models=f}if(i){let f=v(e.params.defaultModel);f?d.defaultModel=f:delete d.defaultModel}if(c){let f=v(e.params.anthropicDefaultOpusModel);if(!f)throw new Error("anthropicDefaultOpusModel cannot be empty");d.ANTHROPIC_DEFAULT_OPUS_MODEL=f}if(p){let f=v(e.params.anthropicDefaultSonnetModel);f?d.ANTHROPIC_DEFAULT_SONNET_MODEL=f:delete d.ANTHROPIC_DEFAULT_SONNET_MODEL}if(k){let f=v(e.params.anthropicDefaultHaikuModel);f?d.ANTHROPIC_DEFAULT_HAIKU_MODEL=f:delete d.ANTHROPIC_DEFAULT_HAIKU_MODEL}m[t]=d,wt(e.providerFile,b);let g=typeof d.kind=="string"?d.kind:r?.kind??"openai-compatible",h=Array.isArray(d.models)?d.models.filter(f=>typeof f=="string"):r?.models??[];return e.providers[t]={kind:g,displayName:typeof d.displayName=="string"?d.displayName:r?.displayName??t,source:"provider-file",models:h,modelsDiscoveredBy:r?.modelsDiscoveredBy??(h.length>0?"provider-file":"api-probe"),...typeof d.baseUrl=="string"?{baseUrl:d.baseUrl}:{},configEditable:!0,...K(typeof d.apiKey=="string"?d.apiKey:void 0)?{apiKeyPreview:K(typeof d.apiKey=="string"?d.apiKey:void 0)}:{},...typeof d.defaultModel=="string"?{defaultModel:d.defaultModel}:{},...typeof d.ANTHROPIC_DEFAULT_OPUS_MODEL=="string"?{anthropicDefaultOpusModel:d.ANTHROPIC_DEFAULT_OPUS_MODEL}:{},...typeof d.ANTHROPIC_DEFAULT_SONNET_MODEL=="string"?{anthropicDefaultSonnetModel:d.ANTHROPIC_DEFAULT_SONNET_MODEL}:{},...typeof d.ANTHROPIC_DEFAULT_HAIKU_MODEL=="string"?{anthropicDefaultHaikuModel:d.ANTHROPIC_DEFAULT_HAIKU_MODEL}:{}},{ok:!0,providerName:t,...s?{kind:g}:{},apiKeyPreview:K(typeof d.apiKey=="string"?d.apiKey:void 0)??null,...a?{models:Array.isArray(d.models)?d.models:[]}:{},...i?{defaultModel:typeof d.defaultModel=="string"?d.defaultModel:null}:{},...k?{anthropicDefaultHaikuModel:typeof d.ANTHROPIC_DEFAULT_HAIKU_MODEL=="string"?d.ANTHROPIC_DEFAULT_HAIKU_MODEL:null}:{},...p?{anthropicDefaultSonnetModel:typeof d.ANTHROPIC_DEFAULT_SONNET_MODEL=="string"?d.ANTHROPIC_DEFAULT_SONNET_MODEL:null}:{},...c?{anthropicDefaultOpusModel:typeof d.ANTHROPIC_DEFAULT_OPUS_MODEL=="string"?d.ANTHROPIC_DEFAULT_OPUS_MODEL:null}:{},needsRestart:!0}}var Fn=/api[-_]?key|secret|token|password|authorization|base[-_]?url|api[-_]?url/i;function se(e,t=0){if(t>6)return"[redacted:depth]";if(Array.isArray(e))return e.map(n=>se(n,t+1));if(!e||typeof e!="object")return typeof e=="string"&&e.length>1e3?`${e.slice(0,1e3)}...`:e;let r={};for(let[n,o]of Object.entries(e))r[n]=Fn.test(n)?"[redacted]":se(o,t+1);return r}function Hn(e){if(["opus","sonnet","haiku"].includes(e.selectedModel))return e.selectedModel;let t=e.providerFile?.trim()?ie(e.providerFile)[e.providerName]:void 0,r=e.provider.anthropicDefaultOpusModel??t?.anthropicDefaultOpusModel,n=e.provider.anthropicDefaultSonnetModel??t?.anthropicDefaultSonnetModel,o=e.provider.anthropicDefaultHaikuModel??t?.anthropicDefaultHaikuModel;return e.selectedModel===n?"sonnet":e.selectedModel===o?"haiku":e.selectedModel===r||r?"opus":e.selectedModel}function Ln(e){return e.provider.kind==="claude-code"||e.provider.kind==="claude-compatible"?Hn(e):e.selectedModel}function pt(e){return e==="default"||e==="none"||e==="minimal"||e==="low"||e==="medium"||e==="high"||e==="xhigh"||e==="max"?e:void 0}function mt(e){return e==="default"||e==="disabled"||e==="adaptive"?e:void 0}function gt(e){return e==="default"||e==="fast"||e==="flex"?e:void 0}function $n(e){let t=U(e);if(t.type!=="agent_run.assigned")return null;let r=v(t.agentRunId),n=U(t.payload),o=U(n.runtimeSelection),s=v(n.workspaceId),a=v(n.agentId),i=v(n.agentSessionId),c=v(n.prompt),p=v(o.providerName),k=v(o.selectedModel);return!r||!s||!a||!i||!c||!p||!k?null:{type:"agent_run.assigned",agentRunId:r,payload:{workspaceId:s,agentId:a,agentSessionId:i,channelId:v(n.channelId)||null,systemPrompt:v(n.systemPrompt)||null,prompt:c,toolMode:v(n.toolMode)==="silent_relevance"?"silent_relevance":"full",repoAlias:v(n.repoAlias)||"default",repoPath:v(n.repoPath)||null,baseBranch:v(n.baseBranch)||null,runtimeSelection:{providerName:p,selectedModel:k,...pt(o.reasoningEffort)?{reasoningEffort:pt(o.reasoningEffort)}:{},...mt(o.thinkingMode)?{thinkingMode:mt(o.thinkingMode)}:{},...gt(o.serviceTier)?{serviceTier:gt(o.serviceTier)}:{}}}}}function jn(e){switch(e.kind){case"claude-code":case"claude-compatible":return Je;case"codex":case"openai-compatible":return it;default:return async()=>{throw new Error(`unsupported node provider kind: ${e.kind}`)}}}function qn(e){return{type:"agent_run.rejected",agentRunId:e.agentRunId,payload:{reason:"capability_mismatch",providerName:e.payload.runtimeSelection.providerName,selectedModel:e.payload.runtimeSelection.selectedModel}}}function Bn(e){return["<truesayer_workspace_context>",`Current channel worktree root: ${e.cwd}`,e.worktreePath?`Node-managed worktree path: ${e.worktreePath}`:null,e.repoPath?`Source repository path: ${e.repoPath}`:null,"Treat the current channel as the active repository workspace. Unless the user explicitly says otherwise, repository modifications may span the whole worktree.","</truesayer_workspace_context>","",e.prompt].filter(r=>!!r).join(`
|
|
32
|
+
`)}async function Wn(e){let{providerName:t,selectedModel:r}=e.message.payload.runtimeSelection,n=e.providers[t];if(!n||!n.models.includes(r)){let d=qn(e.message);return await e.emitMessage?.(d),{messages:[d]}}let o=Oe({truesayerNodeHome:e.truesayerNodeHome,workspaceId:e.message.payload.workspaceId,agentId:e.message.payload.agentId,agentSessionId:e.message.payload.agentSessionId,agentRunId:e.message.agentRunId,repoAlias:e.message.payload.repoAlias||"default"}),s=o.worktreeDir,a={cwd:s};if(e.message.payload.repoPath){let d=await Ie({actionId:e.message.agentRunId,baseBranch:e.message.payload.baseBranch||"main",repoPath:e.message.payload.repoPath,worktreePath:o.worktreeDir});s=d.path,a.cwd=s,a.repoPath=e.message.payload.repoPath,a.worktreePath=d.path,a.branch=d.branch,a.baseSha=d.baseSha,a.baseRef=d.baseRef,a.headSha=await Pe(d.path)}else a.worktreePath=o.worktreeDir;let i=Bn({prompt:e.message.payload.prompt,cwd:s,repoPath:e.message.payload.repoPath,worktreePath:v(a.worktreePath)}),c=[],p=async d=>{c.push(d),await e.emitMessage?.(d)},k=1,b=0,m=async(d,g)=>{k+=1,await p({type:"agent_run.event_batch",agentRunId:e.message.agentRunId,payload:{events:[{eventId:`${e.message.agentRunId}:event:${k}`,eventSeq:k,type:d,payload:g}]}})},y=e.invokeTool?async d=>{b+=1;let g=d.toolCallId||`${e.message.agentRunId}:tool:${b}`;await m("agent_run.tool.started",{toolCallId:g,toolName:d.toolName,status:"started",args:se(d.args)});try{let h=await e.invokeTool({...d,toolCallId:g});return await m("agent_run.tool.completed",{toolCallId:g,toolName:d.toolName,status:"completed",result:se(h)}),h}catch(h){throw await m("agent_run.tool.failed",{toolCallId:g,toolName:d.toolName,status:"failed",error:h instanceof Error?h.message:String(h)}),h}}:void 0;await p({type:"agent_run.accepted",agentRunId:e.message.agentRunId,payload:{agentId:e.message.payload.agentId,agentSessionId:e.message.payload.agentSessionId,providerName:t,selectedModel:r}}),await p({type:"agent_run.event_batch",agentRunId:e.message.agentRunId,payload:{events:[{eventId:`${e.message.agentRunId}:workspace-prepared`,eventSeq:1,type:"agent_run.workspace_prepared",payload:{workspacePrepared:!0,fileToolRoot:"space",providerHomeExposedToFileTools:!1,...a}}]}});try{let d=e.runProviderAgent??jn(n),g;try{g=await d({agentRunId:e.message.agentRunId,agentId:e.message.payload.agentId,agentSessionId:e.message.payload.agentSessionId,prompt:i,toolMode:e.message.payload.toolMode,provider:n,availableProviders:e.providers,providerName:t,selectedModel:Ln({providerFile:e.providerFile,providerName:t,provider:n,selectedModel:r}),reasoningEffort:e.message.payload.runtimeSelection.reasoningEffort,thinkingMode:e.message.payload.runtimeSelection.thinkingMode,serviceTier:e.message.payload.runtimeSelection.serviceTier,cwd:s,env:bn({...o.providerEnv,...En(e.providerFile,t)}),sessionStateDir:o.contextDir,systemPrompt:e.message.payload.systemPrompt??void 0,invokeTool:y,emitEvent:async f=>{await p({type:"agent_run.event_batch",agentRunId:e.message.agentRunId,payload:{events:[f]}})}})}catch(f){let l=f instanceof Error?f:void 0,w=l?.emittedEventIds??new Set,C=(l?.events??[]).filter(T=>!w.has(T.eventId));throw C.length&&await p({type:"agent_run.event_batch",agentRunId:e.message.agentRunId,payload:{events:C}}),f}let h=ln(g);h.length&&await p({type:"agent_run.event_batch",agentRunId:e.message.agentRunId,payload:{events:h}}),await p({type:"agent_run.completed",agentRunId:e.message.agentRunId,payload:{output:g.output,...g.inputTokens===void 0?{}:{inputTokens:g.inputTokens},...g.outputTokens===void 0?{}:{outputTokens:g.outputTokens}}})}catch(d){await p({type:"agent_run.failed",agentRunId:e.message.agentRunId,payload:{error:d instanceof Error?d.message:String(d)}})}return{layout:o,messages:c}}function Kn(e,t){let r=new URL(e);return r.protocol=r.protocol==="https:"?"wss:":"ws:",r.pathname="/api/nodes/ws",r.search="",r.searchParams.set("apiKey",t),r.toString()}function Gn(){let e=globalThis.WebSocket;if(!e)throw new Error("global WebSocket is not available; run with Node 22+ or pass WebSocketImpl");return e}function ft(e){e.readyState===1&&e.send(JSON.stringify({type:"node.heartbeat"}))}function Jn(e){ft(e);let t=setInterval(()=>{ft(e)},un);return typeof t.unref=="function"&&t.unref(),()=>clearInterval(t)}async function bt(e){let t=e.WebSocketImpl??Gn(),r=fn(e.providerFile??Me(e.truesayerNodeHome)),n=await hn({providerFile:r,probeProviderModels:e.probeProviderModels}),o=yn({...e,providerFile:r},n),s=new t(Kn(e.serverUrl,e.apiKey)),a=In(s),i=new Set;await new Promise((c,p)=>{let k=!1,b=null;s.addEventListener("open",()=>{s.send(JSON.stringify(o))}),s.addEventListener("message",m=>{let y=typeof m.data=="string"?m.data:"";try{let d=JSON.parse(y);if(a.handleJsonRpcMessage(d))return;if(Rn(d)){(async()=>{try{let h=await On({truesayerNodeHome:e.truesayerNodeHome,providerFile:r,providers:n,request:d,runProviderAgent:e.runProviderAgent,emitMessage:f=>{s.send(JSON.stringify(f))}});s.send(Tn(d.id,h))}catch(h){s.send(Nn(d.id,h instanceof Error?h.message:String(h)))}})();return}if(d.type==="node.ready"){k=!0,b??=Jn(s);return}let g=$n(d);if(g){if(i.has(g.agentRunId))return;i.add(g.agentRunId),Wn({truesayerNodeHome:e.truesayerNodeHome,providerFile:r,providers:n,message:g,invokeTool:a.invokeTool,runProviderAgent:e.runProviderAgent,emitMessage:h=>{s.send(JSON.stringify(h))}}).catch(h=>{s.send(JSON.stringify({type:"agent_run.failed",agentRunId:g.agentRunId,payload:{error:h instanceof Error?h.message:String(h)}}))})}}catch{}}),s.addEventListener("error",m=>{b?.(),b=null,p(new Error(`node ws error: ${String(m)}`))}),s.addEventListener("close",()=>{b?.(),b=null,a.rejectPending(new Error("node websocket closed")),k?c():p(new Error("node ws closed before node.ready"))})})}var zn=["DATABASE_URL","TRUESAYER_API_KEY","TRUESAYER_ADMIN_API_KEY","BETTER_AUTH_SECRET","ASSET_STORAGE_DRIVER","S3_ENDPOINT","S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY","S3_REGION","S3_FORCE_PATH_STYLE"];function ae(e){let t=`${e}=`,r=process.argv.find(o=>o.startsWith(t));if(r)return r.slice(t.length);let n=process.argv.indexOf(e);if(n>=0)return process.argv[n+1]}function Yn(...e){return process.argv.some(t=>e.includes(t))}function Vn(){let e=zn.filter(t=>process.env[t]?.trim());if(e.length>0)throw new Error(`forbidden Server-only env present in Node process: ${e.join(", ")}`)}function Xn(){let e=Number(process.versions.node.split(".")[0]);if(!Number.isFinite(e)||e<22)throw new Error(`truesayer-node requires Node.js >=22. Current: ${process.version}`)}function Et(){return"Usage: truesayer-node --server <url> --api-key <key> [--node-name <name>] [--node-home <path>]"}function _t(){if(process.env.npm_package_version)return process.env.npm_package_version;try{let e=JSON.parse((0,Rt.readFileSync)((0,Tt.join)(__dirname,"..","package.json"),"utf8"));return typeof e.version=="string"&&e.version.trim()?e.version:"0.1.0"}catch{return"0.1.0"}}async function Zn(){if(Yn("--version","-v")){console.log(_t());return}Xn(),Vn();let e=ae("--server")??process.env.SERVER_URL,t=ae("--api-key")??process.env.NODE_API_KEY,r=ae("--node-name")??process.env.NODE_NAME??"truesayer-node",n=ae("--node-home")??process.env.TRUESAYER_NODE_HOME;if(!e)throw new Error(`--server or SERVER_URL is required
|
|
33
|
+
${Et()}`);if(!t)throw new Error(`--api-key or NODE_API_KEY is required
|
|
34
|
+
${Et()}`);await bt({serverUrl:e,apiKey:t,nodeName:r,nodeVersion:_t(),truesayerNodeHome:n})}Zn().catch(e=>{console.error("[node] Fatal:",e instanceof Error?e.message:String(e)),process.exit(1)});
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@truesayer/node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Truesayer local Node runtime CLI",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=22"
|
|
12
|
+
},
|
|
13
|
+
"overrides": {
|
|
14
|
+
"qs": "6.15.2"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"truesayer-node": "dist/node.js"
|
|
18
|
+
},
|
|
19
|
+
"main": "dist/node.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/node.js",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "tsx watch src/node.ts",
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"prepack": "pnpm run build",
|
|
28
|
+
"start": "node dist/node.js",
|
|
29
|
+
"typecheck": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.144",
|
|
33
|
+
"@anthropic-ai/sdk": "0.96.0",
|
|
34
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
35
|
+
"@openai/codex": "^0.125.0",
|
|
36
|
+
"simple-git": "^3.36.0",
|
|
37
|
+
"zod": "^4"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.19.17",
|
|
41
|
+
"tsup": "^8.5.0",
|
|
42
|
+
"tsx": "^4.19.0",
|
|
43
|
+
"typescript": "^5.7.0"
|
|
44
|
+
}
|
|
45
|
+
}
|