shennian 0.3.63 → 0.3.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +78 -63
- package/dist/src/agents/adapter.d.ts +5 -1
- package/dist/src/agents/claude.d.ts +3 -2
- package/dist/src/agents/claude.js +6 -6
- package/dist/src/agents/workbuddy.d.ts +3 -2
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/index.js +3 -3
- package/dist/src/manager/runtime.js +8 -8
- package/dist/src/native-fusion/codex-files.d.ts +2 -0
- package/dist/src/native-fusion/codex-files.js +1 -0
- package/dist/src/native-fusion/codex-parser.d.ts +1 -2
- package/dist/src/native-fusion/codex-parser.js +3 -5
- package/dist/src/native-fusion/codex-user-input.d.ts +11 -0
- package/dist/src/native-fusion/codex-user-input.js +5 -0
- package/dist/src/native-fusion/file-watcher.d.ts +13 -5
- package/dist/src/native-fusion/file-watcher.js +1 -1
- package/dist/src/native-fusion/local-repository.d.ts +2 -0
- package/dist/src/native-fusion/local-repository.js +2 -2
- package/dist/src/native-fusion/parser-common.d.ts +1 -0
- package/dist/src/native-fusion/parser-common.js +4 -4
- package/dist/src/native-fusion/parsers.d.ts +1 -0
- package/dist/src/native-fusion/parsers.js +2 -2
- package/dist/src/native-fusion/service.d.ts +3 -0
- package/dist/src/native-fusion/service.js +2 -2
- package/dist/src/native-fusion/state.js +2 -2
- package/dist/src/native-fusion/types.d.ts +4 -0
- package/dist/src/personal-sync/epoch.d.ts +17 -0
- package/dist/src/personal-sync/epoch.js +2 -0
- package/dist/src/personal-sync/identity.js +2 -2
- package/dist/src/personal-sync/machine-authority.d.ts +11 -0
- package/dist/src/personal-sync/machine-authority.js +1 -0
- package/dist/src/personal-sync/metrics.d.ts +7 -1
- package/dist/src/personal-sync/metrics.js +1 -1
- package/dist/src/personal-sync/paths.d.ts +2 -5
- package/dist/src/personal-sync/paths.js +1 -1
- package/dist/src/relay/client.d.ts +8 -2
- package/dist/src/relay/client.js +1 -1
- package/dist/src/session/attachment-transfer-store.d.ts +10 -3
- package/dist/src/session/canonical-store.d.ts +2 -0
- package/dist/src/session/canonical-store.js +5 -5
- package/dist/src/session/deletion-sync-state.d.ts +18 -0
- package/dist/src/session/deletion-sync-state.js +2 -2
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/handlers/fs.js +1 -1
- package/dist/src/session/handlers/message-status.js +1 -1
- package/dist/src/session/handlers/title.js +1 -1
- package/dist/src/session/handlers/tool-detail.js +1 -1
- package/dist/src/session/handlers/voice-window.js +1 -1
- package/dist/src/session/history-subscriptions.js +1 -1
- package/dist/src/session/index-publisher.d.ts +2 -2
- package/dist/src/session/index-publisher.js +1 -1
- package/dist/src/session/manager.d.ts +1 -0
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/native-session-lifecycle.js +4 -4
- package/dist/src/session/queue.d.ts +1 -0
- package/dist/src/session/queue.js +4 -4
- package/dist/src/session/store.d.ts +16 -0
- package/dist/src/session/store.js +2 -2
- package/node_modules/@shennian/wire/dist/frames.d.ts +3 -0
- package/node_modules/@shennian/wire/dist/fs.d.ts +16 -6
- package/node_modules/@shennian/wire/dist/machine.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/message-payload.js +2 -0
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +76 -33
- package/node_modules/@shennian/wire/dist/session-sync.js +50 -2
- package/node_modules/@shennian/wire/dist/session.d.ts +20 -10
- package/node_modules/@shennian/wire/dist/tool-detail.d.ts +3 -2
- package/node_modules/@shennian/wire/package.json +1 -1
- package/package.json +1 -1
- package/dist/src/personal-sync/account-namespace.d.ts +0 -6
- package/dist/src/personal-sync/account-namespace.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
`).trim()}function
|
|
1
|
+
import Y from"node:http";import{randomBytes as V,randomUUID as p}from"node:crypto";import S from"node:fs";import b from"node:os";import w from"node:path";import{AVAILABLE_BUILTIN_AGENT_TYPES as Q,extractPayloadText as X,formatExternalConversationText as Z,formatExternalMessageLine as ee,formatExternalAttachmentReference as te,isAgentHiddenPayload as ne,isToolPayload as re}from"@shennian/wire";import{ManagerRegistry as ae}from"./registry.js";import{readMessages as se}from"../session/store.js";import{listProjectedSessions as ie}from"../session/projection.js";import{RetiredChannelRuntime as oe,RetiredWeChatRpaSessionBindingSync as ce,createRetiredAutomationLane as le,createRetiredChannelApiClient as de,retiredDirectWeChat as ue,retiredDirectWeChat as $,splitExternalReplyText as he,weChatChannelConversationId as ge}from"./retired-external-channel-compat.js";import{loadConfig as N,resolveShennianPath as pe}from"../config/index.js";import{buildExternalChannelInstructions as me}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as v}from"../personal-sync/epoch.js";const C=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),A=12*6e4;let _=null;function Je(a){_=a}function Ye(){return _}function R(a){return w.resolve(a||b.homedir())}function u(a,e,n){a.writeHead(e,{"content-type":"application/json; charset=utf-8"}),a.end(JSON.stringify(n))}function fe(a){return/^agent-(.+)-\d+$/.exec(a)?.[1]??null}function ye(a){return a==="manager"||a==="pi"?!1:a.startsWith("custom:")?!0:Q.includes(a)}function we(a){const e=/^agent-.+-(\d+)$/.exec(a);if(!e)return null;const n=Number(e[1]);return Number.isInteger(n)&&n>=0?n:null}function B(a){return a.replace(/\r\n/g,`
|
|
2
|
+
`).trim()}function Ie(a){const e=a.map((n,t)=>[`${t+1}. \u4E8B\u4EF6\u7C7B\u578B\uFF1A${n.kind}`,`Worker\uFF1A${n.workerSessionId}`,n.runId?`Run\uFF1A${n.runId}`:"",`\u6458\u8981\uFF1A
|
|
3
3
|
${n.summary}`].filter(Boolean).join(`
|
|
4
4
|
`));return`Manager \u4E8B\u4EF6\u6536\u4EF6\u7BB1\u4E2D\u6709 ${a.length} \u6761\u5F85\u5904\u7406\u4E8B\u4EF6\uFF1A
|
|
5
5
|
|
|
@@ -7,7 +7,7 @@ ${e.join(`
|
|
|
7
7
|
|
|
8
8
|
`)}
|
|
9
9
|
|
|
10
|
-
\u8BF7\u7ED3\u5408\u7528\u6237\u76EE\u6807\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u7EE7\u7EED\u7B49\u5F85\u3001\u521B\u5EFA\u6216\u6307\u6D3E worker\u3001\u505C\u6B62 worker\u3001\u8BE2\u95EE\u7528\u6237\uFF0C\u6216\u5411\u7528\u6237\u6C47\u62A5\u3002`}function Ie(a){try{const e=JSON.parse(a),n=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",r=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=r.length>220?`${r.slice(0,220)}...`:r;return s?`[${n}] ${t}: ${s}`:`[${n}] ${t}`}catch{return"[tool]"}}function B(a){if(!a||typeof a!="object")return;const e=a,n=String(e.kind||""),t=String(e.name||""),r=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),c=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(n!=="image"&&n!=="video"&&n!=="file")&&!(!t||!r||!Number.isFinite(c)||c<0)&&!(!o&&!i))return{kind:n,name:t,mimeType:r,size:c,...o?{localPath:o}:{},...i?{url:i}:{}}}function q(a){const e=a.binding&&typeof a.binding=="object"&&!Array.isArray(a.binding)?a.binding:{},n=String(e.sessionId||a.managerSessionId||"").trim(),t=String(e.channelId||"").trim(),r=String(e.conversationId||"").trim(),s=String(e.conversationName||a.conversation||"").trim(),o=String(e.workDir||a.workDir||"").trim();if(!n)throw new Error("WeChat tool sessionId is required");if(!t)throw new Error("WeChat tool channelId is required");if(!r)throw new Error("WeChat tool conversationId is required");if(!s)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:n,channelId:t,conversationId:r,conversationName:s,workDir:o}}function ke(a,e){const n=[...a].sort((l,d)=>l.ts-d.ts),t=[];let r=null,s=null,o=null,i="";const c=()=>{if(!r)return;const l=i.trim();l&&t.push({...r,id:`${r.id}-compact`,payload:l}),r=null,s=null,o=null,i=""};for(const l of n){if(te(l.payload)){c();continue}if(l.role==="user"){c(),t.push(l);continue}if(ne(l.payload)){c(),t.push({...l,payload:Ie(l.payload)});continue}const d=Q(l.payload);if(!d.trim())continue;const h=me(l.id),g=ye(l.id);r&&r.role===l.role&&s===h&&h&&g!==null&&o!==null&&g===o+1?(i+=d,r.ts=l.ts,o=g):(c(),r=l,s=h,o=g,i=d)}return c(),t.slice(-e).sort((l,d)=>d.ts-l.ts)}async function Se(a){const e=[];let n=0;for await(const r of a){const s=Buffer.from(r);if(n+=s.byteLength,Number.isFinite(C)&&C>0&&n>C)throw new Error(`Manager IPC request body is too large. Max: ${C} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function f(a,e,n,t){a.client.sendRes({type:"res",id:e,ok:n,...n?{payload:t}:{error:String(t.error||"unknown error")}})}function L(a){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(a)}function F(){return Re()?w.join(b.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):ge("runtime","manager-ipc.json")}function Re(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Je{opts;registry=new re;channelRuntime;weChatRpaSessionSync;server=null;ipcUrl=null;ipcToken=Y(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??ce(),this.channelRuntime=e.channelRuntime??new ie((n,t)=>{this.handleExternalMessage(n,t)},n=>this.registry.createReplyTarget(n).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane}),this.weChatRpaSessionSync=new oe({channelRuntime:this.channelRuntime,getLocalMachineId:()=>process.env.SHENNIAN_MACHINE_ID||N().machineId||"",listSessions:()=>this.listWeChatRpaSessionContexts(),listAuthoritativeSessions:()=>this.listAuthoritativeWeChatRpaSessionContexts()})}*listWeChatRpaSessionContexts(){const e=this.opts.getRuntime().sessions;for(const[n,t]of e)yield{sessionId:n,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,externalChannel:t.externalChannel??null}}async listAuthoritativeWeChatRpaSessionContexts(){const e=N(),n=process.env.SHENNIAN_MACHINE_ID||e.machineId||"";if(!e.machineToken||!n)return null;const t=this.listLocalWeChatRpaSessionContexts(n);return(await le({serverUrl:e.serverUrl,machineToken:e.machineToken}).listBindings({machineId:n})).bindings.filter(s=>s.enabled!==!1).map(s=>{const o=t.get(s.sessionId);return{sessionId:s.sessionId,workDir:s.sessionWorkDir?.trim()||o?.workDir?.trim()||b.homedir(),agentType:s.sessionAgentType?.trim()||o?.agentType||void 0,agentSessionId:s.sessionAgentSessionId??o?.agentSessionId??null,modelId:s.sessionModelId??o?.modelId??null,externalChannel:{connected:!0,configured:!0,type:"wechat-rpa",channelId:s.id,machineId:s.machineId,name:s.conversationName,canReply:s.allowReply!==!1,systemPrompt:null,wechatRpaSource:"wechat-channel",wechatRpaGroups:[{name:s.conversationName}],downloadAttachments:s.downloadMedia!==!1}}})}listLocalWeChatRpaSessionContexts(e){const n=new Map;for(const t of this.listWeChatRpaSessionContexts())n.set(t.sessionId,t);for(const t of se())!t?.id||t.deletedAt||t.machineId&&e&&t.machineId!==e||n.has(t.id)||n.set(t.id,{sessionId:t.id,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,modelId:t.modelId,externalChannel:t.externalChannel??null});return n}async notifyWeChatRpaSessionBinding(e){await this.weChatRpaSessionSync.reconcileSession(e).catch(n=>{console.error(`[wechat-rpa-sync] reconcileSession failed sessionId=${e.sessionId}: ${n instanceof Error?n.message:String(n)}`)})}async reconcileWeChatRpaSessionBindings(){await this.weChatRpaSessionSync.reconcileAll().catch(e=>{console.error(`[wechat-rpa-sync] reconcileAll failed: ${e instanceof Error?e.message:String(e)}`)})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=J.createServer((n,t)=>{this.handleIpc(n,t)}),this.server.requestTimeout=A,this.server.timeout=A,this.server.keepAliveTimeout=A,this.server.headersTimeout=A+5e3,await new Promise((n,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>n())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.reconcileWeChatRpaSessionBindings(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=F();S.mkdirSync(w.dirname(e),{recursive:!0}),S.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),S.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=F(),n=JSON.parse(S.readFileSync(e,"utf-8"));(n.url===this.ipcUrl||n.token===this.ipcToken)&&S.unlinkSync(e)}catch{}}getInjectedEnv(e,n,t,r){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:n??"",SHENNIAN_MANAGER_WORKDIR:R(t),SHENNIAN_MANAGER_MODEL:r,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:R(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,n,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,defaultWorkerAgentType:n??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const n=this.registry.getManager(e);return{agentType:n?.defaultWorkerAgentType??null,modelId:n?.defaultWorkerModelId??null}}noteManagerAgentSession(e,n,t,r){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:n,workDir:R(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:r,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,n){if(!this.findWorker(e))return;const r={lastActivityAt:new Date().toISOString(),runId:n.runId};n.agentSessionId&&(r.agentSessionId=n.agentSessionId);const s=`${e}:${n.runId}`;if(n.state==="delta"&&n.text&&!n.thinking){const i=(this.workerTextAcc.get(s)??"")+n.text;this.workerTextAcc.set(s,i);const c=_(i);c&&(r.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(n.state==="final"||n.state==="error"||n.state==="aborted"){r.status=n.state;const i=_(this.workerTextAcc.get(s)??"");i&&(r.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else n.state==="start"&&(r.status="running");const o=this.registry.updateWorker(e,r);o&&(n.state==="final"||n.state==="error"||n.state==="aborted")&&this.enqueueWorkerEvent(o.managedBy,o,n.state,n.runId,n.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const n=this.opts.getRuntime();if(e.method.includes("channel")||e.method.includes("wechat-rpa")){f(n,e.id,!1,{error:"feature_retired"});return}const t=e.params??{};try{const r=String(t.managerSessionId||t.sessionId||"");if(!r)throw new Error("sessionId is required");const s=this.registry.getManager(r),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",l=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${r}`),managerSessionId:r,sessionId:r,workDir:String(t.workDir||s?.workDir||""),type:"websocket",name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(o){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"wechat-rpa",{includeSecret:!0})});return}if(i){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${r}`),managerSessionId:r,sessionId:r,workDir:R(String(t.workDir||s?.workDir||"")),name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:O(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:U(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(c){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(r);this.broadcastManagerChannelStatus(r),f(n,e.id,!0,d);return}if(l){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:r,idempotencyKey:typeof t.idempotencyKey=="string"?t.idempotencyKey:null,replyId:typeof t.replyId=="string"?t.replyId:null,reason:typeof t.reason=="string"?t.reason:"user_cancelled"});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(r){f(n,e.id,!1,{error:r instanceof Error?r.message:String(r)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const n=this.opts.getRuntime();if(!n.client?.sendEvent)return;const t=this.registry.getManager(e),r=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");n.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:r}}})}async handleIpc(e,n){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(n,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),r=await Se(e);if(t.pathname.startsWith("/channel/")||t.pathname.startsWith("/external/")||t.pathname.startsWith("/wechat-rpa/")){u(n,410,{ok:!1,error:"feature_retired"});return}const s=String(r.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!s)throw new Error("managerSessionId is required");const o=this.registry.getManager(s);if(t.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const i=new Set(this.opts.getRuntime().sessions.keys());u(n,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(r.agentType||r.agent||o.defaultWorkerAgentType||"codex");if(!fe(i))throw new Error(`Unsupported manager worker agent: ${i}`);const c=R(String(r.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const l=String(r.message||"");if(!l)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:c,summary:l.slice(0,120)}),h=String(r.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,i,c,l,null,h),u(n,200,{ok:!0,session:d});return}if(t.pathname==="/sessions/send"){const i=String(r.sessionId||""),c=String(r.message||""),l=r.enqueue===void 0?!0:!!r.enqueue,d=this.registry.getWorkerForManager(s,i);if(!d)throw new Error("Worker not found in this manager scope");const h=String(r.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));l?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const l=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(n,200,{ok:!0,queue:l});return}if(t.pathname==="/sessions/queue/edit"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${p()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||""),text:String(r.message||r.text||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${p()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${p()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(r.sessionId||""),c=Number(r.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=ae(i,{limit:Math.max(c*20,c)});u(n,200,{ok:!0,messages:ke(d,c),rawMessageCount:d.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(n,200,{ok:!0,path:w.join(o.workDir,".shennian")});return}if(t.pathname==="/external/reply"){const i=typeof r.replyTarget=="string"?this.registry.getReplyTarget(r.replyTarget):this.registry.getLatestReplyTargetForManager(s),c=String(r.text||""),l=B(r.attachment),d=String(r.idempotencyKey||p()),h=String(r.channelId||""),g=String(r.conversationId||""),M=!i&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,I=i?.channelId||h||M?.channelId||"",E=i?.conversationId||g||M?.conversationId||"",D=I?this.channelRuntime.getChannelById(I):void 0,z=D&&(!!(i||h)||D.managedBy!=="session-sync");if(I&&z){if(!E)throw new Error("No external channel target is available for this Manager");const y=await this.channelRuntime.reply({managerSessionId:s,channelId:I,conversationId:E,messageId:i?.messageId??void 0,text:c,attachment:l,idempotencyKey:d});u(n,y.ok?200:400,y);return}const x=await this.tryDirectWeChatRpaReply(s,c,l);if(x){u(n,x.ok?200:400,x);return}let k;try{k=await this.sendManagedWeComReply({managerSessionId:s,text:c,attachment:l,idempotencyKey:d})}catch(y){const W=y instanceof Error?y.message:String(y);if(!L(W))throw y;k={ok:!1,error:W}}if(k.ok){u(n,200,{ok:!0,payload:k.payload});return}if(!L(k.error||"")||!I||!E){u(n,400,{ok:!1,error:k.error||"External send failed"});return}u(n,400,{ok:!1,error:`No local external channel is configured for ${I}`});return}if(t.pathname==="/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(r.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof r.name=="string"?r.name:void 0,enabled:!!r.enabled,wsUrl:typeof r.wsUrl=="string"?r.wsUrl:void 0,token:typeof r.token=="string"?r.token:void 0,canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/tool/read"){const i=q(r),c=m(r.limit)??10,l=typeof r.traceId=="string"?r.traceId:void 0;let d;try{d=await this.weChatAutomationLane.run(`wechat-tool:read:${i.channelId}`,()=>de(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,limit:c,recentLimit:c,download:r.download==="never"?"never":"auto",traceId:l,timeoutMs:m(r.timeoutMs)}))}catch(h){u(n,400,{ok:!1,...j(h,l)});return}u(n,200,{ok:!0,messages:d.messages,outDir:d.outDir,helperTracePath:d.helperTracePath,activityGuardPath:d.activityGuardPath});return}if(t.pathname==="/wechat-rpa/tool/send"){const i=q(r),c=String(r.text||""),l=B(r.attachment),d=typeof r.traceId=="string"?r.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${i.channelId}`,()=>P(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,text:c,attachment:l,traceId:d,timeoutMs:m(r.timeoutMs)}))}catch(g){u(n,400,{ok:!1,...j(g,d)});return}u(n,200,{ok:!0,...h});return}if(t.pathname==="/wechat-rpa/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=R(String(r.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const c=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(r.id||`wechat-rpa:${s}`),managerSessionId:s,sessionId:s,workDir:i,name:typeof r.name=="string"?r.name:void 0,agentType:typeof r.agentType=="string"?r.agentType:void 0,agentSessionId:typeof r.agentSessionId=="string"?r.agentSessionId:null,modelId:typeof r.modelId=="string"?r.modelId:null,enabled:!!r.enabled,groups:O(r.groups),canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0,source:U(r.source),pollIntervalMs:m(r.pollIntervalMs),recentLimit:m(r.recentLimit),idleSeconds:m(r.idleSeconds),forceForeground:r.forceForeground===void 0?void 0:!!r.forceForeground,noRestore:r.noRestore===void 0?void 0:!!r.noRestore,downloadAttachments:r.downloadAttachments===void 0?void 0:!!r.downloadAttachments,downloadAttachmentsDir:typeof r.downloadAttachmentsDir=="string"?r.downloadAttachmentsDir:void 0,selfNickname:typeof r.selfNickname=="string"?r.selfNickname:void 0,selfTriggerMarker:typeof r.selfTriggerMarker=="string"?r.selfTriggerMarker:void 0,deferInitialPoll:r.deferInitialPoll===void 0?void 0:!!r.deferInitialPoll,privacyConsentAccepted:r.privacyConsentAccepted===void 0?void 0:!!r.privacyConsentAccepted,flowScriptPath:typeof r.flowScriptPath=="string"?r.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:c});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i,messages:c});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof r.idempotencyKey=="string"?r.idempotencyKey:null,replyId:typeof r.replyId=="string"?r.replyId:null,reason:typeof r.reason=="string"?r.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,...i});return}u(n,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(n,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,n,t,r,s,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${p()}`,method:"chat.send",params:{sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o}})}async dispatchChatEnqueue(e,n,t,r,s,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${p()}`,method:"chat.enqueue",params:{sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o}})}async tryDirectWeChatRpaReply(e,n,t){const r=this.opts.getRuntime().sessions.get(e),s=r?.externalChannel;if(!s||s.type!=="wechat-rpa"||!s.channelId||!s.name)return null;const o=process.env.SHENNIAN_MACHINE_ID||N().machineId||"";if(s.machineId&&o&&s.machineId!==o)return{ok:!1,error:`WeChat \u7ED1\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u673A\u5668 (${s.machineId})\uFF0C\u8BF7\u5728\u7ED1\u5B9A\u673A\u5668\u4E0A\u53D1\u9001\u3002`};if(!n.trim()&&!t)return{ok:!1,error:"text or attachment is required"};const i=r?.workDir||process.cwd(),c={sessionId:e,channelId:s.channelId,conversationId:he(s.name),conversationName:s.name,workDir:i};try{return{ok:!0,payload:await this.weChatAutomationLane.run(`wechat-tool:send:${c.channelId}`,()=>P(c,{conversation:c.conversationName,workDir:c.workDir,sessionId:c.sessionId,text:n,attachment:t}))}}catch(l){return{ok:!1,error:l instanceof Error?l.message:String(l)}}}async sendManagedWeComReply(e){const n=ue(e.text);if(!n.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const r=[];for(const[s,o]of n.entries()){const i=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:n.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};r.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:n.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};r.push(s.payload)}return{ok:!0,payload:r.length===1?r[0]:r}}enqueueWorkerEvent(e,n,t,r,s){this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:n.sessionId,kind:t==="final"?"worker.final":t==="error"?"worker.error":"worker.aborted",priority:"normal",runId:r??null,summary:s||n.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"})&&this.drainManagerInbox(e)}handleExternalMessage(e,n){const t=this.channelRuntime.getChannelById(n.channelId)??this.channelRuntime.getManagerChannel(e,n.channelType),r=this.channelRuntime.getChannelStatusById(n.channelId)??this.channelRuntime.getManagerChannelStatus(e),s=this.registry.getManager(e),o=t?.agentType||(s?"manager":"codex"),i=t?.workDir||s?.workDir||process.cwd(),c=t?.agentSessionId??s?.agentSessionId??null,l=t?.modelId||s?.modelId||"",d=Me(n.attachments,n.channelType),h=Ae(n);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:i,agentSessionId:c,modelId:l,text:h,messageId:n.messageId,attachments:d,externalChannel:xe(r),replyTarget:n.replyTarget})}async dispatchExternalMessage(e){const n=typeof e.messageId=="string"&&e.messageId.trim()?e.messageId.trim():void 0;await this.opts.dispatchReq({type:"req",id:`external-enqueue-${p()}`,method:"chat.enqueue",params:{sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",queueMessageId:n,clientMessageId:n,attachments:e.attachments,externalChannel:e.externalChannel??null,replyTarget:e.replyTarget}})}scanWorkerHealth(){const e=Date.now(),n=this.registry.load();for(const t of Object.values(n.workers)){if(t.status!=="running")continue;const r=e-Date.parse(t.createdAt);if(r<10*6e4)continue;const s=t.healthNotifiedAt?Date.parse(t.healthNotifiedAt):0;if(s&&e-s<10*6e4)continue;const o=n.managers[t.managedBy];if(!o)continue;const i=Math.max(0,Math.floor((e-Date.parse(t.lastActivityAt))/6e4)),c=`\u5DF2\u8FD0\u884C ${Math.floor(r/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
10
|
+
\u8BF7\u7ED3\u5408\u7528\u6237\u76EE\u6807\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u7EE7\u7EED\u7B49\u5F85\u3001\u521B\u5EFA\u6216\u6307\u6D3E worker\u3001\u505C\u6B62 worker\u3001\u8BE2\u95EE\u7528\u6237\uFF0C\u6216\u5411\u7528\u6237\u6C47\u62A5\u3002`}function ke(a){try{const e=JSON.parse(a),n=e.type==="tool_result"||e.result?"tool_result":"tool_call",t=e.name||"tool",r=typeof e.result=="string"?e.result.replace(/\s+/g," ").trim():"",s=r.length>220?`${r.slice(0,220)}...`:r;return s?`[${n}] ${t}: ${s}`:`[${n}] ${t}`}catch{return"[tool]"}}function q(a){if(!a||typeof a!="object")return;const e=a,n=String(e.kind||""),t=String(e.name||""),r=String(e.mimeType||""),s=String(e.dataBase64||""),o=String(e.localPath||""),i=String(e.url||""),c=Number(e.size||0);if(s)throw new Error("Manager IPC external attachments must use localPath or url; dataBase64 is not accepted");if(!(n!=="image"&&n!=="video"&&n!=="file")&&!(!t||!r||!Number.isFinite(c)||c<0)&&!(!o&&!i))return{kind:n,name:t,mimeType:r,size:c,...o?{localPath:o}:{},...i?{url:i}:{}}}function L(a){const e=a.binding&&typeof a.binding=="object"&&!Array.isArray(a.binding)?a.binding:{},n=String(e.sessionId||a.managerSessionId||"").trim(),t=String(e.channelId||"").trim(),r=String(e.conversationId||"").trim(),s=String(e.conversationName||a.conversation||"").trim(),o=String(e.workDir||a.workDir||"").trim();if(!n)throw new Error("WeChat tool sessionId is required");if(!t)throw new Error("WeChat tool channelId is required");if(!r)throw new Error("WeChat tool conversationId is required");if(!s)throw new Error("WeChat tool conversationName is required");if(!o)throw new Error("WeChat tool workDir is required");return{sessionId:n,channelId:t,conversationId:r,conversationName:s,workDir:o}}function Se(a,e){const n=[...a].sort((l,d)=>l.ts-d.ts),t=[];let r=null,s=null,o=null,i="";const c=()=>{if(!r)return;const l=i.trim();l&&t.push({...r,id:`${r.id}-compact`,payload:l}),r=null,s=null,o=null,i=""};for(const l of n){if(ne(l.payload)){c();continue}if(l.role==="user"){c(),t.push(l);continue}if(re(l.payload)){c(),t.push({...l,payload:ke(l.payload)});continue}const d=X(l.payload);if(!d.trim())continue;const h=fe(l.id),g=we(l.id);r&&r.role===l.role&&s===h&&h&&g!==null&&o!==null&&g===o+1?(i+=d,r.ts=l.ts,o=g):(c(),r=l,s=h,o=g,i=d)}return c(),t.slice(-e).sort((l,d)=>d.ts-l.ts)}async function Re(a){const e=[];let n=0;for await(const r of a){const s=Buffer.from(r);if(n+=s.byteLength,Number.isFinite(C)&&C>0&&n>C)throw new Error(`Manager IPC request body is too large. Max: ${C} bytes.`);e.push(s)}const t=Buffer.concat(e).toString("utf-8");return t?JSON.parse(t):{}}function f(a,e,n,t){a.client.sendRes({type:"res",id:e,ok:n,...n?{payload:t}:{error:String(t.error||"unknown error")}})}function F(a){return/binding not found|unknown method|not supported|relay is not connected|no external channel/i.test(a)}function O(){return Me()?w.join(b.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):pe("runtime","manager-ipc.json")}function Me(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class Ve{opts;registry=new ae;channelRuntime;weChatRpaSessionSync;server=null;ipcUrl=null;ipcToken=V(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;weChatAutomationLane;constructor(e){this.opts=e,this.weChatAutomationLane=e.weChatAutomationLane??le(),this.channelRuntime=e.channelRuntime??new oe((n,t)=>{this.handleExternalMessage(n,t)},n=>this.registry.createReplyTarget(n).replyTarget,{createWeChatRpaProductRunner:e.createWeChatRpaProductRunner,weChatAutomationLane:this.weChatAutomationLane}),this.weChatRpaSessionSync=new ce({channelRuntime:this.channelRuntime,getLocalMachineId:()=>process.env.SHENNIAN_MACHINE_ID||N().machineId||"",listSessions:()=>this.listWeChatRpaSessionContexts(),listAuthoritativeSessions:()=>this.listAuthoritativeWeChatRpaSessionContexts()})}*listWeChatRpaSessionContexts(){const e=this.opts.getRuntime().sessions;for(const[n,t]of e)yield{sessionId:n,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,externalChannel:t.externalChannel??null}}async listAuthoritativeWeChatRpaSessionContexts(){const e=N(),n=process.env.SHENNIAN_MACHINE_ID||e.machineId||"";if(!e.machineToken||!n)return null;const t=this.listLocalWeChatRpaSessionContexts(n);return(await de({serverUrl:e.serverUrl,machineToken:e.machineToken}).listBindings({machineId:n})).bindings.filter(s=>s.enabled!==!1).map(s=>{const o=t.get(s.sessionId);return{sessionId:s.sessionId,workDir:s.sessionWorkDir?.trim()||o?.workDir?.trim()||b.homedir(),agentType:s.sessionAgentType?.trim()||o?.agentType||void 0,agentSessionId:s.sessionAgentSessionId??o?.agentSessionId??null,modelId:s.sessionModelId??o?.modelId??null,externalChannel:{connected:!0,configured:!0,type:"wechat-rpa",channelId:s.id,machineId:s.machineId,name:s.conversationName,canReply:s.allowReply!==!1,systemPrompt:null,wechatRpaSource:"wechat-channel",wechatRpaGroups:[{name:s.conversationName}],downloadAttachments:s.downloadMedia!==!1}}})}listLocalWeChatRpaSessionContexts(e){const n=new Map;for(const t of this.listWeChatRpaSessionContexts())n.set(t.sessionId,t);for(const t of ie())!t?.id||t.deletedAt||t.machineId&&e&&t.machineId!==e||n.has(t.id)||n.set(t.id,{sessionId:t.id,workDir:t.workDir,agentType:t.agentType,agentSessionId:t.agentSessionId,modelId:t.modelId,externalChannel:t.externalChannel??null});return n}async notifyWeChatRpaSessionBinding(e){await this.weChatRpaSessionSync.reconcileSession(e).catch(n=>{console.error(`[wechat-rpa-sync] reconcileSession failed sessionId=${e.sessionId}: ${n instanceof Error?n.message:String(n)}`)})}async reconcileWeChatRpaSessionBindings(){await this.weChatRpaSessionSync.reconcileAll().catch(e=>{console.error(`[wechat-rpa-sync] reconcileAll failed: ${e instanceof Error?e.message:String(e)}`)})}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=Y.createServer((n,t)=>{this.handleIpc(n,t)}),this.server.requestTimeout=A,this.server.timeout=A,this.server.keepAliveTimeout=A,this.server.headersTimeout=A+5e3,await new Promise((n,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>n())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.reconcileWeChatRpaSessionBindings(),this.broadcastConfiguredChannelStatuses(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=O();S.mkdirSync(w.dirname(e),{recursive:!0}),S.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),S.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=O(),n=JSON.parse(S.readFileSync(e,"utf-8"));(n.url===this.ipcUrl||n.token===this.ipcToken)&&S.unlinkSync(e)}catch{}}getInjectedEnv(e,n,t,r){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:n??"",SHENNIAN_MANAGER_WORKDIR:R(t),SHENNIAN_MANAGER_MODEL:r,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:R(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagerWorkerDefaults(e,n,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,defaultWorkerAgentType:n??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const n=this.registry.getManager(e);return{agentType:n?.defaultWorkerAgentType??null,modelId:n?.defaultWorkerModelId??null}}noteManagerAgentSession(e,n,t,r){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:n,workDir:R(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:r,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,n){if(!this.findWorker(e))return;const r={lastActivityAt:new Date().toISOString(),runId:n.runId};n.agentSessionId&&(r.agentSessionId=n.agentSessionId);const s=`${e}:${n.runId}`;if(n.state==="delta"&&n.text&&!n.thinking){const i=(this.workerTextAcc.get(s)??"")+n.text;this.workerTextAcc.set(s,i);const c=B(i);c&&(r.summary=c.length>160?`${c.slice(0,160)}...`:c)}if(n.state==="final"||n.state==="error"||n.state==="aborted"){r.status=n.state;const i=B(this.workerTextAcc.get(s)??"");i&&(r.summary=i.length>240?`${i.slice(0,240)}...`:i),this.workerTextAcc.delete(s)}else n.state==="start"&&(r.status="running");const o=this.registry.updateWorker(e,r);o&&(n.state==="final"||n.state==="error"||n.state==="aborted")&&this.enqueueWorkerEvent(o.managedBy,o,n.state,n.runId,n.message)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){const n=this.opts.getRuntime();if(e.method.includes("channel")||e.method.includes("wechat-rpa")){f(n,e.id,!1,{error:"feature_retired"});return}const t=e.params??{};try{const r=String(t.managerSessionId||t.sessionId||"");if(!r)throw new Error("sessionId is required");const s=this.registry.getManager(r),o=e.method==="session.wechat-rpa.channel.get"||e.method==="manager.wechat-rpa.channel.get",i=e.method==="session.wechat-rpa.channel.upsert"||e.method==="manager.wechat-rpa.channel.upsert",c=e.method==="session.wechat-rpa.channel.sync"||e.method==="manager.wechat-rpa.channel.sync",l=e.method==="session.wechat-rpa.outbound.cancel"||e.method==="manager.wechat-rpa.outbound.cancel";if(e.method==="manager.channel.get"){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"websocket",{includeSecret:!0})});return}if(e.method==="manager.channel.upsert"){const d=await this.channelRuntime.upsertManagerChannel({id:String(t.id||`websocket:${r}`),managerSessionId:r,sessionId:r,workDir:String(t.workDir||s?.workDir||""),type:"websocket",name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,wsUrl:typeof t.wsUrl=="string"?t.wsUrl:void 0,token:typeof t.token=="string"?t.token:void 0,canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(o){f(n,e.id,!0,{channel:this.channelRuntime.getManagerChannel(r,"wechat-rpa",{includeSecret:!0})});return}if(i){const d=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(t.id||`wechat-rpa:${r}`),managerSessionId:r,sessionId:r,workDir:R(String(t.workDir||s?.workDir||"")),name:typeof t.name=="string"?t.name:void 0,agentType:typeof t.agentType=="string"?t.agentType:void 0,agentSessionId:typeof t.agentSessionId=="string"?t.agentSessionId:null,modelId:typeof t.modelId=="string"?t.modelId:null,enabled:!!t.enabled,groups:U(t.groups),canReply:t.canReply===void 0?void 0:!!t.canReply,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:void 0,source:H(t.source),pollIntervalMs:m(t.pollIntervalMs),recentLimit:m(t.recentLimit),idleSeconds:m(t.idleSeconds),forceForeground:t.forceForeground===void 0?void 0:!!t.forceForeground,noRestore:t.noRestore===void 0?void 0:!!t.noRestore,downloadAttachments:t.downloadAttachments===void 0?void 0:!!t.downloadAttachments,downloadAttachmentsDir:typeof t.downloadAttachmentsDir=="string"?t.downloadAttachmentsDir:void 0,selfNickname:typeof t.selfNickname=="string"?t.selfNickname:void 0,selfTriggerMarker:typeof t.selfTriggerMarker=="string"?t.selfTriggerMarker:void 0,deferInitialPoll:t.deferInitialPoll===void 0?void 0:!!t.deferInitialPoll,privacyConsentAccepted:t.privacyConsentAccepted===void 0?void 0:!!t.privacyConsentAccepted,flowScriptPath:typeof t.flowScriptPath=="string"?t.flowScriptPath:void 0});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,{channel:d});return}if(c){const d=await this.channelRuntime.syncManagerWeChatRpaChannel(r);this.broadcastManagerChannelStatus(r),f(n,e.id,!0,d);return}if(l){const d=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:r,idempotencyKey:typeof t.idempotencyKey=="string"?t.idempotencyKey:null,replyId:typeof t.replyId=="string"?t.replyId:null,reason:typeof t.reason=="string"?t.reason:"user_cancelled"});this.broadcastManagerChannelStatus(r),f(n,e.id,!0,d);return}throw new Error(`Unsupported manager app method: ${e.method}`)}catch(r){f(n,e.id,!1,{error:r instanceof Error?r.message:String(r)})}}broadcastConfiguredChannelStatuses(){for(const e of this.channelRuntime.listManagerChannelStatuses())this.broadcastManagerChannelStatus(e.managerSessionId)}broadcastManagerChannelStatus(e){const n=this.opts.getRuntime();if(!n.client?.sendEvent)return;const t=this.registry.getManager(e),r=this.channelRuntime.getManagerChannelStatus(e),s=this.channelRuntime.getManagerChannel(e,"wechat-rpa")??this.channelRuntime.getManagerChannel(e,"websocket");n.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:t?"manager":s?.agentType,agentSessionId:t?.agentSessionId??s?.agentSessionId??null,modelId:t?.modelId??s?.modelId??null,workDir:t?.workDir??s?.workDir,externalChannel:r}}})}async handleIpc(e,n){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){u(n,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),r=await Re(e);if(t.pathname.startsWith("/channel/")||t.pathname.startsWith("/external/")||t.pathname.startsWith("/wechat-rpa/")){u(n,410,{ok:!1,error:"feature_retired"});return}const s=String(r.managerSessionId||e.headers["x-shennian-manager-session-id"]||"");if(!s)throw new Error("managerSessionId is required");const o=this.registry.getManager(s);if(t.pathname==="/sessions/list"){if(!o)throw new Error("Manager runtime is not registered");const i=new Set(this.opts.getRuntime().sessions.keys());u(n,200,{ok:!0,sessions:this.registry.listWorkers(s,{runningSessionIds:i})});return}if(t.pathname==="/sessions/start"){if(!o)throw new Error("Manager runtime is not registered");const i=String(r.agentType||r.agent||o.defaultWorkerAgentType||"codex");if(!ye(i))throw new Error(`Unsupported manager worker agent: ${i}`);const c=R(String(r.workDir||o.workDir));if(c!==o.workDir)throw new Error("Manager can only start workers in the same workDir");const l=String(r.message||"");if(!l)throw new Error("message is required");const d=this.registry.addWorker({managerSessionId:s,agentType:i,workDir:c,summary:l.slice(0,120)}),h=String(r.modelId||(i===o.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));await this.dispatchChatSend(d.sessionId,i,c,l,null,h),u(n,200,{ok:!0,session:d});return}if(t.pathname==="/sessions/send"){const i=String(r.sessionId||""),c=String(r.message||""),l=r.enqueue===void 0?!0:!!r.enqueue,d=this.registry.getWorkerForManager(s,i);if(!d)throw new Error("Worker not found in this manager scope");const h=String(r.modelId||(d.agentType===o?.defaultWorkerAgentType?o.defaultWorkerModelId??"":""));l?await this.dispatchChatEnqueue(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/queue"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const l=this.opts.getRuntime().chatQueue?.getSnapshot(i);u(n,200,{ok:!0,queue:l});return}if(t.pathname==="/sessions/queue/edit"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-edit-${p()}`,method:"chat.queue.edit",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||""),text:String(r.message||r.text||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/queue/delete"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-queue-delete-${p()}`,method:"chat.queue.delete",params:{sessionId:i,queueMessageId:String(r.queueMessageId||r.messageId||"")}}),u(n,200,{ok:!0,queue:this.opts.getRuntime().chatQueue?.getSnapshot(i)});return}if(t.pathname==="/sessions/stop"||t.pathname==="/sessions/terminate"){const i=String(r.sessionId||"");if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");await this.opts.dispatchReq({type:"req",id:`manager-abort-${p()}`,method:"chat.abort",params:{sessionId:i}}),this.registry.updateWorker(i,{status:"aborted"}),u(n,200,{ok:!0});return}if(t.pathname==="/sessions/read"){const i=String(r.sessionId||""),c=Number(r.limit||200);if(!this.registry.getWorkerForManager(s,i))throw new Error("Worker not found in this manager scope");const d=se(i,{limit:Math.max(c*20,c)});u(n,200,{ok:!0,messages:Se(d,c),rawMessageCount:d.length});return}if(t.pathname==="/memory/path"){if(!o)throw new Error("Manager runtime is not registered");u(n,200,{ok:!0,path:w.join(o.workDir,".shennian")});return}if(t.pathname==="/external/reply"){const i=typeof r.replyTarget=="string"?this.registry.getReplyTarget(r.replyTarget):this.registry.getLatestReplyTargetForManager(s),c=String(r.text||""),l=q(r.attachment),d=String(r.idempotencyKey||p()),h=String(r.channelId||""),g=String(r.conversationId||""),M=!i&&(!h||!g)?await this.channelRuntime.getDefaultReplyTarget(s).catch(()=>null):null,I=i?.channelId||h||M?.channelId||"",E=i?.conversationId||g||M?.conversationId||"",W=I?this.channelRuntime.getChannelById(I):void 0,J=W&&(!!(i||h)||W.managedBy!=="session-sync");if(I&&J){if(!E)throw new Error("No external channel target is available for this Manager");const y=await this.channelRuntime.reply({managerSessionId:s,channelId:I,conversationId:E,messageId:i?.messageId??void 0,text:c,attachment:l,idempotencyKey:d});u(n,y.ok?200:400,y);return}const x=await this.tryDirectWeChatRpaReply(s,c,l);if(x){u(n,x.ok?200:400,x);return}let k;try{k=await this.sendManagedWeComReply({managerSessionId:s,text:c,attachment:l,idempotencyKey:d})}catch(y){const P=y instanceof Error?y.message:String(y);if(!F(P))throw y;k={ok:!1,error:P}}if(k.ok){u(n,200,{ok:!0,payload:k.payload});return}if(!F(k.error||"")||!I||!E){u(n,400,{ok:!1,error:k.error||"External send failed"});return}u(n,400,{ok:!1,error:`No local external channel is configured for ${I}`});return}if(t.pathname==="/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"websocket")});return}if(t.pathname==="/channel/upsert"){if(!o)throw new Error("Manager runtime is not registered");const i=await this.channelRuntime.upsertManagerChannel({id:String(r.id||`websocket:${s}`),managerSessionId:s,workDir:o.workDir,type:"websocket",name:typeof r.name=="string"?r.name:void 0,enabled:!!r.enabled,wsUrl:typeof r.wsUrl=="string"?r.wsUrl:void 0,token:typeof r.token=="string"?r.token:void 0,canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0});this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i});return}if(t.pathname==="/wechat-rpa/tool/read"){const i=L(r),c=m(r.limit)??10,l=typeof r.traceId=="string"?r.traceId:void 0;let d;try{d=await this.weChatAutomationLane.run(`wechat-tool:read:${i.channelId}`,()=>ue(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,limit:c,recentLimit:c,download:r.download==="never"?"never":"auto",traceId:l,timeoutMs:m(r.timeoutMs)}))}catch(h){u(n,400,{ok:!1,...z(h,l)});return}u(n,200,{ok:!0,messages:d.messages,outDir:d.outDir,helperTracePath:d.helperTracePath,activityGuardPath:d.activityGuardPath});return}if(t.pathname==="/wechat-rpa/tool/send"){const i=L(r),c=String(r.text||""),l=q(r.attachment),d=typeof r.traceId=="string"?r.traceId:void 0;let h;try{h=await this.weChatAutomationLane.run(`wechat-tool:send:${i.channelId}`,()=>$(i,{conversation:i.conversationName,workDir:i.workDir,sessionId:i.sessionId,text:c,attachment:l,traceId:d,timeoutMs:m(r.timeoutMs)}))}catch(g){u(n,400,{ok:!1,...z(g,d)});return}u(n,200,{ok:!0,...h});return}if(t.pathname==="/wechat-rpa/channel/get"){u(n,200,{ok:!0,channel:this.channelRuntime.getManagerChannel(s,"wechat-rpa",{includeSecret:!0})});return}if(t.pathname==="/wechat-rpa/channel/upsert"){const i=R(String(r.workDir||o?.workDir||""));if(!i)throw new Error("workDir is required");const c=await this.channelRuntime.upsertManagerWeChatRpaChannel({id:String(r.id||`wechat-rpa:${s}`),managerSessionId:s,sessionId:s,workDir:i,name:typeof r.name=="string"?r.name:void 0,agentType:typeof r.agentType=="string"?r.agentType:void 0,agentSessionId:typeof r.agentSessionId=="string"?r.agentSessionId:null,modelId:typeof r.modelId=="string"?r.modelId:null,enabled:!!r.enabled,groups:U(r.groups),canReply:r.canReply===void 0?void 0:!!r.canReply,systemPrompt:typeof r.systemPrompt=="string"?r.systemPrompt:void 0,source:H(r.source),pollIntervalMs:m(r.pollIntervalMs),recentLimit:m(r.recentLimit),idleSeconds:m(r.idleSeconds),forceForeground:r.forceForeground===void 0?void 0:!!r.forceForeground,noRestore:r.noRestore===void 0?void 0:!!r.noRestore,downloadAttachments:r.downloadAttachments===void 0?void 0:!!r.downloadAttachments,downloadAttachmentsDir:typeof r.downloadAttachmentsDir=="string"?r.downloadAttachmentsDir:void 0,selfNickname:typeof r.selfNickname=="string"?r.selfNickname:void 0,selfTriggerMarker:typeof r.selfTriggerMarker=="string"?r.selfTriggerMarker:void 0,deferInitialPoll:r.deferInitialPoll===void 0?void 0:!!r.deferInitialPoll,privacyConsentAccepted:r.privacyConsentAccepted===void 0?void 0:!!r.privacyConsentAccepted,flowScriptPath:typeof r.flowScriptPath=="string"?r.flowScriptPath:void 0});o&&this.registry.upsertManager({...o,status:o.status}),this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:c});return}if(t.pathname==="/wechat-rpa/channel/sync"){const{channel:i,messages:c}=await this.channelRuntime.syncManagerWeChatRpaChannel(s);this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,channel:i,messages:c});return}if(t.pathname==="/wechat-rpa/outbound/cancel"){const i=await this.channelRuntime.cancelManagerWeChatRpaOutbound({managerSessionId:s,idempotencyKey:typeof r.idempotencyKey=="string"?r.idempotencyKey:null,replyId:typeof r.replyId=="string"?r.replyId:null,reason:typeof r.reason=="string"?r.reason:"user_cancelled"});this.broadcastManagerChannelStatus(s),u(n,200,{ok:!0,...i});return}u(n,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){u(n,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async dispatchChatSend(e,n,t,r,s,o){await this.opts.dispatchReq({type:"req",id:`manager-send-${p()}`,method:"chat.send",params:{...v(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o}})}async dispatchChatEnqueue(e,n,t,r,s,o){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${p()}`,method:"chat.enqueue",params:{...v(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o}})}async tryDirectWeChatRpaReply(e,n,t){const r=this.opts.getRuntime().sessions.get(e),s=r?.externalChannel;if(!s||s.type!=="wechat-rpa"||!s.channelId||!s.name)return null;const o=process.env.SHENNIAN_MACHINE_ID||N().machineId||"";if(s.machineId&&o&&s.machineId!==o)return{ok:!1,error:`WeChat \u7ED1\u5B9A\u5C5E\u4E8E\u5176\u4ED6\u673A\u5668 (${s.machineId})\uFF0C\u8BF7\u5728\u7ED1\u5B9A\u673A\u5668\u4E0A\u53D1\u9001\u3002`};if(!n.trim()&&!t)return{ok:!1,error:"text or attachment is required"};const i=r?.workDir||process.cwd(),c={sessionId:e,channelId:s.channelId,conversationId:ge(s.name),conversationName:s.name,workDir:i};try{return{ok:!0,payload:await this.weChatAutomationLane.run(`wechat-tool:send:${c.channelId}`,()=>$(c,{conversation:c.conversationName,workDir:c.workDir,sessionId:c.sessionId,text:n,attachment:t}))}}catch(l){return{ok:!1,error:l instanceof Error?l.message:String(l)}}}async sendManagedWeComReply(e){const n=he(e.text);if(!n.length&&!e.attachment)return{ok:!1,error:"text or attachment is required"};const t=this.opts.getRuntime().client;if(!t||typeof t.sendReq!="function")return{ok:!1,error:"Relay is not connected"};const r=[];for(const[s,o]of n.entries()){const i=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,text:o,idempotencyKey:n.length>1?`${e.idempotencyKey}:${s+1}`:e.idempotencyKey}});if(!i.ok)return{ok:!1,error:i.error||"External send failed"};r.push(i.payload)}if(e.attachment){const s=await t.sendReq({type:"req",id:`external-send-${p()}`,method:"external.send",params:{managerSessionId:e.managerSessionId,attachment:e.attachment,idempotencyKey:n.length?`${e.idempotencyKey}:attachment`:e.idempotencyKey}});if(!s.ok)return{ok:!1,error:s.error||"External send failed"};r.push(s.payload)}return{ok:!0,payload:r.length===1?r[0]:r}}enqueueWorkerEvent(e,n,t,r,s){this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:n.sessionId,kind:t==="final"?"worker.final":t==="error"?"worker.error":"worker.aborted",priority:"normal",runId:r??null,summary:s||n.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"})&&this.drainManagerInbox(e)}handleExternalMessage(e,n){const t=this.channelRuntime.getChannelById(n.channelId)??this.channelRuntime.getManagerChannel(e,n.channelType),r=this.channelRuntime.getChannelStatusById(n.channelId)??this.channelRuntime.getManagerChannelStatus(e),s=this.registry.getManager(e),o=t?.agentType||(s?"manager":"codex"),i=t?.workDir||s?.workDir||process.cwd(),c=t?.agentSessionId??s?.agentSessionId??null,l=t?.modelId||s?.modelId||"",d=Ce(n.attachments,n.channelType),h=Te(n);this.dispatchExternalMessage({sessionId:e,agentType:o,workDir:i,agentSessionId:c,modelId:l,text:h,messageId:n.messageId,attachments:d,externalChannel:be(r),replyTarget:n.replyTarget})}async dispatchExternalMessage(e){const n=typeof e.messageId=="string"&&e.messageId.trim()?e.messageId.trim():void 0;await this.opts.dispatchReq({type:"req",id:`external-enqueue-${p()}`,method:"chat.enqueue",params:{...v(),sessionId:e.sessionId,text:e.text,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,origin:"external",queueMessageId:n,clientMessageId:n,attachments:e.attachments,externalChannel:e.externalChannel??null,replyTarget:e.replyTarget}})}scanWorkerHealth(){const e=Date.now(),n=this.registry.load();for(const t of Object.values(n.workers)){if(t.status!=="running")continue;const r=e-Date.parse(t.createdAt);if(r<10*6e4)continue;const s=t.healthNotifiedAt?Date.parse(t.healthNotifiedAt):0;if(s&&e-s<10*6e4)continue;const o=n.managers[t.managedBy];if(!o)continue;const i=Math.max(0,Math.floor((e-Date.parse(t.lastActivityAt))/6e4)),c=`\u5DF2\u8FD0\u884C ${Math.floor(r/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
11
11
|
|
|
12
12
|
\u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
|
|
13
13
|
- \u6700\u8FD1\u6587\u672C\u6458\u8981\uFF1A${t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}
|
|
@@ -16,10 +16,10 @@ ${e.join(`
|
|
|
16
16
|
|
|
17
17
|
\u8BF7\u51B3\u5B9A\u662F\u5426\u7EE7\u7EED\u7B49\u5F85\u3001\u8BE2\u95EE\u7528\u6237\u3001\u505C\u6B62 worker\u3001\u6216\u521B\u5EFA\u5176\u4ED6 worker \u534F\u52A9\u3002`;this.registry.updateWorker(t.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:c}),this.registry.enqueueInboxEvent({managerSessionId:o.sessionId,workerSessionId:t.sessionId,kind:"worker.health",priority:"normal",runId:t.runId??null,summary:c})&&this.drainManagerInbox(o.sessionId)}}async interruptAndResumeManager(e,n,t){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&(this.registry.upsertManager({...e,status:"interrupting"}),await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${p()}`,method:"chat.abort",params:{sessionId:e.sessionId}}));const o=t?`\u4E8B\u4EF6\u7C7B\u578B\uFF1A${t}
|
|
18
18
|
|
|
19
|
-
${n}`:n;await this.dispatchManagerContinuation(e,o)}bindManagerAdapterEvents(e,n){n.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const r=this.registry.getManager(e);r&&this.noteManagerAgentSession(e,t.agentSessionId,r.workDir,r.modelId)}t.state==="start"&&this.updateManagerStatus(e,"running"),(t.state==="final"||t.state==="error"||t.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:n})}async drainManagerInbox(e){const n=this.registry.getManager(e);if(!n)return;const t=this.registry.listInboxEvents(e);if(!t.length)return;const r=t.find(c=>c.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(n.status!=="idle"&&!r||s?.currentRunId&&!r)return;const o=r?[r]:t.slice(0,20),i=
|
|
19
|
+
${n}`:n;await this.dispatchManagerContinuation(e,o)}bindManagerAdapterEvents(e,n){n.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const r=this.registry.getManager(e);r&&this.noteManagerAgentSession(e,t.agentSessionId,r.workDir,r.modelId)}t.state==="start"&&this.updateManagerStatus(e,"running"),(t.state==="final"||t.state==="error"||t.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,n){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:n})}async drainManagerInbox(e){const n=this.registry.getManager(e);if(!n)return;const t=this.registry.listInboxEvents(e);if(!t.length)return;const r=t.find(c=>c.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(n.status!=="idle"&&!r||s?.currentRunId&&!r)return;const o=r?[r]:t.slice(0,20),i=Ie(o);try{r&&s?.currentRunId?await this.interruptAndResumeManager(n,i,r.kind):(this.registry.upsertManager({...n,status:"running"}),await this.dispatchManagerContinuation(n,i,!0)),this.registry.removeInboxEvents(e,o.map(c=>c.id))}catch(c){this.registry.upsertManager({...n,status:"idle"}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${c instanceof Error?c.message:String(c)}`)}}async dispatchManagerContinuation(e,n,t=!1){const r=e.agentType??"manager",s=r==="manager"?e.modelId:e.providerModelId??void 0,o={sessionId:e.sessionId,text:n,agentType:r,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:s,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null};await this.opts.dispatchReq({type:"req",id:`manager-${t?"enqueue":"send"}-${p()}`,method:t?"chat.enqueue":"chat.send",params:o})}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(n=>me(n,void 0,e,"manager")).join(`
|
|
20
20
|
|
|
21
|
-
`).trim()}}function
|
|
21
|
+
`).trim()}}function U(a){return Array.isArray(a)?a.map(e=>({name:String(e?.name||"").trim()})).filter(e=>e.name):[]}function H(a){return a==="wechat-channel"||a==="macos-flow"||a==="macos-probe"||a==="windows-visual-flow"||a==="wechat-rpa-lab"||a==="fixture-jsonl"?a:void 0}function Ce(a,e){const n=a.map(t=>G(t,e)).filter(t=>t!=null);return n.length?n:void 0}function Ae(a,e,n,t){const r=e||"",s=new Set;return a.map((i,c)=>{const l=G(i,t),d=te({type:i.type,name:l?.name||i.name,path:l?.path,mimeType:l?.mimeType||i.mimeType,availability:i.availability,providerError:i.providerError||xe(i)},c),h=l?.path||"";if(h){const M=`${d}
|
|
22
22
|
${h}`;return s.has(M)||r.includes(h)?"":(s.add(M),`${n}: ${d}`)}const g=d;return s.has(g)?"":(s.add(g),`${n}: ${d}`)}).filter(i=>!r.includes(i)).join(`
|
|
23
|
-
`)}function
|
|
24
|
-
`);return[
|
|
25
|
-
`)}function
|
|
23
|
+
`)}function Te(a){const n=(a.channelType==="wechat-rpa"?Ee(a.sender.name):a.sender.name||a.sender.id)||(a.channelType==="wechat-rpa"?"\u5BF9\u65B9":a.sender.id)||"\u5BF9\u65B9",t=Ae(a.attachments,a.text,n,a.channelType),s=[a.text?ee({senderName:n,senderId:a.sender.id,text:a.text}):t?"":`${n}:`,t].filter(Boolean).join(`
|
|
24
|
+
`);return[Z({conversationName:a.conversationName||a.conversationId,messages:[]}),s].filter(Boolean).join(`
|
|
25
|
+
`)}function Ee(a){const e=a?.trim()||"";return!e||/^(contact|self|system|unknown)$/i.test(e)||/^wechat-sender:/i.test(e)?"":e}function xe(a){return!a.localPath||a.availability&&a.availability!=="edge-local"||D(a.localPath)?"":"edge-local-unavailable"}function G(a,e){return a.localPath&&Ne(a,e)&&D(a.localPath)?{path:a.localPath,name:a.name||w.basename(a.localPath)||"attachment",mimeType:a.mimeType||j(a)}:a.url&&ve(a)?{path:a.url,name:a.name||a.url.split("/").filter(Boolean).at(-1)||"attachment",mimeType:a.mimeType||j(a)}:a.thumbnailPath&&!K(a,e)&&D(a.thumbnailPath)?{path:a.thumbnailPath,name:a.name?`${a.name}-preview.png`:w.basename(a.thumbnailPath)||"preview.png",mimeType:"image/png"}:null}function be(a){return a?a.type!=="wechat-rpa"?a:{configured:a.configured,connected:a.connected,type:a.type,channelId:a.channelId,name:a.name,canReply:a.canReply,systemPrompt:a.systemPrompt,wechatRpaSource:a.wechatRpaSource,wechatRpaGroups:a.wechatRpaGroups,pollIntervalMs:a.pollIntervalMs,recentLimit:a.recentLimit,idleSeconds:a.idleSeconds,forceForeground:a.forceForeground,noRestore:a.noRestore,downloadAttachments:a.downloadAttachments,selfNickname:a.selfNickname,wechatRpaPrivacyConsentAccepted:a.wechatRpaPrivacyConsentAccepted,wechatRpaServerDecisionAvailable:a.wechatRpaServerDecisionAvailable,wechatRpaPreflightChecks:a.wechatRpaPreflightChecks,wechatRpaRuntimeState:a.wechatRpaRuntimeState,wechatRpaLastMessageAt:a.wechatRpaLastMessageAt,wechatRpaPendingReplyCount:a.wechatRpaPendingReplyCount,wechatRpaLastError:a.wechatRpaLastError}:null}function Ne(a,e){return a.providerError||a.availability&&a.availability!=="edge-local"?!1:K(a,e)?a.materializationKind==="original-file"&&a.isOriginal===!0&&a.mimeKindMatches===!0&&!De(a.localPath):!0}function ve(a){return!a.providerError&&(!a.availability||a.availability==="server-url")}function K(a,e){if(e!=="wechat-rpa")return!1;const n=String(a.type||"").toLowerCase();return n==="file"||n==="video"||n==="video-file"}function De(a){const e=String(a||"").replace(/\\/g,"/"),n=w.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(n)}function D(a){try{return S.statSync(a).isFile()}catch{return!1}}function j(a){return a.type==="image"?"image/*":a.type==="video"?"video/*":a.type==="audio"?"audio/*":"application/octet-stream"}function m(a){const e=Number(a);return Number.isFinite(e)?e:void 0}function z(a,e){const n=T(a,"reasonCode")||We(a),t=T(a,"outDir"),r=T(a,"helperTracePath"),s=T(a,"activityGuardPath");return{error:a instanceof Error?a.message:String(a||n),reasonCode:n,...t?{outDir:t}:{},...r?{helperTracePath:r}:{},...s?{activityGuardPath:s}:{},...e?{traceId:e}:{}}}function T(a,e){if(!a||typeof a!="object")return"";const n=a[e];return typeof n=="string"&&n.trim()?n.trim():""}function We(a){const e=a instanceof Error?a.message:String(a||"");return/^([a-z][a-z0-9_]*)(?::|\b)/i.exec(e.trim())?.[1]||"wechat_tool_failed"}export{Ve as ManagerRuntimeService,Ye as getManagerRuntimeService,Je as setManagerRuntimeService};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import l from"node:fs";import c from"node:os";import s from"node:path";import{readJsonlLines as f,safeParse as d}from"./parser-common.js";const a=new Set(["untitled","new chat","new conversation","new task","new thread","codex","\u65E0\u6807\u9898","\u672A\u547D\u540D","\u65B0\u4EFB\u52A1","\u65B0\u4F1A\u8BDD","\u65B0\u5BF9\u8BDD"]);function m(t,n){if((typeof t.id=="string"?t.id:typeof t.thread_id=="string"?t.thread_id:typeof t.session_id=="string"?t.session_id:"")!==n)return null;const e=(typeof t.thread_name=="string"?t.thread_name:typeof t.title=="string"?t.title:"").trim();return e&&!u(e)?e.slice(0,120):null}function u(t){const n=t.trim().toLocaleLowerCase().replace(/[\s_-]+/g," ");return a.has(n)}function w(t){if(!t)return null;const n=s.join(c.homedir(),".codex","session_index.jsonl");if(!l.existsSync(n))return null;let i=null;return f(n,0,o=>{if(!o.trim())return;const e=d(o);if(!e)return;const r=m(e,t);r&&(i=r)}),i}function p(){const t=[s.join(c.homedir(),".codex","sessions"),s.join(c.homedir(),".codex","archived_sessions")],n=[],i=o=>{for(const e of l.readdirSync(o,{withFileTypes:!0})){const r=s.join(o,e.name);e.isDirectory()?i(r):e.isFile()&&e.name.endsWith(".jsonl")&&n.push(r)}};for(const o of t)l.existsSync(o)&&i(o);return[...new Set(n)].sort()}export{p as listCodexRolloutFiles,w as lookupCodexThreadName};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ParsedNativeEvent } from './types.js';
|
|
2
|
-
export
|
|
3
|
-
export declare function listCodexRolloutFiles(): string[];
|
|
2
|
+
export { listCodexRolloutFiles, lookupCodexThreadName } from './codex-files.js';
|
|
4
3
|
export declare function parseCodexRolloutChunk(filePath: string, startOffset: number): {
|
|
5
4
|
nextOffset: number;
|
|
6
5
|
events: ParsedNativeEvent[];
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import E from"node:fs";import R from"node:path";import{createHash as Y}from"node:crypto";import{buildApprovalPendingPayload as Z,buildUserMessagePayload as S,isSystemControlPayload as A,isToolPayload as k}from"@shennian/wire";import{buildImagePayload as P,buildToolPayload as D,findJsonlLookbackOffset as K,inferMimeType as U,makeCursor as H,normalizeText as y,parseStructuredString as O,readJsonlLines as ee,readTimestamp as te,safeParse as W,shouldIgnoreNativeScanPath as q}from"./parser-common.js";import{lookupCodexThreadName as L}from"./codex-files.js";import{parseCodexDelegationInput as ne,parseCodexResponseUserInput as re,parseCodexTextElements as z,stripCodexUserMessageWrapper as oe}from"./codex-user-input.js";import{listCodexRolloutFiles as ve,lookupCodexThreadName as Me}from"./codex-files.js";function N(e){if(typeof e=="string"){const i=e.trim();return i?{path:i,name:R.basename(i)||"image.png",mimeType:U(i),kind:"image"}:null}if(typeof e!="object"||e===null)return null;const n=e,r=typeof n.path=="string"?n.path:typeof n.file_path=="string"?n.file_path:typeof n.saved_path=="string"?n.saved_path:typeof n.image_path=="string"?n.image_path:"";if(!r.trim())return null;const t=typeof n.name=="string"&&n.name.trim()?n.name:R.basename(r)||"image.png",o=typeof n.mimeType=="string"&&n.mimeType.trim()?n.mimeType:U(r);return{path:r,name:t,mimeType:o,kind:"image"}}function B(e){const n=y(e);if(!n)return!1;if(A(n)||/^<environment_context>[\s\S]*<\/environment_context>$/i.test(n)&&n.includes("<current_date>")&&n.includes("<timezone>")&&n.includes("<workspace_roots>"))return!0;const r=n.startsWith("<recommended_plugins>")&&n.includes("</recommended_plugins>"),t=n.includes("# AGENTS.md instructions for ")&&n.includes("<INSTRUCTIONS>")&&n.includes("</INSTRUCTIONS>"),o=n.includes("<environment_context>")&&n.includes("</environment_context>");return t&&o&&(n.startsWith("# AGENTS.md instructions for ")||r)}function F(e){const n=z(e.text_elements),r=Array.isArray(e.input)?z(e.input):"",t=typeof e.message=="string"?oe(e.message):{text:"",strippedWrapper:!1},o=t.text,i=n||r||o,u=t.strippedWrapper?"codex_app_context_wrapper":void 0,a=Array.isArray(e.local_images)?e.local_images.map(N).filter(l=>l!=null):[];return!i&&a.length===0||A(i)&&a.length===0||B(i)&&a.length===0?null:a.length>0?{payload:S(i,a),titleText:i,repairHint:u}:{payload:i,titleText:i,repairHint:u}}function ie(e){return e==="turn_completed"||e==="turn_complete"||e==="task_complete"}function se(e){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(e)}function ce(e){const n=typeof e.command=="string"?e.command:Array.isArray(e.command)?e.command.join(" "):"",r=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:n?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${n}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";return Z({title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",content:r,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"})}function ue(e,n){return Array.isArray(e)?y(e.map(r=>{if(typeof r=="string")return r;if(typeof r!="object"||r===null)return"";const t=r;return t.type!==n?"":typeof t.text=="string"?t.text:""}).filter(Boolean).join(`
|
|
2
2
|
|
|
3
|
-
`):""}function
|
|
4
|
-
|
|
5
|
-
`)):""}function ct(t){if(!Array.isArray(t))return[];const r=[];for(const n of t){if(typeof n!="object"||n===null)continue;const e=n;if(e.type!=="input_image")continue;const o=typeof e.path=="string"?e.path:typeof e.file_path=="string"?e.file_path:typeof e.saved_path=="string"?e.saved_path:typeof e.image_path=="string"?e.image_path:"";o.trim()&&r.push({path:o,name:I.basename(o)||"image.png",mimeType:N(o),kind:"image"})}return r}function J(t){if(t.type!=="message")return null;const r=t.role==="assistant"?"agent":t.role==="user"?"user":null;if(!r)return null;if(r==="agent"){const i=G(t.content,"output_text");return M(i)?null:i?{role:r,payload:i,titleText:i}:null}const n=F(G(t.content,"input_text")),e=n.text,o=ct(t.content);return!e&&o.length===0||M(e)&&o.length===0||L(e)&&o.length===0?null:{role:r,payload:o.length>0?k(e,o):e,titleText:e,repairHint:n.strippedWrapper?"codex_app_context_wrapper":void 0}}function X(t){if(!t)return"";try{const r=JSON.parse(t);if(typeof r=="object"&&r!==null){const n=r;if(n.type==="user"&&typeof n.content=="string")return h(n.content)}}catch{}return h(t)}function ut(t,r,n,e){return t.agentType!=="codex"||t.role!==r||W(t.payload)||Math.abs(t.ts-e)>300*1e3?!1:X(t.payload)===X(n)}function $(t,r,n,e){for(let o=t.length-1;o>=0;o-=1){const i=t[o];if(i){if(ut(i,r,n,e))return o;if(i.agentType==="codex"&&Math.abs(i.ts-e)>300*1e3)break}}return-1}function v(t,r,n,e,o,i,u,a,f,c,s="agent",l=!1,m){u&&t.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:H(r,n,e),cursor:q(r,n),role:s,ts:i,payload:u,title:a,modelId:f,workDir:c,terminal:l,repairHint:m})}function x(t,r,n,e,o,i,u,a,f,c,s,l,m){const A=H(r,n,e),y=typeof m?.callId=="string"&&m.callId.trim()?m.callId.trim():null,T=y?Z("sha256").update(`${o}\0${y}`).digest("hex").slice(0,32):null,p=T?`tool:${T}`:void 0,C=T?`native:tool:${T}`:`native:${A}`,g=m?.completed&&T?2:1,E=l!==void 0?"completed":"running";t.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:A,cursor:q(r,n),role:"agent",ts:i,payload:K({name:u,toolCallId:C,detailRevision:g,status:E}),title:a,modelId:f,workDir:c,...p?{canonicalMessageKey:p,canonicalMutation:m?.completed?"upsert":"append"}:{},localToolDetail:{toolCallId:C,detailRevision:g,name:u,...s!==void 0?{args:s}:{},...l!==void 0?{result:l}:{},status:E}})}function at(t,r,n,e,o,i,u,a,f){const c=typeof e.type=="string"?e.type:"";if(c){if(c==="message"){const s=J(e);if(!s||$(t,s.role,s.payload,i)>=0)return;v(t,r,n,`${c}:${s.role}`,o,i,s.payload,u,a,f,s.role,!1,s.repairHint);return}if(c==="function_call"){const s=typeof e.name=="string"?e.name:"function_call";x(t,r,n,c,o,i,s,u,a,f,O(e.arguments),void 0,{callId:e.call_id});return}if(c==="function_call_output"){x(t,r,n,c,o,i,"function_call_output",u,a,f,void 0,e.output,{callId:e.call_id,completed:!0});return}if(c==="custom_tool_call"){const s=typeof e.name=="string"?e.name:"custom_tool_call",l=e.input!==void 0?{input:e.input}:void 0;x(t,r,n,c,o,i,s,u,a,f,l,void 0,{callId:e.call_id});return}if(c==="custom_tool_call_output"){const s=typeof e.name=="string"?e.name:"custom_tool_call_output",l=e.output??e.result??e.content;x(t,r,n,c,o,i,s,u,a,f,void 0,l,{callId:e.call_id,completed:!0});return}if(c==="web_search_call"){const s=typeof e.action=="object"&&e.action!==null?e.action:null;x(t,r,n,c,o,i,"web_search",u,a,f,s??e.query);return}}}function ft(t,r,n,e,o,i,u,a,f){const c=typeof e.type=="string"?e.type:"";if(c){if(c==="user_message"){const s=B(e);if(!s)return;v(t,r,n,c,o,i,s.payload,u,a,f,"user",!1,s.repairHint);return}if(c==="agent_message"){const s=typeof e.message=="string"?h(e.message):"";if(!s||M(s))return;v(t,r,n,c,o,i,s,u,a,f);return}if(c==="turn_aborted"||c==="error"){const s=typeof e.message=="string"?h(e.message):typeof e.error=="string"?h(e.error):"";if(!s)return;v(t,r,n,c,o,i,s,u,a,f,"agent",!0);return}if(it(c)){v(t,r,n,c,o,i,st(e),u,a,f,"agent");return}if(c==="exec_command_end"){const s=Array.isArray(e.command)?e.command:e.parsed_cmd??e.command,l=e.exit_code??e.exitCode,m=[typeof e.aggregated_output=="string"?e.aggregated_output:"",l!=null?`
|
|
6
|
-
(exit ${String(l)})`:""].join("");x(t,r,n,c,o,i,"command_execution",u,a,f,{command:s},m);return}if(c==="patch_apply_end"){x(t,r,n,c,o,i,"apply_patch",u,a,f,e.changes,{success:e.success,stdout:e.stdout,stderr:e.stderr});return}if(c==="mcp_tool_call_end"){const s=typeof e.invocation=="object"&&e.invocation!==null?e.invocation:null,l=typeof s?.tool=="string"?s.tool:"mcp_tool_call",m={server:s?.server,...typeof s?.arguments=="object"&&s.arguments!==null?s.arguments:{}};x(t,r,n,c,o,i,l,u,a,f,m,e.result);return}if(c==="dynamic_tool_call_request"){const s=typeof e.tool=="string"?e.tool:"dynamic_tool_call";x(t,r,n,c,o,i,s,u,a,f,e.arguments,void 0,{callId:e.callId??e.call_id});return}if(c==="dynamic_tool_call_response"){const s=typeof e.tool=="string"?e.tool:"dynamic_tool_call";x(t,r,n,c,o,i,s,u,a,f,void 0,e.result??e.output,{callId:e.callId??e.call_id,completed:!0});return}if(c==="web_search_end"){const s=typeof e.action=="object"&&e.action!==null?e.action:null;x(t,r,n,c,o,i,"web_search",u,a,f,s??e.query,e.query);return}if(c==="image_generation_end"){const s=typeof e.saved_path=="string"?e.saved_path.trim():"";if(!s)return;v(t,r,n,c,o,i,D(s),u,a,f);return}c==="view_image_tool_call"&&x(t,r,n,c,o,i,"view_image",u,a,f,e.path??e.image_path??e)}}function S(t=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:t}}function Q(t){if((typeof t.thread_source=="string"?t.thread_source.trim().toLowerCase():"")==="subagent")return!0;const n=typeof t.source=="object"&&t.source!==null?t.source:null;if(n&&typeof n.subagent=="object"&&n.subagent!==null)return!0;const e=typeof t.parent_thread_id=="string"&&t.parent_thread_id.trim().length>0,o=typeof t.agent_nickname=="string"||typeof t.agent_role=="string"||typeof t.multi_agent_version=="string";return e&&o}function lt(t){const r=j.openSync(t,"r");try{const n=[];let e=0,o="";for(;e<1024*1024;){const f=Buffer.alloc(16384),c=j.readSync(r,f,0,f.length,e);if(c<=0)break;const s=f.subarray(0,c);n.push(s),e+=c;const l=Buffer.concat(n),m=l.indexOf(10);if(m>=0){o=l.subarray(0,m).toString("utf8");break}}!o&&n.length>0&&(o=Buffer.concat(n).toString("utf8"));const i=R(o);if(!i||i.type!=="session_meta")return S();const u=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!u)return S();if(Q(u))return S(!0);const a=typeof u.id=="string"?u.id:"";return{sourceSessionKey:a,workDir:typeof u.cwd=="string"?u.cwd:"",modelId:V(u),title:Y(a)??"",skipImport:!1}}finally{j.closeSync(r)}return S()}function V(t){return(typeof t.model=="string"?t.model.trim():"")||null}function mt(t,r){if((typeof t.id=="string"?t.id:typeof t.thread_id=="string"?t.thread_id:typeof t.session_id=="string"?t.session_id:"")!==r)return null;const o=(typeof t.thread_name=="string"?t.thread_name:typeof t.title=="string"?t.title:"").trim();return o&&!pt(o)?o.slice(0,120):null}function pt(t){const r=t.trim().toLocaleLowerCase().replace(/[\s_-]+/g," ");return et.has(r)}function Y(t){if(!t)return null;const r=I.join(w.homedir(),".codex","session_index.jsonl");if(!j.existsSync(r))return null;let n=null;return U(r,0,e=>{if(!e.trim())return;const o=R(e);if(!o)return;const i=mt(o,t);i&&(n=i)}),n}function Tt(){const t=[I.join(w.homedir(),".codex","sessions"),I.join(w.homedir(),".codex","archived_sessions")],r=[],n=e=>{for(const o of j.readdirSync(e,{withFileTypes:!0})){const i=I.join(e,o.name);o.isDirectory()?n(i):o.isFile()&&o.name.endsWith(".jsonl")&&r.push(i)}};for(const e of t)j.existsSync(e)&&n(e);return[...new Set(r)].sort()}function Ct(t,r){const n=[],e=lt(t);let{sourceSessionKey:o,workDir:i,modelId:u,title:a,skipImport:f}=e,c=e.title;if(f)return{nextOffset:j.statSync(t).size,events:[]};let s=!1;const l=U(t,r,(m,A)=>{const y=R(m);if(!y)return;const T=typeof y.type=="string"?y.type:"",p=typeof y.payload=="object"&&y.payload!==null?y.payload:null,C=tt(y.timestamp);if(!(!p||!C)){if(T==="session_meta"){if(Q(p)){n.length=0,f=!0;return}o=typeof p.id=="string"?p.id:o,i=typeof p.cwd=="string"?p.cwd:i,u=V(p)??u;const g=Y(o);g&&(c=g,a=g);return}if(!f&&o){if(T==="response_item"){const g=J(p);g?.role==="user"&&g.titleText&&!a&&(a=g.titleText.slice(0,80)),at(n,t,A,p,o,C,a,u,i);return}if(T==="event_msg"){const g=typeof p.type=="string"?p.type:"";if(ot(g)){let d=!1;for(let _=n.length-1;_>=0;_-=1){const b=n[_];if(b?.agentType==="codex"&&b.role==="agent"&&!W(b.payload)){b.terminal=!0,d=!0,s=!1;break}}if(!d)for(let _=n.length-1;_>=0;_-=1){const b=n[_];if(!(b?.agentType!=="codex"||b.role!=="agent")){b.terminal=!0,d=!0,s=!1;break}}d||(s=!0);return}if(g==="user_message"){const d=B(p);if(d?.titleText&&!a&&(a=d.titleText.slice(0,80)),d){const _=$(n,"user",d.payload,C);_>=0&&n.splice(_,1)}}else if(g==="agent_message"){const d=typeof p.message=="string"?h(p.message):"";if(d&&!M(d)){const _=$(n,"agent",d,C);_>=0&&n.splice(_,1)}}const E=n.length;ft(n,t,A,p,o,C,a,u,i),s&&n.length>E&&(n[n.length-1].terminal=!0,s=!1)}}}});for(const m of n)m.titleSource=c&&m.title===c?"agent_native":"first_user_fallback";return{nextOffset:l,events:n}}export{Tt as listCodexRolloutFiles,Y as lookupCodexThreadName,Ct as parseCodexRolloutChunk};
|
|
3
|
+
`)):""}function ae(e){if(!Array.isArray(e))return[];const n=[];for(const r of e){if(typeof r!="object"||r===null)continue;const t=r;if(t.type!=="input_image")continue;const o=N(t);o&&n.push(o)}return n}function w(e){if(e.type!=="message")return null;const n=e.role==="assistant"?"agent":e.role==="user"?"user":null;if(!n)return null;if(n==="agent"){const i=ue(e.content,"output_text");return A(i)?null:i?{role:n,payload:i,titleText:i}:null}const r=re(e.content),t=r.text,o=[...ae(e.content)];for(const i of r.imagePaths){const u=N(i);u&&(o.some(a=>a.path===u.path)||o.push(u))}return!t&&o.length===0||A(t)&&o.length===0||B(t)&&o.length===0?null:{role:n,payload:o.length>0?S(t,o):t,titleText:t,repairHint:r.strippedWrapper?"codex_app_context_wrapper":void 0}}function J(e){if(!e)return"";try{const n=JSON.parse(e);if(typeof n=="object"&&n!==null){const r=n;if(r.type==="user"&&typeof r.content=="string")return y(r.content)}}catch{}return y(e)}function le(e,n,r,t){return e.agentType!=="codex"||e.role!==n||k(e.payload)||Math.abs(e.ts-t)>300*1e3?!1:J(e.payload)===J(r)}function $(e,n,r,t){for(let o=e.length-1;o>=0;o-=1){const i=e[o];if(i){if(le(i,n,r,t))return o;if(i.agentType==="codex"&&Math.abs(i.ts-t)>300*1e3)break}}return-1}function v(e,n,r,t,o,i,u,a,l,c,s="agent",m=!1,p){u&&e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:G(o,r,t),cursor:H(n,r),role:s,ts:i,payload:u,title:a,modelId:l,workDir:c,terminal:m,repairHint:p})}function b(e,n,r,t,o,i,u,a,l,c,s,m,p){const T=G(o,r,t),I=typeof p?.callId=="string"&&p.callId.trim()?p.callId.trim():null,d=I?Y("sha256").update(`${o}\0${I}`).digest("hex").slice(0,32):null,M=d?`tool:${d}`:void 0,f=d?`native:tool:${d}`:`native:${T}`,C=p?.completed&&d?2:1,g=m!==void 0?"completed":"running";e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:T,cursor:H(n,r),role:"agent",ts:i,payload:D({name:u,toolCallId:f,detailRevision:C,status:g}),title:a,modelId:l,workDir:c,...M?{canonicalMessageKey:M,canonicalMutation:p?.completed?"upsert":"append"}:{},localToolDetail:{toolCallId:f,detailRevision:C,name:u,...s!==void 0?{args:s}:{},...m!==void 0?{result:m}:{},status:g}})}function G(e,n,r){return`codex:${e}:${n}:${r}`}function me(e,n,r,t,o,i,u,a,l){const c=typeof t.type=="string"?t.type:"";if(c){if(c==="message"){const s=w(t);if(!s||$(e,s.role,s.payload,i)>=0)return;v(e,n,r,`${c}:${s.role}`,o,i,s.payload,u,a,l,s.role,!1,s.repairHint);return}if(c==="function_call"){const s=typeof t.name=="string"?t.name:"function_call";b(e,n,r,c,o,i,s,u,a,l,O(t.arguments),void 0,{callId:t.call_id});return}if(c==="function_call_output"){const s=ne(t);if(s){v(e,n,r,"delegation:user",o,i,s,u||s.slice(0,80),a,l,"user");return}b(e,n,r,c,o,i,"function_call_output",u,a,l,void 0,t.output,{callId:t.call_id,completed:!0});return}if(c==="custom_tool_call"){const s=typeof t.name=="string"?t.name:"custom_tool_call",m=t.input!==void 0?{input:t.input}:void 0;b(e,n,r,c,o,i,s,u,a,l,m,void 0,{callId:t.call_id});return}if(c==="custom_tool_call_output"){const s=typeof t.name=="string"?t.name:"custom_tool_call_output",m=t.output??t.result??t.content;b(e,n,r,c,o,i,s,u,a,l,void 0,m,{callId:t.call_id,completed:!0});return}if(c==="web_search_call"){const s=typeof t.action=="object"&&t.action!==null?t.action:null;b(e,n,r,c,o,i,"web_search",u,a,l,s??t.query);return}}}function fe(e,n,r,t,o,i,u,a,l){const c=typeof t.type=="string"?t.type:"";if(c){if(c==="user_message"){const s=F(t);if(!s)return;v(e,n,r,c,o,i,s.payload,u,a,l,"user",!1,s.repairHint);return}if(c==="agent_message"){const s=typeof t.message=="string"?y(t.message):"";if(!s||A(s))return;v(e,n,r,c,o,i,s,u,a,l);return}if(c==="turn_aborted"||c==="error"){const s=typeof t.message=="string"?y(t.message):typeof t.error=="string"?y(t.error):"";if(!s)return;v(e,n,r,c,o,i,s,u,a,l,"agent",!0);return}if(se(c)){v(e,n,r,c,o,i,ce(t),u,a,l,"agent");return}if(c==="exec_command_end"){const s=Array.isArray(t.command)?t.command:t.parsed_cmd??t.command,m=t.exit_code??t.exitCode,p=[typeof t.aggregated_output=="string"?t.aggregated_output:"",m!=null?`
|
|
4
|
+
(exit ${String(m)})`:""].join("");b(e,n,r,c,o,i,"command_execution",u,a,l,{command:s},p);return}if(c==="patch_apply_end"){b(e,n,r,c,o,i,"apply_patch",u,a,l,t.changes,{success:t.success,stdout:t.stdout,stderr:t.stderr});return}if(c==="mcp_tool_call_end"){const s=typeof t.invocation=="object"&&t.invocation!==null?t.invocation:null,m=typeof s?.tool=="string"?s.tool:"mcp_tool_call",p={server:s?.server,...typeof s?.arguments=="object"&&s.arguments!==null?s.arguments:{}};b(e,n,r,c,o,i,m,u,a,l,p,t.result);return}if(c==="dynamic_tool_call_request"){const s=typeof t.tool=="string"?t.tool:"dynamic_tool_call";b(e,n,r,c,o,i,s,u,a,l,t.arguments,void 0,{callId:t.callId??t.call_id});return}if(c==="dynamic_tool_call_response"){const s=typeof t.tool=="string"?t.tool:"dynamic_tool_call";b(e,n,r,c,o,i,s,u,a,l,void 0,t.result??t.output,{callId:t.callId??t.call_id,completed:!0});return}if(c==="web_search_end"){const s=typeof t.action=="object"&&t.action!==null?t.action:null;b(e,n,r,c,o,i,"web_search",u,a,l,s??t.query,t.query);return}if(c==="image_generation_end"){const s=typeof t.saved_path=="string"?t.saved_path.trim():"";if(!s)return;v(e,n,r,c,o,i,P(s),u,a,l);return}c==="view_image_tool_call"&&b(e,n,r,c,o,i,"view_image",u,a,l,t.path??t.image_path??t)}}function j(e=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:e}}function Q(e){if((typeof e.thread_source=="string"?e.thread_source.trim().toLowerCase():"")==="subagent")return!0;const r=typeof e.source=="object"&&e.source!==null?e.source:null;if(r&&typeof r.subagent=="object"&&r.subagent!==null)return!0;const t=typeof e.parent_thread_id=="string"&&e.parent_thread_id.trim().length>0,o=typeof e.agent_nickname=="string"||typeof e.agent_role=="string"||typeof e.multi_agent_version=="string";return t&&o}function pe(e){const n=E.openSync(e,"r");try{const r=[];let t=0,o="";for(;t<1024*1024;){const l=Buffer.alloc(16384),c=E.readSync(n,l,0,l.length,t);if(c<=0)break;const s=l.subarray(0,c);r.push(s),t+=c;const m=Buffer.concat(r),p=m.indexOf(10);if(p>=0){o=m.subarray(0,p).toString("utf8");break}}!o&&r.length>0&&(o=Buffer.concat(r).toString("utf8"));const i=W(o);if(!i||i.type!=="session_meta")return j();const u=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!u)return j();if(Q(u))return j(!0);const a=typeof u.id=="string"?u.id:"";return{sourceSessionKey:a,workDir:typeof u.cwd=="string"?u.cwd:"",modelId:V(u),title:L(a)??"",skipImport:!1}}finally{E.closeSync(n)}return j()}function V(e){return(typeof e.model=="string"?e.model.trim():"")||null}function Ce(e,n){const r=[],t=pe(e);let{sourceSessionKey:o,workDir:i,modelId:u,title:a,skipImport:l}=t,c=t.title;if(l||q(i))return{nextOffset:E.statSync(e).size,events:[]};let s=!1;const m=new Set,p=ee(e,K(e,n),(T,I)=>{const d=W(T);if(!d)return;const M=typeof d.type=="string"?d.type:"",f=typeof d.payload=="object"&&d.payload!==null?d.payload:null,C=te(d.timestamp);if(!(!f||!C)){if(M==="session_meta"){if(Q(f)){r.length=0,l=!0;return}if(o=typeof f.id=="string"?f.id:o,i=typeof f.cwd=="string"?f.cwd:i,q(i)){r.length=0,l=!0;return}u=V(f)??u;const g=L(o);g&&(c=g,a=g);return}if(!l&&o){if(M==="response_item"){const g=w(f);g?.role==="user"&&g.titleText&&!a&&(a=g.titleText.slice(0,80)),me(r,e,I,f,o,C,a,u,i);return}if(M==="event_msg"){const g=typeof f.type=="string"?f.type:"";if(ie(g)){let _=!1;for(let x=r.length-1;x>=0;x-=1){const h=r[x];if(h?.agentType==="codex"&&h.role==="agent"&&!k(h.payload)){h.terminal=!0,I>=n&&Number(h.cursor?.slice(e.length+1))<n&&m.add(h.sourceEventKey),_=!0,s=!1;break}}if(!_)for(let x=r.length-1;x>=0;x-=1){const h=r[x];if(!(h?.agentType!=="codex"||h.role!=="agent")){h.terminal=!0,I>=n&&Number(h.cursor?.slice(e.length+1))<n&&m.add(h.sourceEventKey),_=!0,s=!1;break}}_||(s=!0);return}if(g==="user_message"){const _=F(f);if(_?.titleText&&!a&&(a=_.titleText.slice(0,80)),_){const x=$(r,"user",_.payload,C);x>=0&&r.splice(x,1)}}else if(g==="agent_message"){const _=typeof f.message=="string"?y(f.message):"";if(_&&!A(_)){const x=$(r,"agent",_,C);x>=0&&r.splice(x,1)}}const X=r.length;fe(r,e,I,f,o,C,a,u,i),s&&r.length>X&&(r[r.length-1].terminal=!0,s=!1)}}}});for(const T of r)T.titleSource=c&&T.title===c?"agent_native":"first_user_fallback";return{nextOffset:p,events:r.filter(T=>m.has(T.sourceEventKey)||Number(T.cursor?.slice(e.length+1))>=n)}}export{ve as listCodexRolloutFiles,Me as lookupCodexThreadName,Ce as parseCodexRolloutChunk};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function parseCodexTextElements(value: unknown): string;
|
|
2
|
+
export declare function stripCodexUserMessageWrapper(text: string): {
|
|
3
|
+
text: string;
|
|
4
|
+
strippedWrapper: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function parseCodexResponseUserInput(content: unknown): {
|
|
7
|
+
text: string;
|
|
8
|
+
imagePaths: string[];
|
|
9
|
+
strippedWrapper: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function parseCodexDelegationInput(payload: Record<string, unknown>): string | null;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{normalizeText as o}from"./parser-common.js";function _(t){return Array.isArray(t)?t.map(e=>{if(typeof e=="string")return e;if(typeof e!="object"||e===null)return"";const n=e;return typeof n.text=="string"?n.text:""}).map(e=>o(e)).filter(Boolean).join(`
|
|
2
|
+
|
|
3
|
+
`):""}function l(t){return o(t.replace(/<image>\s*<\/image>/gi,""))}function d(t){const e=o(t);if(!e)return{text:"",strippedWrapper:!1};const n=e.match(/My request for Codex:\s*([\s\S]*)$/i);if(n?.[1])return{text:l(n[1]),strippedWrapper:!0};const p=e.match(/^# Files mentioned by the user:\s*[\s\S]*?\n+## My request:\s*([\s\S]*)$/i);return p?.[1]?{text:l(p[1]),strippedWrapper:!0}:{text:e,strippedWrapper:!1}}const g=/^<image\b[^>]*\bpath="([^"]+)"[^>]*>\s*$/i,m=/^<\/image>\s*$/i;function C(t){if(!Array.isArray(t))return{text:"",imagePaths:[],strippedWrapper:!1};const e=new Set,n=[];for(let r=0;r+2<t.length;r+=1){const s=t[r],i=t[r+1],u=t[r+2];if(typeof s!="object"||s===null||typeof i!="object"||i===null||typeof u!="object"||u===null)continue;const a=s,x=i,c=u;if(a.type!=="input_text"||typeof a.text!="string"||x.type!=="input_image"||c.type!=="input_text"||typeof c.text!="string"||!m.test(o(c.text)))continue;const f=o(a.text).match(g)?.[1];f&&(n.push(f),e.add(r),e.add(r+2))}const p=t.map((r,s)=>{if(e.has(s))return"";if(typeof r=="string")return r;if(typeof r!="object"||r===null)return"";const i=r;return i.type==="input_text"&&typeof i.text=="string"?i.text:""}).map(r=>o(r)).filter(Boolean).join(`
|
|
4
|
+
|
|
5
|
+
`);return{...d(p),imagePaths:n}}function E(t){if(t.namespace!=="codex_app"||typeof t.output!="string")return null;const e=o(t.output);if(!e.startsWith("<codex_delegation>")||!e.endsWith("</codex_delegation>"))return null;const n=e.match(/<input>([\s\S]*?)<\/input>\s*<\/codex_delegation>$/)?.[1];return n?o(n):null}export{E as parseCodexDelegationInput,C as parseCodexResponseUserInput,_ as parseCodexTextElements,d as stripCodexUserMessageWrapper};
|
|
@@ -2,17 +2,25 @@ type WatchHandle = {
|
|
|
2
2
|
close(): void;
|
|
3
3
|
};
|
|
4
4
|
type WatchListener = (eventType: string, filename: string | Buffer | null) => void;
|
|
5
|
-
type WatchFactory = (
|
|
5
|
+
type WatchFactory = (target: string, listener: WatchListener) => WatchHandle;
|
|
6
6
|
export declare class NativeFusionFileWatcher {
|
|
7
7
|
private onChanged;
|
|
8
|
-
private
|
|
9
|
-
private
|
|
8
|
+
private directoryWatchFactory;
|
|
9
|
+
private fileWatchFactory;
|
|
10
|
+
private directoryWatchers;
|
|
11
|
+
private fileWatchers;
|
|
10
12
|
private filesByDirectory;
|
|
11
13
|
private debounceTimers;
|
|
12
|
-
constructor(onChanged: (filePaths: string[]) => void,
|
|
13
|
-
refresh(filePaths: readonly string[]): void;
|
|
14
|
+
constructor(onChanged: (filePaths: string[]) => void, directoryWatchFactory?: WatchFactory, fileWatchFactory?: WatchFactory);
|
|
15
|
+
refresh(filePaths: readonly string[], rootDirectories?: readonly string[]): void;
|
|
16
|
+
/** Install the append watcher before parsing a newly discovered transcript. */
|
|
17
|
+
trackFile(filePath: string): void;
|
|
18
|
+
untrackFile(filePath: string): void;
|
|
14
19
|
stop(): void;
|
|
15
20
|
private handleChange;
|
|
21
|
+
private trackDirectory;
|
|
22
|
+
private collectDirectories;
|
|
23
|
+
private trackDirectoryTree;
|
|
16
24
|
private schedule;
|
|
17
25
|
}
|
|
18
26
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import o from"node:fs";import a from"node:path";const y=250;class W{onChanged;directoryWatchFactory;fileWatchFactory;directoryWatchers=new Map;fileWatchers=new Map;filesByDirectory=new Map;debounceTimers=new Map;constructor(t,r=(s,i)=>o.watch(s,i),e=(s,i)=>o.watch(s,i)){this.onChanged=t,this.directoryWatchFactory=r,this.fileWatchFactory=e}refresh(t,r=[]){const e=new Set(t),s=new Map;for(const c of t){const n=a.dirname(c),f=s.get(n)??new Set;f.add(c),s.set(n,f)}const i=new Set(s.keys());for(const c of r)this.collectDirectories(c,i);for(const[c,n]of this.directoryWatchers.entries())i.has(c)||(n.close(),this.directoryWatchers.delete(c));for(const[c,n]of this.fileWatchers.entries())e.has(c)||(n.close(),this.fileWatchers.delete(c));this.filesByDirectory=s;for(const c of i)this.trackDirectory(c);for(const c of e)this.trackFile(c)}trackFile(t){const r=a.dirname(t),e=this.filesByDirectory.get(r)??new Set;if(e.add(t),this.filesByDirectory.set(r,e),this.trackDirectory(r),!this.fileWatchers.has(t))try{const s=this.fileWatchFactory(t,()=>this.schedule(t));this.fileWatchers.set(t,s)}catch(s){console.warn(`[native-fusion] unable to watch transcript ${t}`,s)}}untrackFile(t){this.fileWatchers.get(t)?.close(),this.fileWatchers.delete(t);const r=a.dirname(t),e=this.filesByDirectory.get(r);e?.delete(t),e?.size===0&&this.filesByDirectory.delete(r)}stop(){for(const t of this.directoryWatchers.values())t.close();for(const t of this.fileWatchers.values())t.close();this.directoryWatchers.clear(),this.fileWatchers.clear(),this.filesByDirectory.clear();for(const t of this.debounceTimers.values())clearTimeout(t);this.debounceTimers.clear()}handleChange(t,r){const e=r?a.join(t,r.toString()):null;if(e)try{const i=o.statSync(e);if(i.isDirectory()){this.trackDirectoryTree(e,!0);return}if(i.isFile()&&l(e)){this.trackFile(e),this.schedule(e);return}}catch{if(l(e)){this.untrackFile(e),this.schedule(e);return}}const s=[...this.filesByDirectory.get(t)??[]];for(const i of s)this.schedule(i)}trackDirectory(t){if(!this.directoryWatchers.has(t))try{const r=this.directoryWatchFactory(t,(e,s)=>{this.handleChange(t,s)});this.directoryWatchers.set(t,r)}catch(r){console.warn(`[native-fusion] unable to watch ${t}`,r)}}collectDirectories(t,r){if(!o.existsSync(t))return;let e;try{e=o.statSync(t)}catch{return}if(!e.isDirectory())return;r.add(t);let s;try{s=o.readdirSync(t,{withFileTypes:!0})}catch{return}for(const i of s)i.isDirectory()&&this.collectDirectories(a.join(t,i.name),r)}trackDirectoryTree(t,r){this.trackDirectory(t);let e;try{e=o.readdirSync(t,{withFileTypes:!0})}catch{return}for(const s of e){const i=a.join(t,s.name);s.isDirectory()?this.trackDirectoryTree(i,r):s.isFile()&&l(i)&&(this.trackFile(i),r&&this.schedule(i))}}schedule(t){const r=this.debounceTimers.get(t);r&&clearTimeout(r);const e=setTimeout(()=>{this.debounceTimers.delete(t),this.onChanged([t])},y);this.debounceTimers.set(t,e)}}function l(h){return h.endsWith(".jsonl")||h.endsWith(".opencode-session.json")}export{W as NativeFusionFileWatcher};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SessionDeliveryState } from '@shennian/wire';
|
|
1
2
|
import type { CanonicalSessionStore } from '../session/canonical-store.js';
|
|
2
3
|
import { listSessionRecords, recordSession } from '../session/store.js';
|
|
3
4
|
import type { ManagedSuppressionWindow, ParsedNativeEvent, PendingManagedClaim } from './types.js';
|
|
@@ -46,6 +47,7 @@ export declare class NativeFusionLocalRepository {
|
|
|
46
47
|
deletionStore?: Pick<NativeSourceDeletionStore, 'has'>;
|
|
47
48
|
});
|
|
48
49
|
commitEvents(events: readonly ParsedNativeEvent[]): NativeLocalCommitResult[];
|
|
50
|
+
updateMessageDeliveryState(sessionId: string, messageId: string, deliveryState: SessionDeliveryState): void;
|
|
49
51
|
commitEvent(event: ParsedNativeEvent): NativeLocalCommitResult;
|
|
50
52
|
repairMissingUserBoundaries(): number;
|
|
51
53
|
listManagedClaims(): PendingManagedClaim[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import h from"node:crypto";import l from"node:fs";import f from"node:path";import{sessionPreviewFromPayload as R}from"@shennian/wire";import{resolvePersonalSyncV1Path as L}from"../personal-sync/paths.js";import{getCanonicalSessionStore as H,getDaemonInstallationId as P,listSessionRecords as C,recordSession as K}from"../session/store.js";import{readToolDetail as U,writeToolDetail as J}from"../session/tool-detail-store.js";import{NativeSourceDeletionStore as V}from"./native-source-deletions.js";import{recordPersonalSyncLocalMetric as A}from"../personal-sync/metrics.js";const S=1;class q extends Error{code="missing_user_boundary";constructor(e){super(`native source ${e} does not start at a visible user boundary`),this.name="MissingUserBoundaryError"}}class W extends Error{code="native_source_deleted";constructor(e){super(`native source ${e} is blocked by a durable deletion tombstone`),this.name="NativeSourceDeletedError"}}class le{options;constructor(e={}){this.options=e}commitEvents(e){return[...e].sort((s,o)=>s.ts-o.ts).map(s=>this.commitEvent(s))}commitEvent(e){if(z(e),this.isSourceDeleted(e.agentType,e.sourceSessionKey))throw new W(e.sourceSessionKey);const s=this.installationId(),o=this.readSources(),n=w(e.agentType,e.sourceSessionKey),i=o.sources[n],c=X(e),a=this.ledgerFile(e.agentType,e.sourceEventKey),r=l.existsSync(a)?b(a):null;if(r&&r.daemonInstallationId!==s)throw new Error("native event ledger belongs to another daemon installation");if(r&&r.eventFingerprint!==c)throw new Error(`native event identity conflict: ${e.sourceEventKey}`);if(!i&&!r&&e.role!=="user")throw new q(e.sourceSessionKey);const m=e.claimSessionId??null,d=i?.sessionId??r?.sessionId??m??G(s,e.agentType,e.sourceSessionKey);if(m&&i&&i.sessionId!==m)throw new Error(`managed claim session conflicts with source binding ${e.sourceSessionKey}`);const y=this.canonicalStore(),p=e.localDisposition==="managed_echo_suppressed"||r?.mappedBy==="managed_echo_suppressed",u=r?.canonicalMessageId??(p?null:e.claimCanonicalMessageId??Q(s,e.agentType,e.canonicalMessageKey??e.sourceEventKey));let I=y.getManifest(d)?.bodyRevision??0,D=!!r;const E=!p&&!!e.claimCanonicalMessageId;if(E){if(!u)throw new Error("managed echo claim requires a canonical message");const M=y.getMessageState(d,u);if(!M||M.tombstoned)throw new Error(`managed echo claim target is not durable: ${u}`)}if(!r&&!E&&!p){if(!u)throw new Error("native event requires a canonical message");const M=e.canonicalMessageKey?y.getMessageState(d,u):null,F=e.canonicalMutation==="upsert"||!!M,$=F?M??y.getMessageState(d,u):null,x={id:u,sessionId:d,role:e.role,ts:$?.message?.ts??e.ts,payload:Y($?.message?.payload,e.payload)},_=F?y.upsert(d,x):y.append(d,x);if(I=_.bodyRevision,D=_.duplicate,e.localToolDetail){const N=this.options.writeLocalToolDetail??J,v=this.options.readLocalToolDetail??U,B=Z(v(d,e.localToolDetail.toolCallId),e.localToolDetail);N({sessionId:d,...B,ts:e.ts,detailRef:{toolIndex:0}})}}!r&&!p&&this.recordIndex(e,d,u,I);const T=new Date().toISOString();return r||g(a,{schemaVersion:S,daemonInstallationId:s,sessionId:d,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceEventKey:e.sourceEventKey,eventFingerprint:c,canonicalMessageId:u,canonicalRevision:I,cursor:e.cursor??null,mappedBy:p?"managed_echo_suppressed":E?"managed_echo_claim":e.sourceMode,eventTs:e.ts,createdAt:T}),o.sources[n]={schemaVersion:S,daemonInstallationId:s,sessionId:d,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceMode:r?i?.sourceMode??e.sourceMode:e.sourceMode,lastCursor:r?i?.lastCursor??r.cursor:e.cursor??i?.lastCursor??null,bodyRevision:I,createdAt:i?.createdAt??T,updatedAt:T},g(this.sourcesFile(),o),!r&&e.cursor&&A("sourceCursorAdvanced"),!r&&!E&&!p&&!D&&A("canonicalCommits"),{sessionId:d,messageId:u,bodyRevision:I,duplicate:D,claimed:E,suppressed:p}}repairMissingUserBoundaries(){const e=this.options.listLocalSessions??C,s=this.options.recordLocalSession??K,o=this.canonicalStore();let n=0;for(const i of e()){if(i.localReady)continue;const a=o.getManifest(i.sessionId)?.firstAvailableSeq;if(a==null)continue;const r=o.readPage(i.sessionId,{limit:1,beforeSequence:a+1}).entries[0];!r||r.sequence!==a||r.message.role!=="user"||(s({sessionId:i.sessionId,agentType:i.agentType,sessionMode:i.sessionMode,managerConfig:i.managerConfig,workDir:i.workDir,agentSessionId:i.agentSessionId,modelId:i.modelId,managerDefaultWorkerAgentType:i.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:i.managerDefaultWorkerModelId,status:i.status,lastActivityAt:i.lastActivityAt,lastMessagePreview:i.lastMessagePreview,localReady:!0,firstUserMessageId:r.message.id,firstUserBodyRevision:r.revision}),n+=1)}return n}listManagedClaims(){return Object.values(this.readManagedEchoState().claims)}listManagedSuppressions(){return Object.values(this.readManagedEchoState().suppressions)}upsertManagedClaim(e){k(e);const s=this.readManagedEchoState();s.claims[e.canonicalMessageId]=e,g(this.managedEchoStateFile(),s)}updateManagedClaimSource(e,s,o){const n=this.readManagedEchoState();let i=!1;for(const c of Object.values(n.claims))c.sessionId!==e||c.sourceAgentType!==s||(c.sourceSessionKey=o,i=!0);i&&g(this.managedEchoStateFile(),n)}removeManagedClaim(e){const s=this.readManagedEchoState();s.claims[e]&&(delete s.claims[e],g(this.managedEchoStateFile(),s))}completeManagedClaim(e,s){const o=this.readManagedEchoState();delete o.claims[e],s&&(O(s),o.suppressions[w(s.agentType,s.sourceSessionKey)]=s),g(this.managedEchoStateFile(),o)}removeManagedSuppression(e,s){const o=this.readManagedEchoState(),n=w(e,s);o.suppressions[n]&&(delete o.suppressions[n],g(this.managedEchoStateFile(),o))}listSourceBindings(){return Object.values(this.readSources().sources).filter(e=>!this.isSourceDeleted(e.agentType,e.sourceSessionKey))}purgeSession(e){if(!e)throw new Error("sessionId is required");const s=this.readSources();let o=!1;for(const[a,r]of Object.entries(s.sources))r.sessionId===e&&(delete s.sources[a],o=!0);o&&g(this.sourcesFile(),s);const n=this.readManagedEchoState();let i=!1;for(const[a,r]of Object.entries(n.claims))r.sessionId===e&&(delete n.claims[a],i=!0);for(const[a,r]of Object.entries(n.suppressions))r.sessionId===e&&(delete n.suppressions[a],i=!0);i&&g(this.managedEchoStateFile(),n);const c=f.join(this.rootDir(),"ledger");if(l.existsSync(c))for(const a of l.readdirSync(c,{withFileTypes:!0})){if(!a.isFile()||!a.name.endsWith(".json"))continue;const r=f.join(c,a.name),m=b(r);if(m.daemonInstallationId!==this.installationId())throw new Error("native event ledger belongs to another daemon installation");m.sessionId===e&&l.unlinkSync(r)}}isSourceDeleted(e,s){return(this.options.deletionStore??new V({rootDir:f.basename(this.rootDir())==="native-fusion"?f.dirname(this.rootDir()):this.rootDir(),daemonInstallationId:this.installationId()})).has(e,s)}observeNativeTitle(e,s,o){const n=o.trim().slice(0,120);if(!n)return!1;const i=this.readSources().sources[w(e,s)];if(!i||this.isSourceDeleted(e,s))return!1;const c=(this.options.listLocalSessions??C)().find(m=>m.sessionId===i.sessionId);if(c?.titleLockedByUser||c?.titleSource==="agent_native"&&c.title===n)return!1;const a=h.createHash("sha256").update(`${e}\0${s}\0${n}`).digest("hex");return(this.options.recordLocalSession??K)({sessionId:i.sessionId,agentType:i.agentType,workDir:c?.workDir??"",agentSessionId:s,title:n,titleSource:"agent_native",titleOriginEventId:`agent-native:${a}`}),!0}recordIndex(e,s,o,n){const i=this.options.recordLocalSession??K,c=e.terminal?`terminal:${h.createHash("sha256").update(e.sourceEventKey).digest("hex")}`:void 0;i({sessionId:s,agentType:e.agentType,workDir:e.workDir??"",agentSessionId:e.sourceSessionKey,modelId:e.modelId??null,status:e.terminal?"completed":"active",attentionId:c,lastActivityAt:new Date(e.ts).toISOString(),lastMessagePreview:e.role==="user"||e.role==="agent"?j(e.payload):void 0,lastMessagePreviewMessageId:e.role==="user"||e.role==="agent"?o:void 0,lastMessagePreviewAt:e.role==="user"||e.role==="agent"?new Date(e.ts).toISOString():void 0,...e.role==="user"&&o?{localReady:!0,firstUserMessageId:o,firstUserBodyRevision:n}:{},title:e.title?.trim()||(e.role==="user"?j(e.payload)??"":void 0),titleSource:e.title?.trim()?e.titleSource??"first_user_fallback":"first_user_fallback",titleOriginEventId:e.sourceEventKey})}readSources(){const e=this.sourcesFile();if(!l.existsSync(e))return{schemaVersion:S,daemonInstallationId:this.installationId(),sources:{}};const s=b(e);if(s.schemaVersion!==S||s.daemonInstallationId!==this.installationId()||!s.sources)throw new Error("invalid native source binding store");for(const o of Object.values(s.sources))if(o.daemonInstallationId!==this.installationId())throw new Error("native source binding belongs to another daemon installation");return s}installationId(){return this.options.daemonInstallationId??P()}canonicalStore(){return this.options.canonicalStore??H()}rootDir(){return this.options.rootDir??L("native-fusion")}sourcesFile(){return f.join(this.rootDir(),"sources.json")}managedEchoStateFile(){return f.join(this.rootDir(),"managed-echo-state.json")}readManagedEchoState(){const e=this.managedEchoStateFile();if(!l.existsSync(e))return{schemaVersion:S,daemonInstallationId:this.installationId(),claims:{},suppressions:{}};const s=b(e);if(s.schemaVersion!==S||s.daemonInstallationId!==this.installationId()||!s.claims||!s.suppressions)throw new Error("invalid managed echo state");for(const o of Object.values(s.claims))k(o);for(const o of Object.values(s.suppressions))O(o);return s}ledgerFile(e,s){const o=h.createHash("sha256").update(`${e}\0${s}`).digest("hex");return f.join(this.rootDir(),"ledger",`${o}.json`)}}function z(t){if(!t.agentType||!t.sourceSessionKey||!t.sourceEventKey||!t.payload)throw new Error("invalid native event");if(!Number.isFinite(t.ts))throw new Error("invalid native event timestamp")}function w(t,e){return h.createHash("sha256").update(`${t}\0${e}`).digest("hex")}function G(t,e,s){return`native-session-${h.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function Q(t,e,s){return`native-${h.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function X(t){return h.createHash("sha256").update(JSON.stringify({agentType:t.agentType,sourceSessionKey:t.sourceSessionKey,sourceEventKey:t.sourceEventKey,role:t.role,ts:t.ts,payload:t.payload,canonicalMessageKey:t.canonicalMessageKey??null,canonicalMutation:t.canonicalMutation??"append"})).digest("hex")}function k(t){if(!t.sessionId||!t.canonicalMessageId||!t.textHash||!Number.isFinite(t.createdAt)||!["codex","claude","opencode"].includes(t.sourceAgentType)||!["suppress_following","import_following"].includes(t.managedEchoPolicy))throw new Error("invalid managed echo claim")}function O(t){if(!t.sessionId||!t.sourceSessionKey||!t.claimSourceEventKey||!["codex","claude","opencode"].includes(t.agentType)||!Number.isFinite(t.startedAt)||!Number.isFinite(t.endsAt)||t.endsAt<t.startedAt)throw new Error("invalid managed echo suppression")}function Y(t,e){if(!t)return e;try{const s=JSON.parse(t),o=JSON.parse(e);if(s.type!=="tool"||o.type!=="tool"||s.toolCallId!==o.toolCallId)return e;const n=typeof o.name=="string"?o.name:"",i=/(?:_output|_result)$/.test(n);return JSON.stringify({...s,...o,...i&&typeof s.name=="string"?{name:s.name}:{}})}catch{return e}}function Z(t,e){if(!t||t.toolCallId!==e.toolCallId)return e;const s=/(?:_output|_result)$/.test(e.name);return{...e,name:s?t.name:e.name,...e.args===void 0&&t.args!==void 0?{args:t.args}:{},...e.result===void 0&&t.result!==void 0?{result:t.result}:{}}}function j(t){return R(t,120)}function g(t,e){const s=f.dirname(t);l.mkdirSync(s,{recursive:!0,mode:448});const o=`${t}.tmp-${process.pid}-${h.randomBytes(6).toString("hex")}`;let n;try{n=l.openSync(o,"wx",384),l.writeFileSync(n,`${JSON.stringify(e,null,2)}
|
|
2
|
-
`,"utf8"),l.fsyncSync(n),l.closeSync(n),n=void 0,l.renameSync(o,t)}finally{n!==void 0&&l.closeSync(n);try{l.unlinkSync(o)}catch{}}}function
|
|
1
|
+
import p from"node:crypto";import l from"node:fs";import h from"node:path";import{sessionPreviewFromPayload as L}from"@shennian/wire";import{resolvePersonalSyncV1Path as R}from"../personal-sync/paths.js";import{getCanonicalSessionStore as P,getDaemonInstallationId as H,listSessionRecords as F,recordSession as w}from"../session/store.js";import{readToolDetail as U,writeToolDetail as J}from"../session/tool-detail-store.js";import{NativeSourceDeletionStore as V}from"./native-source-deletions.js";import{recordPersonalSyncLocalMetric as A}from"../personal-sync/metrics.js";const I=1;class q extends Error{code="missing_user_boundary";constructor(e){super(`native source ${e} does not start at a visible user boundary`),this.name="MissingUserBoundaryError"}}class W extends Error{code="native_source_deleted";constructor(e){super(`native source ${e} is blocked by a durable deletion tombstone`),this.name="NativeSourceDeletedError"}}class le{options;constructor(e={}){this.options=e}commitEvents(e){return[...e].sort((s,o)=>s.ts-o.ts).map(s=>this.commitEvent(s))}updateMessageDeliveryState(e,s,o){if(!this.canonicalStore().updateMessageDeliveryState(e,s,o))return;const i=(this.options.listLocalSessions??F)().find(a=>a.sessionId===e);if(!i)return;(this.options.recordLocalSession??w)({...i})}commitEvent(e){if(z(e),this.isSourceDeleted(e.agentType,e.sourceSessionKey))throw new W(e.sourceSessionKey);const s=this.installationId(),o=this.readSources(),n=b(e.agentType,e.sourceSessionKey),i=o.sources[n],c=X(e),a=this.ledgerFile(e.agentType,e.sourceEventKey),r=l.existsSync(a)?D(a):null;if(r&&r.daemonInstallationId!==s)throw new Error("native event ledger belongs to another daemon installation");if(r&&r.eventFingerprint!==c)throw new Error(`native event identity conflict: ${e.sourceEventKey}`);if(!i&&!r&&e.role!=="user")throw new q(e.sourceSessionKey);const m=e.claimSessionId??null,d=i?.sessionId??r?.sessionId??m??G(s,e.agentType,e.sourceSessionKey);if(m&&i&&i.sessionId!==m)throw new Error(`managed claim session conflicts with source binding ${e.sourceSessionKey}`);const f=this.canonicalStore(),S=e.localDisposition==="managed_echo_suppressed"||r?.mappedBy==="managed_echo_suppressed",u=r?.canonicalMessageId??(S?null:e.claimCanonicalMessageId??Q(s,e.agentType,e.canonicalMessageKey??e.sourceEventKey));let y=f.getManifest(d)?.bodyRevision??0,T=!!r;const M=!S&&!!e.claimCanonicalMessageId;if(M){if(!u)throw new Error("managed echo claim requires a canonical message");const E=f.getMessageState(d,u);if(!E||E.tombstoned)throw new Error(`managed echo claim target is not durable: ${u}`)}if(!r&&!M&&!S){if(!u)throw new Error("native event requires a canonical message");const E=e.canonicalMessageKey?f.getMessageState(d,u):null,x=e.canonicalMutation==="upsert"||!!E,$=x?E??f.getMessageState(d,u):null,_={id:u,sessionId:d,role:e.role,ts:$?.message?.ts??e.ts,payload:Y($?.message?.payload,e.payload)},C=x?f.upsert(d,_):f.append(d,_);if(y=C.bodyRevision,T=C.duplicate,e.localToolDetail){const j=this.options.writeLocalToolDetail??J,N=this.options.readLocalToolDetail??U,B=Z(N(d,e.localToolDetail.toolCallId),e.localToolDetail);j({sessionId:d,...B,ts:e.ts,detailRef:{toolIndex:0}})}}e.terminal&&(y=f.completeStartedUserMessages(d,e.ts).at(-1)?.bodyRevision??y),(!r||e.terminal)&&!S&&this.recordIndex(e,d,u,y);const K=new Date().toISOString();return r||g(a,{schemaVersion:I,daemonInstallationId:s,sessionId:d,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceEventKey:e.sourceEventKey,eventFingerprint:c,canonicalMessageId:u,canonicalRevision:y,cursor:e.cursor??null,mappedBy:S?"managed_echo_suppressed":M?"managed_echo_claim":e.sourceMode,eventTs:e.ts,createdAt:K}),o.sources[n]={schemaVersion:I,daemonInstallationId:s,sessionId:d,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceMode:r?i?.sourceMode??e.sourceMode:e.sourceMode,lastCursor:r?i?.lastCursor??r.cursor:e.cursor??i?.lastCursor??null,bodyRevision:y,createdAt:i?.createdAt??K,updatedAt:K},g(this.sourcesFile(),o),!r&&e.cursor&&A("sourceCursorAdvanced"),!r&&!M&&!S&&!T&&A("canonicalCommits"),{sessionId:d,messageId:u,bodyRevision:y,duplicate:T,claimed:M,suppressed:S}}repairMissingUserBoundaries(){const e=this.options.listLocalSessions??F,s=this.options.recordLocalSession??w,o=this.canonicalStore();let n=0;for(const i of e()){if(i.localReady)continue;const a=o.getManifest(i.sessionId)?.firstAvailableSeq;if(a==null)continue;const r=o.readPage(i.sessionId,{limit:1,beforeSequence:a+1}).entries[0];!r||r.sequence!==a||r.message.role!=="user"||(s({sessionId:i.sessionId,agentType:i.agentType,sessionMode:i.sessionMode,managerConfig:i.managerConfig,workDir:i.workDir,agentSessionId:i.agentSessionId,modelId:i.modelId,managerDefaultWorkerAgentType:i.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:i.managerDefaultWorkerModelId,status:i.status,lastActivityAt:i.lastActivityAt,lastMessagePreview:i.lastMessagePreview,localReady:!0,firstUserMessageId:r.message.id,firstUserBodyRevision:r.revision}),n+=1)}return n}listManagedClaims(){return Object.values(this.readManagedEchoState().claims)}listManagedSuppressions(){return Object.values(this.readManagedEchoState().suppressions)}upsertManagedClaim(e){k(e);const s=this.readManagedEchoState();s.claims[e.canonicalMessageId]=e,g(this.managedEchoStateFile(),s)}updateManagedClaimSource(e,s,o){const n=this.readManagedEchoState();let i=!1;for(const c of Object.values(n.claims))c.sessionId!==e||c.sourceAgentType!==s||(c.sourceSessionKey=o,i=!0);i&&g(this.managedEchoStateFile(),n)}removeManagedClaim(e){const s=this.readManagedEchoState();s.claims[e]&&(delete s.claims[e],g(this.managedEchoStateFile(),s))}completeManagedClaim(e,s){const o=this.readManagedEchoState();delete o.claims[e],s&&(O(s),o.suppressions[b(s.agentType,s.sourceSessionKey)]=s),g(this.managedEchoStateFile(),o)}removeManagedSuppression(e,s){const o=this.readManagedEchoState(),n=b(e,s);o.suppressions[n]&&(delete o.suppressions[n],g(this.managedEchoStateFile(),o))}listSourceBindings(){return Object.values(this.readSources().sources).filter(e=>!this.isSourceDeleted(e.agentType,e.sourceSessionKey))}purgeSession(e){if(!e)throw new Error("sessionId is required");const s=this.readSources();let o=!1;for(const[a,r]of Object.entries(s.sources))r.sessionId===e&&(delete s.sources[a],o=!0);o&&g(this.sourcesFile(),s);const n=this.readManagedEchoState();let i=!1;for(const[a,r]of Object.entries(n.claims))r.sessionId===e&&(delete n.claims[a],i=!0);for(const[a,r]of Object.entries(n.suppressions))r.sessionId===e&&(delete n.suppressions[a],i=!0);i&&g(this.managedEchoStateFile(),n);const c=h.join(this.rootDir(),"ledger");if(l.existsSync(c))for(const a of l.readdirSync(c,{withFileTypes:!0})){if(!a.isFile()||!a.name.endsWith(".json"))continue;const r=h.join(c,a.name),m=D(r);if(m.daemonInstallationId!==this.installationId())throw new Error("native event ledger belongs to another daemon installation");m.sessionId===e&&l.unlinkSync(r)}}isSourceDeleted(e,s){return(this.options.deletionStore??new V({rootDir:h.basename(this.rootDir())==="native-fusion"?h.dirname(this.rootDir()):this.rootDir(),daemonInstallationId:this.installationId()})).has(e,s)}observeNativeTitle(e,s,o){const n=o.trim().slice(0,120);if(!n)return!1;const i=this.readSources().sources[b(e,s)];if(!i||this.isSourceDeleted(e,s))return!1;const c=(this.options.listLocalSessions??F)().find(m=>m.sessionId===i.sessionId);if(c?.titleLockedByUser||c?.titleSource==="agent_native"&&c.title===n)return!1;const a=p.createHash("sha256").update(`${e}\0${s}\0${n}`).digest("hex");return(this.options.recordLocalSession??w)({sessionId:i.sessionId,agentType:i.agentType,workDir:c?.workDir??"",agentSessionId:s,title:n,titleSource:"agent_native",titleOriginEventId:`agent-native:${a}`}),!0}recordIndex(e,s,o,n){const i=this.options.recordLocalSession??w,c=e.terminal?`terminal:${p.createHash("sha256").update(e.sourceEventKey).digest("hex")}`:void 0,a=e.role==="user"||e.role==="agent"?v(e.payload):null,r=a&&o?{lastMessagePreview:a,lastMessagePreviewMessageId:o,lastMessagePreviewAt:new Date(e.ts).toISOString()}:{};i({sessionId:s,agentType:e.agentType,workDir:e.workDir??"",agentSessionId:e.sourceSessionKey,modelId:e.modelId??null,status:e.terminal?"completed":"active",attentionId:c,lastActivityAt:new Date(e.ts).toISOString(),...r,...e.role==="user"&&o?{localReady:!0,firstUserMessageId:o,firstUserBodyRevision:n}:{},title:e.title?.trim()||(e.role==="user"?v(e.payload)??"":void 0),titleSource:e.title?.trim()?e.titleSource??"first_user_fallback":"first_user_fallback",titleOriginEventId:e.sourceEventKey})}readSources(){const e=this.sourcesFile();if(!l.existsSync(e))return{schemaVersion:I,daemonInstallationId:this.installationId(),sources:{}};const s=D(e);if(s.schemaVersion!==I||s.daemonInstallationId!==this.installationId()||!s.sources)throw new Error("invalid native source binding store");for(const o of Object.values(s.sources))if(o.daemonInstallationId!==this.installationId())throw new Error("native source binding belongs to another daemon installation");return s}installationId(){return this.options.daemonInstallationId??H()}canonicalStore(){return this.options.canonicalStore??P()}rootDir(){return this.options.rootDir??R("native-fusion")}sourcesFile(){return h.join(this.rootDir(),"sources.json")}managedEchoStateFile(){return h.join(this.rootDir(),"managed-echo-state.json")}readManagedEchoState(){const e=this.managedEchoStateFile();if(!l.existsSync(e))return{schemaVersion:I,daemonInstallationId:this.installationId(),claims:{},suppressions:{}};const s=D(e);if(s.schemaVersion!==I||s.daemonInstallationId!==this.installationId()||!s.claims||!s.suppressions)throw new Error("invalid managed echo state");for(const o of Object.values(s.claims))k(o);for(const o of Object.values(s.suppressions))O(o);return s}ledgerFile(e,s){const o=p.createHash("sha256").update(`${e}\0${s}`).digest("hex");return h.join(this.rootDir(),"ledger",`${o}.json`)}}function z(t){if(!t.agentType||!t.sourceSessionKey||!t.sourceEventKey||!t.payload)throw new Error("invalid native event");if(!Number.isFinite(t.ts))throw new Error("invalid native event timestamp")}function b(t,e){return p.createHash("sha256").update(`${t}\0${e}`).digest("hex")}function G(t,e,s){return`native-session-${p.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function Q(t,e,s){return`native-${p.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function X(t){return p.createHash("sha256").update(JSON.stringify({agentType:t.agentType,sourceSessionKey:t.sourceSessionKey,sourceEventKey:t.sourceEventKey,role:t.role,ts:t.ts,payload:t.payload,canonicalMessageKey:t.canonicalMessageKey??null,canonicalMutation:t.canonicalMutation??"append"})).digest("hex")}function k(t){if(!t.sessionId||!t.canonicalMessageId||!t.textHash||!Number.isFinite(t.createdAt)||!["codex","claude","opencode"].includes(t.sourceAgentType)||!["suppress_following","import_following"].includes(t.managedEchoPolicy))throw new Error("invalid managed echo claim")}function O(t){if(!t.sessionId||!t.sourceSessionKey||!t.claimSourceEventKey||!["codex","claude","opencode"].includes(t.agentType)||!Number.isFinite(t.startedAt)||!Number.isFinite(t.endsAt)||t.endsAt<t.startedAt)throw new Error("invalid managed echo suppression")}function Y(t,e){if(!t)return e;try{const s=JSON.parse(t),o=JSON.parse(e);if(s.type!=="tool"||o.type!=="tool"||s.toolCallId!==o.toolCallId)return e;const n=typeof o.name=="string"?o.name:"",i=/(?:_output|_result)$/.test(n);return JSON.stringify({...s,...o,...i&&typeof s.name=="string"?{name:s.name}:{}})}catch{return e}}function Z(t,e){if(!t||t.toolCallId!==e.toolCallId)return e;const s=/(?:_output|_result)$/.test(e.name);return{...e,name:s?t.name:e.name,...e.args===void 0&&t.args!==void 0?{args:t.args}:{},...e.result===void 0&&t.result!==void 0?{result:t.result}:{}}}function v(t){return L(t,120)}function g(t,e){const s=h.dirname(t);l.mkdirSync(s,{recursive:!0,mode:448});const o=`${t}.tmp-${process.pid}-${p.randomBytes(6).toString("hex")}`;let n;try{n=l.openSync(o,"wx",384),l.writeFileSync(n,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,"utf8"),l.fsyncSync(n),l.closeSync(n),n=void 0,l.renameSync(o,t)}finally{n!==void 0&&l.closeSync(n);try{l.unlinkSync(o)}catch{}}}function D(t){return JSON.parse(l.readFileSync(t,"utf8"))}export{q as MissingUserBoundaryError,le as NativeFusionLocalRepository,W as NativeSourceDeletedError};
|
|
@@ -2,6 +2,7 @@ export declare function normalizeText(text: string): string;
|
|
|
2
2
|
export declare function readTimestamp(value: unknown): number | null;
|
|
3
3
|
export declare function safeParse(line: string): Record<string, unknown> | null;
|
|
4
4
|
export declare function readJsonlLines(filePath: string, startOffset: number, onLine: (line: string, lineOffset: number) => void): number;
|
|
5
|
+
export declare function findJsonlLookbackOffset(filePath: string, startOffset: number, lineCount?: number): number;
|
|
5
6
|
export declare function readClaudeEventCwd(parsed: Record<string, unknown>): string | null;
|
|
6
7
|
export declare function isClaudeSubagentTranscript(filePath: string): boolean;
|
|
7
8
|
export declare function normalizePathForCompare(filePath: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createHash as
|
|
2
|
-
`).trim())}const
|
|
3
|
-
`).replace(/\n+$/g,""):""}function v(e){if(typeof e!="string")return null;const t=Date.parse(e);return Number.isFinite(t)?t:null}function B(e){try{const t=JSON.parse(e);return typeof t=="object"&&t!==null?t:null}catch{return null}}function J(e,t,
|
|
4
|
-
`).trim()}function
|
|
1
|
+
import{createHash as N}from"node:crypto";import l from"node:fs";import S from"node:os";import r from"node:path";const b=64*1024*1024,I=["/root/.claude-mem/observer-session",r.join(S.tmpdir(),"shennian-agent-e2e")],E=I.map(z);function R(e){return w(e.replace(/\r\n/g,`
|
|
2
|
+
`).trim())}const _=/^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;function w(e){if(!e)return"";if(!/(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(e))return e;const n=e.replace(_,"");return n.trim()?n.replace(/[ \t]+\n/g,`
|
|
3
|
+
`).replace(/\n+$/g,""):""}function v(e){if(typeof e!="string")return null;const t=Date.parse(e);return Number.isFinite(t)?t:null}function B(e){try{const t=JSON.parse(e);return typeof t=="object"&&t!==null?t:null}catch{return null}}function J(e,t,n){const i=l.statSync(e).size;if(t>=i)return i;const f=l.openSync(e,"r");try{const m=Buffer.allocUnsafe(262144);let a=t,g=t,d=Buffer.alloc(0),h=!1;for(;a<i;){const y=l.readSync(f,m,0,Math.min(262144,i-a),a);if(y<=0)break;a+=y;let c=m.subarray(0,y);d.length>0&&(c=Buffer.concat([d,c]),d=Buffer.alloc(0));let u=0;for(;u<c.length;){const s=c.indexOf(10,u);if(s<0)break;const x=c.subarray(u,s),C=g;g+=s-u+1,u=s+1,!h&&x.length>0&&x.length<=b&&n(x.toString("utf8"),C),h=!1}if(u<c.length){const s=c.subarray(u);h||s.length>b?(h=!0,g+=s.length,d=Buffer.alloc(0)):d=Buffer.from(s)}}return g}finally{l.closeSync(f)}}function M(e,t,n=64){if(t<=0||n<=0)return 0;const o=l.openSync(e,"r");try{let i=t,f=n;for(;i>0;){const p=Math.min(i,65536);i-=p;const m=Buffer.allocUnsafe(p);l.readSync(o,m,0,p,i);for(let a=p-1;a>=0;a-=1)if(!(m[a]!==10||i+a+1===t)&&(f-=1,f===0))return i+a+1}return 0}finally{l.closeSync(o)}}function F(e){return typeof e.cwd=="string"&&e.cwd.trim()?e.cwd:null}function G(e){return r.basename(r.dirname(e))==="subagents"}function T(e){const t=r.resolve(e);return process.platform==="win32"?t.toLowerCase():t}function k(e,t){const n=T(e),o=T(t);return n===o||n.startsWith(o+r.sep)}function z(e){return r.resolve(e).replace(/\//g,"-")}function U(e){return I.some(t=>k(e,t))}function D(e){return E.includes(e)}function V(e){const t=r.join(S.homedir(),".claude","projects"),n=r.relative(t,e);if(!n||n.startsWith("..")||r.isAbsolute(n))return!1;const[o]=n.split(r.sep);return o?D(o):!1}function H(e,t){return`${e}:${t}`}function W(e,t,n){return`${N("sha1").update(e).digest("hex").slice(0,16)}:${t}:${n}`}function K(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function X(e){return e.replace(/\r\n/g,`
|
|
4
|
+
`).trim()}function Y(e){if(typeof e!="string")return e;const t=e.trim();if(!t)return"";try{return JSON.parse(t)}catch{return e}}function q(e){const t={v:1,type:"tool",name:e.name,toolCallId:e.toolCallId,detailRevision:e.detailRevision,status:e.status,resultOmitted:!0,detailAvailable:!0,...e.durationMs!==void 0?{durationMs:e.durationMs}:{}};return JSON.stringify(t)}function A(e){return e.replace(/[[\]\\]/g,"\\$&")}function L(e,t="Generated image"){return``}function Q(e){const t=r.basename(e)||"generated-image.png";return JSON.stringify({v:1,type:"image",content:L(e,t),attachments:[{path:e,name:t,mimeType:"image/png",kind:"image"}]})}function Z(e){const t=r.extname(e).toLowerCase();return t===".png"?"image/png":t===".jpg"||t===".jpeg"?"image/jpeg":t===".webp"?"image/webp":t===".gif"?"image/gif":"application/octet-stream"}export{Q as buildImagePayload,L as buildMarkdownImageRef,q as buildToolPayload,z as encodeClaudeProjectDir,A as escapeMarkdownLabel,M as findJsonlLookbackOffset,Z as inferMimeType,G as isClaudeSubagentTranscript,k as isSameOrChildPath,H as makeCursor,W as makeEventKey,T as normalizePathForCompare,R as normalizeText,X as normalizeToolText,Y as parseStructuredString,F as readClaudeEventCwd,J as readJsonlLines,v as readTimestamp,B as safeParse,K as safeStringify,D as shouldIgnoreClaudeProjectDir,V as shouldIgnoreClaudeTranscriptPath,U as shouldIgnoreNativeScanPath};
|
|
@@ -2,6 +2,7 @@ import type { ParsedNativeEvent } from './types.js';
|
|
|
2
2
|
export { listCodexRolloutFiles, lookupCodexThreadName, parseCodexRolloutChunk, } from './codex-parser.js';
|
|
3
3
|
export { inspectNativeSessionFile, normalizeNativeTimestamp } from './session-header.js';
|
|
4
4
|
export { listOpenCodeSessionFiles, parseOpenCodeSessionFile, parseOpenCodeSessionSnapshot, type OpenCodeSnapshot, } from './opencode-parser.js';
|
|
5
|
+
export declare function listNativeTranscriptRoots(): string[];
|
|
5
6
|
export declare function listClaudeTranscriptFiles(): string[];
|
|
6
7
|
export declare function lookupClaudeTranscriptCwd(sourceSessionKey: string): string | null;
|
|
7
8
|
export declare function parseClaudeTranscriptChunk(filePath: string, startOffset: number): {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import g from"node:fs";import
|
|
1
|
+
import g from"node:fs";import l from"node:os";import r from"node:path";import{listCodexRolloutFiles as G,lookupCodexThreadName as H,parseCodexRolloutChunk as Q}from"./codex-parser.js";import{inspectNativeSessionFile as V,normalizeNativeTimestamp as X}from"./session-header.js";import{listOpenCodeSessionFiles as Z,parseOpenCodeSessionFile as $,parseOpenCodeSessionSnapshot as P}from"./opencode-parser.js";import{makeCursor as j,makeEventKey as C,normalizeText as v,readClaudeEventCwd as k,readJsonlLines as S,readTimestamp as w,safeParse as T,isClaudeSubagentTranscript as D,shouldIgnoreClaudeProjectDir as N,shouldIgnoreClaudeTranscriptPath as z,shouldIgnoreNativeScanPath as b}from"./parser-common.js";function M(){return[r.join(l.homedir(),".codex","sessions"),r.join(l.homedir(),".codex","archived_sessions"),r.join(l.homedir(),".claude","projects"),r.join(l.homedir(),".local","share","opencode")]}function E(e){const n=l.homedir();return e.startsWith(n)?e.slice(n.length).replace(/^\//,"")||"~":e.replace(/\//g,"-")}function I(){const e=r.join(l.homedir(),".claude","projects");if(!g.existsSync(e))return[];const n=[],i=t=>{for(const o of g.readdirSync(t,{withFileTypes:!0})){const a=r.join(t,o.name);if(o.isDirectory()){if(t===e&&N(o.name))continue;i(a)}else o.isFile()&&o.name.endsWith(".jsonl")&&n.push(a)}};return i(e),n.sort()}function B(e){if(!e)return null;const n=I().find(t=>r.basename(t,".jsonl")===e);if(!n)return null;let i=null;return S(n,0,t=>{if(i||!t.trim())return;const o=T(t);if(!o)return;const a=k(o);a&&(i=a)}),i}function J(e,n){const i=g.statSync(e).size;if(D(e)||z(e))return{nextOffset:i,events:[]};const t=[],o=r.basename(e,".jsonl"),a=E(r.dirname(e).replace(r.join(l.homedir(),".claude","projects")+r.sep,""));if(b(a))return{nextOffset:i,events:[]};let p="";return{nextOffset:S(e,n,(_,d)=>{const s=T(_);if(!s)return;const y=w(s.timestamp),x=typeof s.type=="string"?s.type:"";if(!y||!x)return;const m=k(s);if(!(m&&b(m))){if(x==="user"){const c=typeof s.message=="object"&&s.message!==null?s.message:null,u=typeof c?.content=="string"?v(c.content):"";if(!u)return;p||(p=u.slice(0,80)),t.push({agentType:"claude",sourceMode:"claude_transcript_import",sourceSessionKey:o,sourceEventKey:C(e,d,"user"),cursor:j(e,d),role:"user",ts:y,payload:u,title:p,titleSource:"first_user_fallback",workDir:m??a})}else if(x==="assistant"){const c=typeof s.message=="object"&&s.message!==null?s.message:null,u=Array.isArray(c?.content)?c.content:[],h=v(u.map(f=>typeof f=="object"&&f!==null&&typeof f.text=="string"?f.text:"").filter(Boolean).join(`
|
|
2
2
|
|
|
3
|
-
`));if(!
|
|
3
|
+
`));if(!h)return;const F=typeof c?.model=="string"?c.model:null;t.push({agentType:"claude",sourceMode:"claude_transcript_import",sourceSessionKey:o,sourceEventKey:C(e,d,"assistant"),cursor:j(e,d),role:"agent",ts:y,payload:h,title:p,titleSource:"first_user_fallback",modelId:F,workDir:m??a})}}}),events:t}}export{V as inspectNativeSessionFile,I as listClaudeTranscriptFiles,G as listCodexRolloutFiles,M as listNativeTranscriptRoots,Z as listOpenCodeSessionFiles,B as lookupClaudeTranscriptCwd,H as lookupCodexThreadName,X as normalizeNativeTimestamp,J as parseClaudeTranscriptChunk,Q as parseCodexRolloutChunk,$ as parseOpenCodeSessionFile,P as parseOpenCodeSessionSnapshot};
|
|
@@ -8,6 +8,7 @@ export declare class NativeSessionFusionService {
|
|
|
8
8
|
private scanPromise;
|
|
9
9
|
private queuedTargetFiles;
|
|
10
10
|
private pendingBoundaryFiles;
|
|
11
|
+
private sourceFiles;
|
|
11
12
|
private fullScanQueued;
|
|
12
13
|
private watchEnabled;
|
|
13
14
|
private lifecycleRevision;
|
|
@@ -33,6 +34,8 @@ export declare class NativeSessionFusionService {
|
|
|
33
34
|
noteManagedSourceSession(sessionId: string, agentType: string, sourceSessionKey: string | null): void;
|
|
34
35
|
handleSessionDeleted(sessionId: string): void;
|
|
35
36
|
private ensureActiveScanning;
|
|
37
|
+
private scanActiveTargets;
|
|
38
|
+
private activeScanTargets;
|
|
36
39
|
private emitExternalActivity;
|
|
37
40
|
private emitExternalTerminal;
|
|
38
41
|
private pruneState;
|