shennian 0.3.85 → 0.3.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +3 -2
  2. package/dist/publish-build-manifest.json +50 -45
  3. package/dist/src/agents/codex-managed-room-tools.js +1 -1
  4. package/dist/src/agents/codex.d.ts +7 -1
  5. package/dist/src/agents/codex.js +10 -10
  6. package/dist/src/agents/manager.d.ts +2 -0
  7. package/dist/src/agents/manager.js +5 -5
  8. package/dist/src/commands/logout.d.ts +3 -0
  9. package/dist/src/commands/logout.js +1 -0
  10. package/dist/src/commands/room.js +2 -2
  11. package/dist/src/daemon-ipc/paths.js +1 -1
  12. package/dist/src/index.js +3 -3
  13. package/dist/src/manager/runtime.d.ts +4 -0
  14. package/dist/src/manager/runtime.js +3 -3
  15. package/dist/src/native-fusion/codex-parser.d.ts +3 -1
  16. package/dist/src/native-fusion/codex-parser.js +3 -3
  17. package/dist/src/native-fusion/parser-common.d.ts +6 -1
  18. package/dist/src/native-fusion/parser-common.js +4 -4
  19. package/dist/src/native-fusion/parsers.d.ts +4 -2
  20. package/dist/src/native-fusion/parsers.js +2 -2
  21. package/dist/src/native-fusion/service.js +2 -2
  22. package/dist/src/room/activation-service.d.ts +1 -0
  23. package/dist/src/room/activation-service.js +4 -4
  24. package/dist/src/room/managed-context.d.ts +1 -1
  25. package/dist/src/room/managed-context.js +1 -1
  26. package/dist/src/room/managed-prompt-composer.d.ts +1 -1
  27. package/dist/src/room/managed-prompt-composer.js +17 -15
  28. package/dist/src/room/managed-room-ipc.d.ts +6 -2
  29. package/dist/src/room/managed-room-ipc.js +2 -2
  30. package/dist/src/room/mirror-sync-service.d.ts +1 -0
  31. package/dist/src/room/mirror-sync-service.js +1 -1
  32. package/dist/src/room/room-action-client.d.ts +11 -0
  33. package/dist/src/room/room-action-client.js +1 -1
  34. package/dist/src/room/room-binding-client.d.ts +1 -0
  35. package/dist/src/room/room-binding-client.js +1 -1
  36. package/dist/src/security/managed-agent-policy.d.ts +3 -2
  37. package/dist/src/security/managed-agent-policy.js +1 -1
  38. package/dist/src/session/handlers/chat.d.ts +1 -1
  39. package/dist/src/session/handlers/chat.js +2 -2
  40. package/dist/src/session/manager.d.ts +1 -0
  41. package/dist/src/session/manager.js +1 -1
  42. package/dist/src/session/queue.d.ts +13 -0
  43. package/dist/src/session/queue.js +8 -8
  44. package/node_modules/@shennian/wire/dist/agent-workspace.js +1 -1
  45. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
- import y from"node:crypto";import U from"node:fs";import b from"node:path";import{PublicAttachmentCache as j}from"../room/public-attachment-cache.js";import{PublicRoomClient as z,PublicRoomClientError as A}from"../room/public-room-client.js";import{callDaemonIpc as O}from"../daemon-ipc/client.js";import{DaemonIpcError as S}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as k,resolveManagedRoomIpcPaths as D}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as T}from"../room/managed-context.js";import{createRoomInviteQr as $}from"../room/invite-qr.js";const C="https://shennian.net/install.md",N={write:e=>{process.stdout.write(e)},fail:()=>{process.exitCode=1}};function te(e,t={}){const n=t.client??new z,a=t.cache??new j({client:n}),s=t.io??N,p=t.createInviteQr??$,d=e.command("room").description("Use one explicitly referenced Shennian Room");d.command("create").description("Create a Room as the paired user").requiredOption("--name <name>","Room name").action(async m=>f("room.create",s,async()=>{R("create");const o=m.name.trim();(!o||o.length>80)&&i("invalid_room_name","Room name must contain 1 to 80 characters.");const r=await n.create(o),c=h(r.room,"invalid_room_response");typeof c.inviteUrl!="string"&&i("invalid_room_response","Shennian returned an invalid Room invitation.");const u=await p(c.inviteUrl).catch(()=>({imagePath:null,mimeType:"image/png",payload:c.inviteUrl}));return{...r,sharing:{inviteUrl:c.inviteUrl,inviteQr:u,installGuideUrl:C}}})),d.command("join <invite-url>").description("Join or request access through an official Room invitation").option("--message <message>","Introduction and reason required when owner approval is enabled").action(async(m,o)=>f("room.join",s,async()=>{R("join");const r=I(m,"invite"),c=o.message?.trim();return c&&c.length>500&&i("join_message_too_long","Join request message must be at most 500 characters."),n.join(r.roomId,r.inviteToken,c)})),d.command("open <room-url>").description("Open one Room where the paired user is already a member").action(async m=>f("room.open",s,async()=>{R("open");const o=I(m,"stable");return n.open(o.roomId)})),d.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async m=>f("room.status",s,()=>{const o=g();return o?(m&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),w(o,"status",{})):n.status(x(m))})),d.command("read [room-ref]").description("Read recent, older, or newer messages from one Room").option("--before <sequence>","Read messages before this sequence").option("--after <sequence>","Read messages after this sequence").option("--limit <count>","Maximum messages, 1 to 50","50").action(async(m,o)=>{await f("room.read",s,async()=>{o.before&&o.after&&i("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),o.before&&!q(o.before)&&i("invalid_sequence","--before sequence is invalid."),o.after&&!q(o.after)&&i("invalid_sequence","--after sequence is invalid.");const r=Number(o.limit);(!Number.isInteger(r)||r<1||r>50)&&i("invalid_limit","--limit must be between 1 and 50.");const c=g();if(c)return m&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),w(c,"read",{...o.before?{before:o.before}:{},...o.after?{after:o.after}:{},limit:r});const u=x(m),_=await n.read(u,{before:o.before,after:o.after,limit:r});return G(_,u,a)})}),d.command("send [room-ref]").description("Send text and up to 10 files as the paired user").option("--text <text>","Message text").option("--file <path>","Local file path; repeat for multiple files",P,[]).action(async(m,o)=>{await f("room.send",s,async()=>{const r=o.text?.trim()??"";!r&&o.file.length===0&&i("room_message_empty","Provide --text or at least one --file."),o.file.length>10&&i("room_attachments_too_many","A message can contain at most 10 files.");const c=g();if(c)return m&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),w(c,"send",{clientMessageId:`managed:${y.randomUUID()}`,text:r,files:o.file});const u=x(m),_=[];for(const L of o.file)_.push(await E(n,u,L));return n.send(u,{clientMessageId:`cli:${y.randomUUID()}`,...r?{text:r}:{},..._.length>0?{attachmentIds:_}:{}})})}),d.command("attachment").description("Download Room attachments through Shennian").command("get [room-ref] [attachment-id]").description("Download and verify one Room attachment").action(async(m,o)=>{await f("room.attachment.get",s,async()=>{const r=g(),c=r?m:o;return(!c||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(c))&&i("invalid_attachment_ref","attachment-id is invalid."),r?w(r,"attachment.get",{attachmentId:c}):a.materialize(x(m),c)})}),d.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async m=>f("room.leave",s,()=>(R("leave"),n.leave(M(m))))),d.command("list",{hidden:!0}).action(()=>v(s,"command_removed","Room listing is not available to Agents.",null)),d.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>v(s,"command_removed","Room search is not available to Agents.",null)),d.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>v(s,"command_removed","Managed Agents are created in the Shennian mobile App.",null))}async function f(e,t,n){try{const a=await n();t.write(`${JSON.stringify({ok:!0,command:e,result:a})}
2
- `)}catch(a){if(a instanceof A){v(t,a.code,a.message,a.nextAction);return}if(a instanceof S){v(t,a.code,a.message,null);return}v(t,"room_command_failed","Shennian Room command failed.",null)}}function g(){return process.env[T]?.trim()||null}async function w(e,t,n){const a=process.env[k]?.trim();if(!a||!b.isAbsolute(a))throw new S("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return O({method:"managed-room.execute",params:{context:e,action:t,...n},paths:D(a),timeoutMs:t==="send"||t==="attachment.get"?18e4:15e3})}function R(e){g()&&i("managed_room_action_forbidden",`${e} is not available in managed Room context.`)}function x(e){return e||i("room_ref_required","Use an official Room URL returned by Shennian."),M(e)}function M(e){return I(e,"stable").roomId}function I(e,t){let n;const a=t==="invite"?"invalid_invite_url":"invalid_room_ref",s=t==="invite"?"Use an official Shennian Room invitation URL.":"Use an official Room URL returned by Shennian.";try{n=new URL(e)}catch{i(a,s)}const p=n.pathname.match(/^\/spaces\/([A-Za-z0-9_-]{1,191})$/),d=n.searchParams.get("invite");return(n.protocol!=="https:"||n.hostname!=="app.shennian.net"||n.port||n.username||n.password||n.hash||!p||n.searchParams.has("access")||[...n.searchParams.keys()].some(l=>l!=="invite")||t==="stable"&&d!=null||t==="invite"&&(d==null||!/^[A-Za-z0-9_-]{32,256}$/.test(d)))&&i(a,s),{roomId:p[1],...d?{inviteToken:d}:{}}}function q(e){return/^(0|[1-9]\d{0,19})$/.test(e)}function P(e,t){return[...t,e]}async function E(e,t,n){const a=b.resolve(n);let s;try{s=await U.promises.lstat(a)}catch{i("file_not_found",`File does not exist: ${n}`)}(!s.isFile()||s.isSymbolicLink())&&i("invalid_file",`Not a regular file: ${n}`),(s.size<1||s.size>100*1024*1024)&&i("invalid_file_size","Each file must be between 1 byte and 100 MB.");const p=await U.promises.readFile(a),d=y.createHash("sha256").update(p).digest("hex"),l=F(a),m=await e.initializeAttachment(t,{clientUploadId:`cli:${y.randomUUID()}`,name:b.basename(a),mediaKind:l.startsWith("image/")?"image":l.startsWith("audio/")?"audio":"file",mimeType:l,byteSize:s.size,sha256:d}),o=h(m.attachment,"invalid_attachment_response"),r=h(m.upload,"invalid_attachment_response");(typeof o.id!="string"||typeof r.url!="string"||r.method!=="PUT")&&i("invalid_attachment_response","Shennian returned an invalid attachment response.");const c=h(r.headers,"invalid_attachment_response");return Object.values(c).some(u=>typeof u!="string")&&i("invalid_attachment_response","Shennian returned invalid upload headers."),J(r.url),await e.upload(r.url,c,p),await e.completeAttachment(t,o.id),o.id}function F(e){const t=b.extname(e).toLowerCase();return{".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".gif":"image/gif",".heic":"image/heic",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4",".mp4":"video/mp4",".mov":"video/quicktime",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".json":"application/json",".csv":"text/csv",".zip":"application/zip",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[t]??"application/octet-stream"}function G(e,t,n){return Array.isArray(e.messages)||i("invalid_server_response","Shennian returned invalid messages."),{messages:e.messages.map(a=>{const s=h(a,"invalid_server_response"),p=Array.isArray(s.attachments)?s.attachments:[];return{...s,attachments:p.map(d=>{const l=h(d,"invalid_server_response");return(typeof l.attachmentId!="string"||typeof l.name!="string"||typeof l.mimeType!="string"||typeof l.byteSize!="string"||typeof l.sha256!="string")&&i("invalid_server_response","Shennian returned invalid attachment metadata."),{...l,...n.lookup(t,l)}})}})}}function h(e,t){return(!e||typeof e!="object"||Array.isArray(e))&&i(t,"Invalid structured response."),e}function J(e){let t;try{t=new URL(e)}catch{i("invalid_attachment_response","Invalid upload URL.")}const n=t.protocol==="http:"&&(t.hostname==="localhost"||t.hostname==="127.0.0.1");(t.protocol!=="https:"&&!n||t.username||t.password||t.hash)&&i("invalid_attachment_response","Invalid upload URL.")}function i(e,t){throw new A(e,t)}function v(e,t,n,a){e.write(`${JSON.stringify({ok:!1,error:t,message:n,nextAction:a})}
1
+ import b from"node:crypto";import U from"node:fs";import R from"node:path";import{PublicAttachmentCache as O}from"../room/public-attachment-cache.js";import{PublicRoomClient as j,PublicRoomClientError as A}from"../room/public-room-client.js";import{callDaemonIpc as z}from"../daemon-ipc/client.js";import{DaemonIpcError as S}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as k,resolveManagedRoomIpcPaths as D}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as P}from"../room/managed-context.js";import{createRoomInviteQr as T}from"../room/invite-qr.js";const $="https://shennian.net/install.md",C={write:e=>{process.stdout.write(e)},fail:()=>{process.exitCode=1}};function te(e,t={}){const o=t.client??new j,a=t.cache??new O({client:o}),r=t.io??C,p=t.createInviteQr??T,c=e.command("room").description("Use one explicitly referenced Shennian Room");c.command("create").description("Create a Room as the paired user").requiredOption("--name <name>","Room name").action(async m=>f("room.create",r,async()=>{w("create");const n=m.name.trim();(!n||n.length>80)&&i("invalid_room_name","Room name must contain 1 to 80 characters.");const s=await o.create(n),d=v(s.room,"invalid_room_response");typeof d.inviteUrl!="string"&&i("invalid_room_response","Shennian returned an invalid Room invitation.");const u=await p(d.inviteUrl).catch(()=>({imagePath:null,mimeType:"image/png",payload:d.inviteUrl}));return{...s,sharing:{inviteUrl:d.inviteUrl,inviteQr:u,installGuideUrl:$}}})),c.command("join <invite-url>").description("Join or request access through an official Room invitation").option("--message <message>","Introduction and reason required when owner approval is enabled").action(async(m,n)=>f("room.join",r,async()=>{w("join");const s=I(m,"invite"),d=n.message?.trim();return d&&d.length>500&&i("join_message_too_long","Join request message must be at most 500 characters."),o.join(s.roomId,s.inviteToken,d)})),c.command("open <room-url>").description("Open one Room where the paired user is already a member").action(async m=>f("room.open",r,async()=>{w("open");const n=I(m,"stable");return o.open(n.roomId)})),c.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async m=>f("room.status",r,()=>{const n=h();return n?(m&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(n,"status",{})):o.status(x(m))})),c.command("read [room-ref]").description("Read recent, older, or newer messages from one Room").option("--before <sequence>","Read messages before this sequence").option("--after <sequence>","Read messages after this sequence").option("--limit <count>","Maximum messages, 1 to 50","50").action(async(m,n)=>{await f("room.read",r,async()=>{n.before&&n.after&&i("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),n.before&&!M(n.before)&&i("invalid_sequence","--before sequence is invalid."),n.after&&!M(n.after)&&i("invalid_sequence","--after sequence is invalid.");const s=Number(n.limit);(!Number.isInteger(s)||s<1||s>50)&&i("invalid_limit","--limit must be between 1 and 50.");const d=h();if(d)return m&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(d,"read",{...n.before?{before:n.before}:{},...n.after?{after:n.after}:{},limit:s});const u=x(m),g=await o.read(u,{before:n.before,after:n.after,limit:s});return G(g,u,a)})}),c.command("send [room-ref]").description("Send text and up to 10 files as the paired user").option("--text <text>","Message text").option("--file <path>","Local file path; repeat for multiple files",N,[]).action(async(m,n)=>{await f("room.send",r,async()=>{const s=n.text?.trim()??"";!s&&n.file.length===0&&i("room_message_empty","Provide --text or at least one --file."),n.file.length>10&&i("room_attachments_too_many","A message can contain at most 10 files.");const d=h();if(d)return m&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(d,"send",{clientMessageId:`managed:${b.randomUUID()}`,text:s,files:n.file});const u=x(m),g=[];for(const L of n.file)g.push(await E(o,u,L));return o.send(u,{clientMessageId:`cli:${b.randomUUID()}`,...s?{text:s}:{},...g.length>0?{attachmentIds:g}:{}})})}),c.command("pause").description("Pause the current managed Agent in its bound Room").requiredOption("--reason <reason>","Short reason shown to all Room members").action(async m=>f("room.pause",r,()=>{const n=h();return n||i("managed_room_context_required","Pause is only available in managed Room context."),y(n,"pause",{reason:m.reason})})),c.command("attachment").description("Download Room attachments through Shennian").command("get [room-ref] [attachment-id]").description("Download and verify one Room attachment").action(async(m,n)=>{await f("room.attachment.get",r,async()=>{const s=h(),d=s?m:n;return(!d||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(d))&&i("invalid_attachment_ref","attachment-id is invalid."),s?y(s,"attachment.get",{attachmentId:d}):a.materialize(x(m),d)})}),c.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async m=>f("room.leave",r,()=>(w("leave"),o.leave(q(m))))),c.command("list",{hidden:!0}).action(()=>_(r,"command_removed","Room listing is not available to Agents.",null)),c.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>_(r,"command_removed","Room search is not available to Agents.",null)),c.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>_(r,"command_removed","Managed Agents are created in the Shennian mobile App.",null))}async function f(e,t,o){try{const a=await o();t.write(`${JSON.stringify({ok:!0,command:e,result:a})}
2
+ `)}catch(a){if(a instanceof A){_(t,a.code,a.message,a.nextAction);return}if(a instanceof S){_(t,a.code,a.message,null);return}_(t,"room_command_failed","Shennian Room command failed.",null)}}function h(){return process.env[P]?.trim()||null}async function y(e,t,o){const a=process.env[k]?.trim();if(!a||!R.isAbsolute(a))throw new S("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return z({method:"managed-room.execute",params:{context:e,action:t,...o},paths:D(a),timeoutMs:t==="send"||t==="attachment.get"?18e4:15e3})}function w(e){h()&&i("managed_room_action_forbidden",`${e} is not available in managed Room context.`)}function x(e){return e||i("room_ref_required","Use an official Room URL returned by Shennian."),q(e)}function q(e){return I(e,"stable").roomId}function I(e,t){let o;const a=t==="invite"?"invalid_invite_url":"invalid_room_ref",r=t==="invite"?"Use an official Shennian Room invitation URL.":"Use an official Room URL returned by Shennian.";try{o=new URL(e)}catch{i(a,r)}const p=o.pathname.match(/^\/spaces\/([A-Za-z0-9_-]{1,191})$/),c=o.searchParams.get("invite");return(o.protocol!=="https:"||o.hostname!=="app.shennian.net"||o.port||o.username||o.password||o.hash||!p||o.searchParams.has("access")||[...o.searchParams.keys()].some(l=>l!=="invite")||t==="stable"&&c!=null||t==="invite"&&(c==null||!/^[A-Za-z0-9_-]{32,256}$/.test(c)))&&i(a,r),{roomId:p[1],...c?{inviteToken:c}:{}}}function M(e){return/^(0|[1-9]\d{0,19})$/.test(e)}function N(e,t){return[...t,e]}async function E(e,t,o){const a=R.resolve(o);let r;try{r=await U.promises.lstat(a)}catch{i("file_not_found",`File does not exist: ${o}`)}(!r.isFile()||r.isSymbolicLink())&&i("invalid_file",`Not a regular file: ${o}`),(r.size<1||r.size>100*1024*1024)&&i("invalid_file_size","Each file must be between 1 byte and 100 MB.");const p=await U.promises.readFile(a),c=b.createHash("sha256").update(p).digest("hex"),l=F(a),m=await e.initializeAttachment(t,{clientUploadId:`cli:${b.randomUUID()}`,name:R.basename(a),mediaKind:l.startsWith("image/")?"image":l.startsWith("audio/")?"audio":"file",mimeType:l,byteSize:r.size,sha256:c}),n=v(m.attachment,"invalid_attachment_response"),s=v(m.upload,"invalid_attachment_response");(typeof n.id!="string"||typeof s.url!="string"||s.method!=="PUT")&&i("invalid_attachment_response","Shennian returned an invalid attachment response.");const d=v(s.headers,"invalid_attachment_response");return Object.values(d).some(u=>typeof u!="string")&&i("invalid_attachment_response","Shennian returned invalid upload headers."),J(s.url),await e.upload(s.url,d,p),await e.completeAttachment(t,n.id),n.id}function F(e){const t=R.extname(e).toLowerCase();return{".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".gif":"image/gif",".heic":"image/heic",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4",".mp4":"video/mp4",".mov":"video/quicktime",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".json":"application/json",".csv":"text/csv",".zip":"application/zip",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}[t]??"application/octet-stream"}function G(e,t,o){return Array.isArray(e.messages)||i("invalid_server_response","Shennian returned invalid messages."),{messages:e.messages.map(a=>{const r=v(a,"invalid_server_response"),p=Array.isArray(r.attachments)?r.attachments:[];return{...r,attachments:p.map(c=>{const l=v(c,"invalid_server_response");return(typeof l.attachmentId!="string"||typeof l.name!="string"||typeof l.mimeType!="string"||typeof l.byteSize!="string"||typeof l.sha256!="string")&&i("invalid_server_response","Shennian returned invalid attachment metadata."),{...l,...o.lookup(t,l)}})}})}}function v(e,t){return(!e||typeof e!="object"||Array.isArray(e))&&i(t,"Invalid structured response."),e}function J(e){let t;try{t=new URL(e)}catch{i("invalid_attachment_response","Invalid upload URL.")}const o=t.protocol==="http:"&&(t.hostname==="localhost"||t.hostname==="127.0.0.1");(t.protocol!=="https:"&&!o||t.username||t.password||t.hash)&&i("invalid_attachment_response","Invalid upload URL.")}function i(e,t){throw new A(e,t)}function _(e,t,o,a){e.write(`${JSON.stringify({ok:!1,error:t,message:o,nextAction:a})}
3
3
  `),e.fail()}export{te as registerRoomCommand};
@@ -1 +1 @@
1
- import s from"node:crypto";import a from"node:os";import t from"node:path";import{getShennianDir as i}from"../config/index.js";const f="SHENNIAN_MANAGED_ROOM_IPC_ROOT";function c(e=i(),n=process.platform){const o=t.join(e,"runtime"),r=t.join(o,"daemon-ipc.secret");if(n==="win32"){const p=s.createHash("sha256").update(t.resolve(e).toLowerCase()).digest("hex").slice(0,20);return{runtimeDirectory:o,secretFile:r,endpoint:`\\\\.\\pipe\\shennian-daemon-${p}`,transport:"windows_named_pipe"}}return{runtimeDirectory:o,secretFile:r,endpoint:t.join(o,"daemon.sock"),transport:"unix_socket"}}function m(e=i(),n=a.tmpdir()){const o=s.createHash("sha256").update(t.resolve(e)).digest("hex").slice(0,20);return t.join(n,`sn-mr-${o}`)}function l(e=m(),n=process.platform){return c(e,n)}export{f as MANAGED_ROOM_IPC_ROOT_ENV,m as getManagedRoomIpcRoot,c as resolveDaemonIpcPaths,l as resolveManagedRoomIpcPaths};
1
+ import s from"node:crypto";import p from"node:fs";import c from"node:os";import n from"node:path";import{getShennianDir as a}from"../config/index.js";const R="SHENNIAN_MANAGED_ROOM_IPC_ROOT";function m(e=a(),o=process.platform){const t=n.join(e,"runtime"),r=n.join(t,"daemon-ipc.secret");if(o==="win32"){const i=s.createHash("sha256").update(n.resolve(e).toLowerCase()).digest("hex").slice(0,20);return{runtimeDirectory:t,secretFile:r,endpoint:`\\\\.\\pipe\\shennian-daemon-${i}`,transport:"windows_named_pipe"}}return{runtimeDirectory:t,secretFile:r,endpoint:n.join(t,"daemon.sock"),transport:"unix_socket"}}function d(e=a(),o=c.tmpdir()){const t=s.createHash("sha256").update(n.resolve(e)).digest("hex").slice(0,20);let r=o;try{r=f(o)}catch{}return n.join(r,`sn-mr-${t}`)}function x(e=d(),o=process.platform){return m(e,o)}function f(e){return p.realpathSync.native(e)}export{R as MANAGED_ROOM_IPC_ROOT_ENV,d as getManagedRoomIpcRoot,m as resolveDaemonIpcPaths,x as resolveManagedRoomIpcPaths};
package/dist/src/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import{Command as ie}from"commander";import I from"chalk";import ae from"node:os";import _ from"node:fs";import{loadConfig as d,saveConfig as ce,getConfigPath as me,getShennianDir as le,resolveShennianPath as J,normalizeAutoUpgradePolicy as de}from"./config/index.js";import{CliRelayClient as pe}from"./relay/client.js";import{collectKnownLogSecrets as K,installCliLogRedaction as ge,sanitizeCliLogValue as ue}from"./log-redaction.js";import{registerPairCommand as he,runSmartStart as fe}from"./commands/pair.js";import{clearDaemonPidIfOwner as j,createDaemonInstanceId as Se,clearDaemonLauncher as W,findRunningDaemonProcessIds as ve,isRemoteAccessDisabled as Y,prepareRemoteAccessPause as Ie,stopPreparedRemoteAccessService as we,isIsolatedDaemonE2eInstance as ye,registerDaemonCommand as Re,writeDaemonPid as q,writeDaemonLauncher as Q}from"./commands/daemon.js";import{registerRuntimeCommand as Ce}from"./commands/runtime.js";import{registerManagerCommand as ke}from"./commands/manager.js";import{registerUpgradeCommand as Ee}from"./commands/upgrade.js";import{registerToolsCommand as De}from"./commands/tools.js";import{registerIntegrationCommand as Te}from"./commands/integration.js";import{registerRoomCommand as Ae}from"./commands/room.js";import{SessionManager as be}from"./session/manager.js";import{SERVERS as M,regionToUrl as Oe,urlToRegion as Ue}from"./region.js";import{getCurrentVersion as X,handleStartupCrashCheck as Ne}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Pe}from"./upgrade/scheduler.js";import{detectAgents as $e}from"./agents/detect.js";import{augmentProcessPath as Z}from"./env-path.js";Z();const ee=X();import{getCachedAgentInfos as _e,resolveAgentInfos as Me}from"./agents/model-registry.js";import{initCliLogReporter as xe,reportLog as w}from"./log-reporter.js";import{isNativeFusionEnabled as Le}from"./native-fusion/config.js";import{NativeSessionFusionService as Ve}from"./native-fusion/service.js";import{startDaemonLogRetention as Be}from"./daemon-log.js";import{startDaemonIpcServer as Ge}from"./daemon-ipc/server.js";import{resolveManagedRoomIpcPaths as He}from"./daemon-ipc/paths.js";import{RoomSessionRegistry as Fe}from"./room/session-registry.js";import{HttpRoomBindingClient as ze}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Je}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Ke}from"./room/host-session-observations.js";import{RoomMirrorSyncService as je}from"./room/mirror-sync-service.js";import{RoomActivationService as We}from"./room/activation-service.js";import{RoomActivationAudit as Ye}from"./room/activation-audit.js";import{RoomAttachmentCache as qe}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Qe}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Xe}from"./room/managed-context.js";import{createManagedRoomExecutor as Ze}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as eo,SESSION_SYNC_ADVERTISED_CAPABILITIES as oo}from"@shennian/wire";import{getDaemonInstallationId as no}from"./session/store.js";import{synchronizeMachinePersonalAuthority as to}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as ro,ensurePersonalDataEpoch as so}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as io,DeletionSyncTerminalError as ao}from"./session/deletion-sync-state.js";const oe=le(),co=J("daemon.pid");function mo(){try{const o=_.readFileSync(co,"utf-8").trim();if(o.startsWith("{")){const a=JSON.parse(o),m=Number(a.pid);return Number.isInteger(m)&&m>0?m:null}const t=parseInt(o,10);return Number.isInteger(t)&&t>0?t:null}catch{return null}}function lo(o){return o.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function x(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
2
- `),process.exitCode=1}function po(o){const t=[`trigger=${o.trigger??"unknown"}`,`phase=${o.phase}`,`code=${o.code??"unknown"}`,`attempt=${o.reconnectAttempt}`];return o.reason&&t.push(`reason=${o.reason}`),o.error&&t.push(`error=${o.error}`),t.join(" ")}async function ne(o,t=5e3){const a=Date.now();for(;Date.now()-a<t;){try{process.kill(o,0)}catch{return!0}await new Promise(m=>setTimeout(m,100))}try{return process.kill(o,0),!1}catch{return!0}}const r=new ie;r.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(ee),r.enablePositionalOptions(),Te(r),Ae(r),r.option("--api <url>","Server URL override").option("--name <name>","Machine name",ae.hostname()).action(async o=>{const t=d(),a=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??t.serverUrl??void 0;await fe(a,o.name)}),r.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async o=>{const t=J("env.json");try{const e=JSON.parse(_.readFileSync(t,"utf-8"));for(const[s,n]of Object.entries(e))process.env[s]||(process.env[s]=n);Z()}catch{}ge(K(process.env,d()));const a=Be();Y()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(a),process.exit(0));const m=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),L=async(e,s)=>{console.log(`[${new Date().toISOString()}] ${s} (PID ${e})`),process.kill(e,"SIGTERM"),await ne(e)||(process.kill(e,"SIGKILL"),await ne(e,2e3))};try{const e=mo();if(e&&e!==process.pid)try{process.kill(e,0),m?await L(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const b=ye(process.env,oe)?[]:ve(process.pid);if(b.length>0)if(m)for(const e of b)try{await L(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${b[0]}), skipping duplicate start`),process.exit(0);const p=Se();q(process.pid,p,{version:ee}),Q(process.pid,void 0,p),process.on("exit",()=>{j(process.pid,p),W(p)}),await Ne()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));try{so()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const c=d(),u=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??c.serverUrl??M.cn.url,g=X(),O=$e(),S=O.map(e=>e.type),U=_e(O),y=new Fe,R=new Ke,N=new Xe(y),h=new ze(u),re=new qe({actionClient:h}),se=new Qe({actionClient:h});let P=null;const V=Ze({authority:N,actionClient:h,attachmentCache:re,attachmentUploader:se,observations:R,getMachineCredentials:()=>({machineToken:d().machineToken})}),C=await Ge({paths:He(),handlers:{"managed-room.execute":e=>V(e)}});let B=!1;const k=()=>{B||(B=!0,C.close().finally(()=>process.exit(0)))};if(process.once("SIGINT",k),process.once("SIGTERM",k),!c.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await C.close(),process.exitCode=1;return}process.removeListener("SIGINT",k),process.removeListener("SIGTERM",k);const G=`${lo(u)}/relay/machine`,$=c.machineToken;if(!$)return;try{await to({serverUrl:u,machineToken:$,machineId:c.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await C.close(),process.exitCode=1;return}c.machineId&&xe(u,c.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${G}... (v${g}) agents: ${S.join(",")}`),w({level:"info",wsEvent:"daemon.start",metadata:{version:g,agents:S}});let E=null,D=null,T=0;const H=de(c.autoUpgrade),l=new pe({serverUrl:G,machineToken:$,cliVersion:g,agentList:S,autoUpgradePolicy:H,sessionSyncProtocolVersion:eo,sessionSyncCapabilities:oo,daemonInstallationId:no(),dataEpoch:ro,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),w({level:"info",wsEvent:"daemon.connected"}),U.some(n=>n.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:U}}),Me(O,{serverUrl:c.serverUrl??u,authToken:c.machineToken??c.accessToken}).then(n=>{JSON.stringify(n)!==JSON.stringify(U)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:n}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:n,clearUpgradeAttempt:i})=>{const v=n();i(),v&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${v.from} \u2192 ${v.to}`),l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:v.from,to:v.to}}))}).catch(()=>{}),A.start(),D?.start();const e=++T,s=async()=>{try{await f.synchronizeSessionDeletions(),e===T&&l.getState()==="connected"&&E?.handleConnected()}catch(n){if(console.error("[session.deletions] startup catch-up failed",n),e===T&&l.getState()==="connected"){if(n instanceof ao)return;const i=n instanceof io?Math.max(1e3,n.retryAfterMs):5e3;setTimeout(()=>{s()},i).unref?.()}}};s()},onDisconnected:e=>{T+=1,D?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${po(e)}`),w({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),w({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),f.handleReq(e)}});E=Le()?new Ve(l):null,D=new je({registry:y,observations:R,actionClient:h,getMachineCredentials:()=>({machineToken:d().machineToken}),onMessages:async(e,s,n,i)=>{await P?.processMessages(e,s,n,i)}});const A=new Pe({currentVersion:g,machineId:c.machineId??"unknown-machine",serverUrl:c.serverUrl??u,getIdleState:()=>f.getUpgradeIdleState(),startUpgrade:async e=>{const{handleUpgradeStart:s}=await import("./commands/upgrade.js");await s(l,"auto-upgrade",e,{currentVersion:g})}});A.setPolicy(H);const f=new be(l,E,g,A,new Je({registry:y,observations:R,bindingClient:h,getMachineCredentials:()=>{const e=d();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${p}`}),{prepare:()=>Ie(),isDisabled:()=>Y(),shutdownAfterResponse:()=>{setTimeout(()=>{we(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>N.revokeToken(e),V);P=new We({registry:y,observations:R,enqueueConversationBatch:e=>f.enqueueManagedRoomBatch(e),loadContext:async e=>{const s=d().machineToken?.trim(),n=e.binding;if(!s||!n)throw new Error("managed_room_context_unavailable");const i=await h.status({machineToken:s,roomId:n.roomId,bindingId:n.bindingId});return{roomName:i.room.name,memberCount:i.room.memberCount,members:i.room.members,agentsByIdentityId:i.agent?{[i.agentIdentityId]:i.agent}:{},agentSystemPromptsByIdentityId:i.agent?{[i.agentIdentityId]:i.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,s)=>{const n=d().machineToken;if(!n)throw new Error("machine_not_authorized");return N.issue({registryKey:e,machineCredential:n,activationCauseMessageId:s})},audit:new Ye}),f.setManagedRoomTurnPreparer(e=>P.prepareConversationTurn(e)),_.mkdirSync(oe,{recursive:!0}),q(process.pid,p,{version:g}),Q(process.pid,void 0,p),l.connect(),process.stdin.resume();let F=!1;const z=async()=>{if(!F){F=!0,console.log(I.gray(`
3
- Disconnecting...`)),w({level:"info",wsEvent:"daemon.stop"}),clearInterval(a);try{await f.cleanup(),A.stop(),E?.stop(),D?.stop(),l.disconnect(),await C.close(),j(process.pid,p),W(p)}finally{process.exit(0)}}};process.on("SIGINT",()=>{z()}),process.on("SIGTERM",()=>{z()})}),r.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>x()),r.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>x()),r.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>x());const te=r.command("config").description("View or modify configuration");te.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${me()}`);const o=d(),t=ue(o,K(process.env,o));if(o.serverUrl){const a=Ue(o.serverUrl);console.log(`Region: ${a} (${M[a].label})`)}console.log(JSON.stringify(t,null,2))}),te.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((o,t)=>{const a=d();if(o==="server"){t!=="cn"&&t!=="global"&&(console.error(I.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const m=t;a.serverUrl=Oe(m),ce(a),console.log(I.green(`\u2713 Server set to ${M[m].label}`)),console.log(I.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(I.red(`\u2717 Unknown config key: ${o}. Supported: server`)),process.exit(1)}),he(r),Re(r),Ce(r),ke(r),Ee(r),De(r),r.parse();
1
+ import{Command as se}from"commander";import y from"chalk";import ae from"node:os";import $ from"node:fs";import{loadConfig as l,saveConfig as ce,getConfigPath as me,getShennianDir as de,resolveShennianPath as J,normalizeAutoUpgradePolicy as le}from"./config/index.js";import{CliRelayClient as pe}from"./relay/client.js";import{collectKnownLogSecrets as K,installCliLogRedaction as ge,sanitizeCliLogValue as ue}from"./log-redaction.js";import{registerPairCommand as he,runSmartStart as fe}from"./commands/pair.js";import{registerLogoutCommand as Se}from"./commands/logout.js";import{clearDaemonPidIfOwner as j,createDaemonInstanceId as ve,clearDaemonLauncher as W,findRunningDaemonProcessIds as Ie,isRemoteAccessDisabled as Y,prepareRemoteAccessPause as we,stopPreparedRemoteAccessService as ye,isIsolatedDaemonE2eInstance as Re,registerDaemonCommand as Ce,writeDaemonPid as q,writeDaemonLauncher as Q}from"./commands/daemon.js";import{registerRuntimeCommand as ke}from"./commands/runtime.js";import{registerManagerCommand as Ee}from"./commands/manager.js";import{registerUpgradeCommand as De}from"./commands/upgrade.js";import{registerToolsCommand as Te}from"./commands/tools.js";import{registerIntegrationCommand as be}from"./commands/integration.js";import{registerRoomCommand as Ae}from"./commands/room.js";import{SessionManager as Pe}from"./session/manager.js";import{SERVERS as _,regionToUrl as Oe,urlToRegion as Ue}from"./region.js";import{getCurrentVersion as X,handleStartupCrashCheck as Me}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Ne}from"./upgrade/scheduler.js";import{detectAgents as $e}from"./agents/detect.js";import{augmentProcessPath as Z}from"./env-path.js";Z();const ee=X();import{getCachedAgentInfos as _e,resolveAgentInfos as xe}from"./agents/model-registry.js";import{initCliLogReporter as Le,reportLog as R}from"./log-reporter.js";import{isNativeFusionEnabled as Be}from"./native-fusion/config.js";import{NativeSessionFusionService as Ve}from"./native-fusion/service.js";import{startDaemonLogRetention as Ge}from"./daemon-log.js";import{startDaemonIpcServer as He}from"./daemon-ipc/server.js";import{resolveManagedRoomIpcPaths as Fe}from"./daemon-ipc/paths.js";import{RoomSessionRegistry as ze}from"./room/session-registry.js";import{HttpRoomBindingClient as Je}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Ke}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as je}from"./room/host-session-observations.js";import{RoomMirrorSyncService as We}from"./room/mirror-sync-service.js";import{RoomActivationService as Ye}from"./room/activation-service.js";import{RoomActivationAudit as qe}from"./room/activation-audit.js";import{RoomAttachmentCache as Qe}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Xe}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Ze}from"./room/managed-context.js";import{createManagedRoomExecutor as eo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as oo,SESSION_SYNC_ADVERTISED_CAPABILITIES as no}from"@shennian/wire";import{getDaemonInstallationId as to}from"./session/store.js";import{synchronizeMachinePersonalAuthority as ro}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as io,ensurePersonalDataEpoch as so}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as ao,DeletionSyncTerminalError as co}from"./session/deletion-sync-state.js";const oe=de(),mo=J("daemon.pid");function lo(){try{const o=$.readFileSync(mo,"utf-8").trim();if(o.startsWith("{")){const a=JSON.parse(o),m=Number(a.pid);return Number.isInteger(m)&&m>0?m:null}const r=parseInt(o,10);return Number.isInteger(r)&&r>0?r:null}catch{return null}}function po(o){return o.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function x(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
2
+ `),process.exitCode=1}function go(o){const r=[`trigger=${o.trigger??"unknown"}`,`phase=${o.phase}`,`code=${o.code??"unknown"}`,`attempt=${o.reconnectAttempt}`];return o.reason&&r.push(`reason=${o.reason}`),o.error&&r.push(`error=${o.error}`),r.join(" ")}async function ne(o,r=5e3){const a=Date.now();for(;Date.now()-a<r;){try{process.kill(o,0)}catch{return!0}await new Promise(m=>setTimeout(m,100))}try{return process.kill(o,0),!1}catch{return!0}}const s=new se;s.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(ee),s.enablePositionalOptions(),be(s),Ae(s),s.option("--api <url>","Server URL override").option("--name <name>","Machine name",ae.hostname()).action(async o=>{const r=l(),a=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??r.serverUrl??void 0;await fe(a,o.name)}),s.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async o=>{const r=J("env.json");try{const e=JSON.parse($.readFileSync(r,"utf-8"));for(const[i,n]of Object.entries(e))process.env[i]||(process.env[i]=n);Z()}catch{}ge(K(process.env,l()));const a=Ge();Y()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(a),process.exit(0));const m=!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM||process.env.SHENNIAN_DESKTOP_SERVER_URL),L=async(e,i)=>{console.log(`[${new Date().toISOString()}] ${i} (PID ${e})`),process.kill(e,"SIGTERM"),await ne(e)||(process.kill(e,"SIGKILL"),await ne(e,2e3))};try{const e=lo();if(e&&e!==process.pid)try{process.kill(e,0),m?await L(e,"managed start taking over from existing daemon"):(console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),process.exit(0))}catch{}}catch{}const P=Re(process.env,oe)?[]:Ie(process.pid);if(P.length>0)if(m)for(const e of P)try{await L(e,"managed start taking over from orphan daemon")}catch{}else console.log(`[${new Date().toISOString()}] daemon already running (PID ${P[0]}), skipping duplicate start`),process.exit(0);const p=ve();q(process.pid,p,{version:ee}),Q(process.pid,void 0,p),process.on("exit",()=>{j(process.pid,p),W(p)}),await Me()&&(console.log(`[${new Date().toISOString()}] Rolled back to previous version, restarting...`),process.exit(0));try{so()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const c=l(),h=o.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??c.serverUrl??_.cn.url,u=X(),O=$e(),v=O.map(e=>e.type),U=_e(O),f=new ze,C=new je,I=new Ze(f),S=new Je(h),re=new Qe({actionClient:S}),ie=new Xe({actionClient:S});let M=null,g;const B=eo({authority:I,actionClient:S,attachmentCache:re,attachmentUploader:ie,observations:C,getMachineCredentials:()=>({machineToken:l().machineToken}),onPaused:({registryKey:e,sessionId:i})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),I.revokeSession(e),g?.discardPendingManagedRoomMessages(i)}}),k=await He({paths:Fe(),handlers:{"managed-room.execute":e=>B(e)}});let V=!1;const E=()=>{V||(V=!0,k.close().finally(()=>process.exit(0)))};if(process.once("SIGINT",E),process.once("SIGTERM",E),!c.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await k.close(),process.exitCode=1;return}process.removeListener("SIGINT",E),process.removeListener("SIGTERM",E);const G=`${po(h)}/relay/machine`,N=c.machineToken;if(!N)return;try{await ro({serverUrl:h,machineToken:N,machineId:c.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await k.close(),process.exitCode=1;return}c.machineId&&Le(h,c.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${G}... (v${u}) agents: ${v.join(",")}`),R({level:"info",wsEvent:"daemon.start",metadata:{version:u,agents:v}});let D=null,T=null,b=0;const H=le(c.autoUpgrade),d=new pe({serverUrl:G,machineToken:N,cliVersion:u,agentList:v,autoUpgradePolicy:H,sessionSyncProtocolVersion:oo,sessionSyncCapabilities:no,daemonInstallationId:to(),dataEpoch:io,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),R({level:"info",wsEvent:"daemon.connected"}),U.some(n=>n.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:v,agents:U}}),xe(O,{serverUrl:c.serverUrl??h,authToken:c.machineToken??c.accessToken}).then(n=>{JSON.stringify(n)!==JSON.stringify(U)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:v,agents:n}})}).catch(()=>{}),import("./upgrade/engine.js").then(({readUpgradeAttempt:n,clearUpgradeAttempt:t})=>{const w=n();t(),w&&(console.log(`[${new Date().toISOString()}] [upgrade] Reporting success: ${w.from} \u2192 ${w.to}`),d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:w.from,to:w.to}}))}).catch(()=>{}),A.start(),T?.start();const e=++b,i=async()=>{try{await g.synchronizeSessionDeletions(),e===b&&d.getState()==="connected"&&D?.handleConnected()}catch(n){if(console.error("[session.deletions] startup catch-up failed",n),e===b&&d.getState()==="connected"){if(n instanceof co)return;const t=n instanceof ao?Math.max(1e3,n.retryAfterMs):5e3;setTimeout(()=>{i()},t).unref?.()}}};i()},onDisconnected:e=>{b+=1,T?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${go(e)}`),R({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),R({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),g.handleReq(e)}});D=Be()?new Ve(d):null,T=new We({registry:f,observations:C,actionClient:S,getMachineCredentials:()=>({machineToken:l().machineToken}),onMessages:async(e,i,n,t)=>{await M?.processMessages(e,i,n,t)},onBindingPaused:e=>{I.revokeSession(e.registryKey),g?.discardPendingManagedRoomMessages(e.nianSessionId)}});const A=new Ne({currentVersion:u,machineId:c.machineId??"unknown-machine",serverUrl:c.serverUrl??h,getIdleState:()=>g.getUpgradeIdleState(),startUpgrade:async e=>{const{handleUpgradeStart:i}=await import("./commands/upgrade.js");await i(d,"auto-upgrade",e,{currentVersion:u})}});A.setPolicy(H),g=new Pe(d,D,u,A,new Ke({registry:f,observations:C,bindingClient:S,getMachineCredentials:()=>{const e=l();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${p}`}),{prepare:()=>we(),isDisabled:()=>Y(),shutdownAfterResponse:()=>{setTimeout(()=>{ye(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>I.revokeToken(e),B),M=new Ye({registry:f,observations:C,enqueueConversationBatch:e=>g.enqueueManagedRoomBatch(e),loadContext:async e=>{const i=l().machineToken?.trim(),n=e.binding;if(!i||!n)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:i,roomId:n.roomId,bindingId:n.bindingId});return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,i)=>{const n=l().machineToken;if(!n)throw new Error("machine_not_authorized");return I.issue({registryKey:e,machineCredential:n,activationCauseMessageId:i})},audit:new qe}),g.setManagedRoomTurnPreparer(e=>M.prepareConversationTurn(e)),$.mkdirSync(oe,{recursive:!0}),q(process.pid,p,{version:u}),Q(process.pid,void 0,p),d.connect(),process.stdin.resume();let F=!1;const z=async()=>{if(!F){F=!0,console.log(y.gray(`
3
+ Disconnecting...`)),R({level:"info",wsEvent:"daemon.stop"}),clearInterval(a);try{await g.cleanup(),A.stop(),D?.stop(),T?.stop(),d.disconnect(),await k.close(),j(process.pid,p),W(p)}finally{process.exit(0)}}};process.on("SIGINT",()=>{z()}),process.on("SIGTERM",()=>{z()})}),s.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>x()),s.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>x()),s.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>x());const te=s.command("config").description("View or modify configuration");te.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${me()}`);const o=l(),r=ue(o,K(process.env,o));if(o.serverUrl){const a=Ue(o.serverUrl);console.log(`Region: ${a} (${_[a].label})`)}console.log(JSON.stringify(r,null,2))}),te.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((o,r)=>{const a=l();if(o==="server"){r!=="cn"&&r!=="global"&&(console.error(y.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const m=r;a.serverUrl=Oe(m),ce(a),console.log(y.green(`\u2713 Server set to ${_[m].label}`)),console.log(y.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(y.red(`\u2717 Unknown config key: ${o}. Supported: server`)),process.exit(1)}),he(s),Se(s),Ce(s),ke(s),Ee(s),De(s),Te(s),s.parse();
@@ -3,6 +3,7 @@ import { type AgentType, type ExternalChannelSessionStatus, type ReqFrame } from
3
3
  import { ManagerRegistry } from './registry.js';
4
4
  import type { SessionManagerRuntime } from '../session/types.js';
5
5
  import { RetiredChannelRuntime as ChannelRuntime, type ChannelRuntimeOptions, type WeChatAutomationLane, type WeChatRpaSessionContext } from './retired-external-channel-compat.js';
6
+ import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
6
7
  export type LocalReqDispatcher = (req: ReqFrame) => Promise<void>;
7
8
  type ManagerRuntimeServiceOptions = {
8
9
  getRuntime: () => SessionManagerRuntime;
@@ -15,6 +16,7 @@ export declare function setManagerRuntimeService(service: ManagerRuntimeService
15
16
  export declare function getManagerRuntimeService(): ManagerRuntimeService | null;
16
17
  export declare class ManagerRuntimeService {
17
18
  private opts;
19
+ private readonly managedPolicies;
18
20
  readonly registry: ManagerRegistry;
19
21
  readonly channelRuntime: ChannelRuntime;
20
22
  private readonly weChatRpaSessionSync;
@@ -49,6 +51,8 @@ export declare class ManagerRuntimeService {
49
51
  defaultWorkerModelId?: string | null;
50
52
  status?: 'idle' | 'running' | 'interrupting';
51
53
  }): void;
54
+ setManagedAgentPolicy(sessionId: string, policy: CompiledManagedAgentPolicy | null): void;
55
+ getManagedAgentPolicyForWorker(managerSessionId: string, workDir: string): CompiledManagedAgentPolicy | null;
52
56
  setManagerWorkerDefaults(sessionId: string, agentType?: AgentType | null, modelId?: string | null): void;
53
57
  getManagerWorkerDefaults(sessionId: string): {
54
58
  agentType: AgentType | null;
@@ -1,4 +1,4 @@
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,`
1
+ import Y from"node:http";import{randomBytes as V,randomUUID as p}from"node:crypto";import R 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 D}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 I(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
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
@@ -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 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
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;managedPolicies=new Map;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();R.mkdirSync(w.dirname(e),{recursive:!0}),R.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),R.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=O(),n=JSON.parse(R.readFileSync(e,"utf-8"));(n.url===this.ipcUrl||n.token===this.ipcToken)&&R.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:I(t),SHENNIAN_MANAGER_MODEL:r,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:I(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null})}setManagedAgentPolicy(e,n){n?this.managedPolicies.set(e,n):this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,n){const t=this.registry.getManager(e),r=this.managedPolicies.get(e);return!t||!r||I(n)!==t.workDir?null:r}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:I(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:I(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=I(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,s),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,s):await this.dispatchChatSend(d.sessionId,d.agentType,d.workDir,c,d.agentSessionId??null,h,s),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,k=i?.channelId||h||M?.channelId||"",E=i?.conversationId||g||M?.conversationId||"",W=k?this.channelRuntime.getChannelById(k):void 0,J=W&&(!!(i||h)||W.managedBy!=="session-sync");if(k&&J){if(!E)throw new Error("No external channel target is available for this Manager");const y=await this.channelRuntime.reply({managerSessionId:s,channelId:k,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 S;try{S=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;S={ok:!1,error:P}}if(S.ok){u(n,200,{ok:!0,payload:S.payload});return}if(!F(S.error||"")||!k||!E){u(n,400,{ok:!1,error:S.error||"External send failed"});return}u(n,400,{ok:!1,error:`No local external channel is configured for ${k}`});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=I(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,i){await this.opts.dispatchReq({type:"req",id:`manager-send-${p()}`,method:"chat.send",params:{...D(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o,managedWorkerParentSessionId:i}})}async dispatchChatEnqueue(e,n,t,r,s,o,i){await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${p()}`,method:"chat.enqueue",params:{...D(),sessionId:e,text:r,agentType:n,workDir:t,agentSessionId:s,modelId:o,managedWorkerParentSessionId:i}})}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:{...D(),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)"}
@@ -22,4 +22,4 @@ ${n}`:n;await this.dispatchManagerContinuation(e,o)}bindManagerAdapterEvents(e,n
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
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
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};
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"||v(a.localPath)?"":"edge-local-unavailable"}function G(a,e){return a.localPath&&Ne(a,e)&&v(a.localPath)?{path:a.localPath,name:a.name||w.basename(a.localPath)||"attachment",mimeType:a.mimeType||j(a)}:a.url&&De(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)&&v(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&&!ve(a.localPath):!0}function De(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 ve(a){const e=String(a||"").replace(/\\/g,"/"),n=w.basename(e).toLowerCase();return/(^|\/)\.uploads(\/|$)/.test(e)||/(^|-)preview([-.]|$)/i.test(n)}function v(a){try{return R.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};
@@ -1,6 +1,8 @@
1
1
  import type { ParsedNativeEvent } from './types.js';
2
2
  export { listCodexRolloutFiles, lookupCodexThreadName } from './codex-files.js';
3
- export declare function parseCodexRolloutChunk(filePath: string, startOffset: number): {
3
+ type CodexRolloutChunk = {
4
4
  nextOffset: number;
5
5
  events: ParsedNativeEvent[];
6
6
  };
7
+ export declare function parseCodexRolloutChunk(filePath: string, startOffset: number): CodexRolloutChunk;
8
+ export declare function parseCodexRolloutChunkCooperatively(filePath: string, startOffset: number): Promise<CodexRolloutChunk>;
@@ -1,4 +1,4 @@
1
- import j from"node:fs";import R from"node:path";import{createHash as Z}from"node:crypto";import{buildApprovalPendingPayload as P,buildUserMessagePayload as U,isSystemControlPayload as A,isToolPayload as k}from"@shennian/wire";import{buildImagePayload as D,buildToolPayload as K,findJsonlLookbackOffset as O,inferMimeType as H,makeCursor as W,normalizeText as y,parseStructuredString as tt,readJsonlLines as et,readTimestamp as nt,safeParse as q,shouldIgnoreNativeScanPath as L}from"./parser-common.js";import{lookupCodexThreadName as z}from"./codex-files.js";import{parseCodexDelegationInput as rt,parseCodexResponseUserInput as ot,parseCodexTextElements as B,stripCodexUserMessageWrapper as it}from"./codex-user-input.js";import{listCodexRolloutFiles as Mt,lookupCodexThreadName as At}from"./codex-files.js";function N(t){if(typeof t=="string"){const i=t.trim();return i?{path:i,name:R.basename(i)||"image.png",mimeType:H(i),kind:"image"}:null}if(typeof t!="object"||t===null)return null;const n=t,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 e=typeof n.name=="string"&&n.name.trim()?n.name:R.basename(r)||"image.png",o=typeof n.mimeType=="string"&&n.mimeType.trim()?n.mimeType:H(r);return{path:r,name:e,mimeType:o,kind:"image"}}function F(t){const n=y(t);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>"),e=n.includes("# AGENTS.md instructions for ")&&n.includes("<INSTRUCTIONS>")&&n.includes("</INSTRUCTIONS>"),o=n.includes("<environment_context>")&&n.includes("</environment_context>");return e&&o&&(n.startsWith("# AGENTS.md instructions for ")||r)}function w(t){const n=B(t.text_elements),r=Array.isArray(t.input)?B(t.input):"",e=typeof t.message=="string"?it(t.message):{text:"",strippedWrapper:!1},o=e.text,i=n||r||o,s=e.strippedWrapper?"codex_app_context_wrapper":void 0,a=Array.isArray(t.local_images)?t.local_images.map(N).filter(l=>l!=null):[];return!i&&a.length===0||A(i)&&a.length===0||F(i)&&a.length===0?null:a.length>0?{payload:U(i,a),titleText:i,repairHint:s}:{payload:i,titleText:i,repairHint:s}}function ut(t){return t==="turn_aborted"?"aborted":t==="turn_completed"||t==="turn_complete"||t==="task_complete"?"completed":null}function ct(t){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(t)}function st(t){const n=typeof t.command=="string"?t.command:Array.isArray(t.command)?t.command.join(" "):"",r=typeof t.prompt=="string"?t.prompt:typeof t.reason=="string"?t.reason:typeof t.message=="string"?t.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 P({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 at(t,n){return Array.isArray(t)?y(t.map(r=>{if(typeof r=="string")return r;if(typeof r!="object"||r===null)return"";const e=r;return e.type!==n?"":typeof e.text=="string"?e.text:""}).filter(Boolean).join(`
1
+ import j from"node:fs";import $ from"node:path";import{createHash as D}from"node:crypto";import{buildApprovalPendingPayload as K,buildUserMessagePayload as k,isSystemControlPayload as A,isToolPayload as L}from"@shennian/wire";import{buildImagePayload as O,buildToolPayload as ee,findJsonlLookbackOffset as te,inferMimeType as U,makeCursor as H,normalizeText as y,parseStructuredString as ne,readJsonlLines as re,readJsonlLinesCooperatively as oe,readTimestamp as ie,safeParse as W,shouldIgnoreNativeScanPath as q}from"./parser-common.js";import{lookupCodexThreadName as w}from"./codex-files.js";import{parseCodexDelegationInput as ue,parseCodexResponseUserInput as ce,parseCodexTextElements as z,stripCodexUserMessageWrapper as se}from"./codex-user-input.js";import{listCodexRolloutFiles as Ee,lookupCodexThreadName as Re}from"./codex-files.js";function R(e){if(typeof e=="string"){const i=e.trim();return i?{path:i,name:$.basename(i)||"image.png",mimeType:U(i),kind:"image"}:null}if(typeof e!="object"||e===null)return null;const r=e,n=typeof r.path=="string"?r.path:typeof r.file_path=="string"?r.file_path:typeof r.saved_path=="string"?r.saved_path:typeof r.image_path=="string"?r.image_path:"";if(!n.trim())return null;const t=typeof r.name=="string"&&r.name.trim()?r.name:$.basename(n)||"image.png",o=typeof r.mimeType=="string"&&r.mimeType.trim()?r.mimeType:U(n);return{path:n,name:t,mimeType:o,kind:"image"}}function B(e){const r=y(e);if(!r)return!1;if(A(r)||/^<environment_context>[\s\S]*<\/environment_context>$/i.test(r)&&r.includes("<current_date>")&&r.includes("<timezone>")&&r.includes("<workspace_roots>"))return!0;const n=r.startsWith("<recommended_plugins>")&&r.includes("</recommended_plugins>"),t=r.includes("# AGENTS.md instructions for ")&&r.includes("<INSTRUCTIONS>")&&r.includes("</INSTRUCTIONS>"),o=r.includes("<environment_context>")&&r.includes("</environment_context>");return t&&o&&(r.startsWith("# AGENTS.md instructions for ")||n)}function F(e){const r=z(e.text_elements),n=Array.isArray(e.input)?z(e.input):"",t=typeof e.message=="string"?se(e.message):{text:"",strippedWrapper:!1},o=t.text,i=r||n||o,s=t.strippedWrapper?"codex_app_context_wrapper":void 0,a=Array.isArray(e.local_images)?e.local_images.map(R).filter(l=>l!=null):[];return!i&&a.length===0||A(i)&&a.length===0||B(i)&&a.length===0?null:a.length>0?{payload:k(i,a),titleText:i,repairHint:s}:{payload:i,titleText:i,repairHint:s}}function ae(e){return e==="turn_aborted"?"aborted":e==="turn_completed"||e==="turn_complete"||e==="task_complete"?"completed":null}function le(e){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(e)}function me(e){const r=typeof e.command=="string"?e.command:Array.isArray(e.command)?e.command.join(" "):"",n=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:r?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${r}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";return K({title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",content:n,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 fe(e,r){return Array.isArray(e)?y(e.map(n=>{if(typeof n=="string")return n;if(typeof n!="object"||n===null)return"";const t=n;return t.type!==r?"":typeof t.text=="string"?t.text:""}).filter(Boolean).join(`
2
2
 
3
- `)):""}function lt(t){if(!Array.isArray(t))return[];const n=[];for(const r of t){if(typeof r!="object"||r===null)continue;const e=r;if(e.type!=="input_image")continue;const o=N(e);o&&n.push(o)}return n}function J(t){if(t.type!=="message")return null;const n=t.role==="assistant"?"agent":t.role==="user"?"user":null;if(!n)return null;if(n==="agent"){const i=at(t.content,"output_text");return A(i)?null:i?{role:n,payload:i,titleText:i}:null}const r=ot(t.content),e=r.text,o=[...lt(t.content)];for(const i of r.imagePaths){const s=N(i);s&&(o.some(a=>a.path===s.path)||o.push(s))}return!e&&o.length===0||A(e)&&o.length===0||F(e)&&o.length===0?null:{role:n,payload:o.length>0?U(e,o):e,titleText:e,repairHint:r.strippedWrapper?"codex_app_context_wrapper":void 0}}function G(t){if(!t)return"";try{const n=JSON.parse(t);if(typeof n=="object"&&n!==null){const r=n;if(r.type==="user"&&typeof r.content=="string")return y(r.content)}}catch{}return y(t)}function mt(t,n,r,e){return t.agentType!=="codex"||t.role!==n||k(t.payload)||Math.abs(t.ts-e)>300*1e3?!1:G(t.payload)===G(r)}function $(t,n,r,e){for(let o=t.length-1;o>=0;o-=1){const i=t[o];if(i){if(mt(i,n,r,e))return o;if(i.agentType==="codex"&&Math.abs(i.ts-e)>300*1e3)break}}return-1}function v(t,n,r,e,o,i,s,a,l,c,u="agent",m=!1,p){s&&t.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:Q(o,r,e),cursor:W(n,r),role:u,ts:i,payload:s,title:a,modelId:l,workDir:c,terminal:m,...m?{terminalState:e==="turn_aborted"?"aborted":e==="error"?"failed":"completed"}:{},repairHint:p})}function b(t,n,r,e,o,i,s,a,l,c,u,m,p){const T=Q(o,r,e),I=typeof p?.callId=="string"&&p.callId.trim()?p.callId.trim():null,_=I?Z("sha256").update(`${o}\0${I}`).digest("hex").slice(0,32):null,M=_?`tool:${_}`:void 0,f=_?`native:tool:${_}`:`native:${T}`,C=p?.completed&&_?2:1,g=m!==void 0?"completed":"running";t.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:T,cursor:W(n,r),role:"agent",ts:i,payload:K({name:s,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:s,...u!==void 0?{args:u}:{},...m!==void 0?{result:m}:{},status:g}})}function Q(t,n,r){return`codex:${t}:${n}:${r}`}function ft(t,n,r,e,o,i,s,a,l){const c=typeof e.type=="string"?e.type:"";if(c){if(c==="message"){const u=J(e);if(!u||$(t,u.role,u.payload,i)>=0)return;v(t,n,r,`${c}:${u.role}`,o,i,u.payload,s,a,l,u.role,!1,u.repairHint);return}if(c==="function_call"){const u=typeof e.name=="string"?e.name:"function_call";b(t,n,r,c,o,i,u,s,a,l,tt(e.arguments),void 0,{callId:e.call_id});return}if(c==="function_call_output"){const u=rt(e);if(u){v(t,n,r,"delegation:user",o,i,u,s||u.slice(0,80),a,l,"user");return}b(t,n,r,c,o,i,"function_call_output",s,a,l,void 0,e.output,{callId:e.call_id,completed:!0});return}if(c==="custom_tool_call"){const u=typeof e.name=="string"?e.name:"custom_tool_call",m=e.input!==void 0?{input:e.input}:void 0;b(t,n,r,c,o,i,u,s,a,l,m,void 0,{callId:e.call_id});return}if(c==="custom_tool_call_output"){const u=typeof e.name=="string"?e.name:"custom_tool_call_output",m=e.output??e.result??e.content;b(t,n,r,c,o,i,u,s,a,l,void 0,m,{callId:e.call_id,completed:!0});return}if(c==="web_search_call"){const u=typeof e.action=="object"&&e.action!==null?e.action:null;b(t,n,r,c,o,i,"web_search",s,a,l,u??e.query);return}}}function pt(t,n,r,e,o,i,s,a,l){const c=typeof e.type=="string"?e.type:"";if(c){if(c==="user_message"){const u=w(e);if(!u)return;v(t,n,r,c,o,i,u.payload,s,a,l,"user",!1,u.repairHint);return}if(c==="agent_message"){const u=typeof e.message=="string"?y(e.message):"";if(!u||A(u))return;v(t,n,r,c,o,i,u,s,a,l);return}if(c==="turn_aborted"||c==="error"){const u=typeof e.message=="string"?y(e.message):typeof e.error=="string"?y(e.error):"";if(!u)return;v(t,n,r,c,o,i,u,s,a,l,"agent",!0);return}if(ct(c)){v(t,n,r,c,o,i,st(e),s,a,l,"agent");return}if(c==="exec_command_end"){const u=Array.isArray(e.command)?e.command:e.parsed_cmd??e.command,m=e.exit_code??e.exitCode,p=[typeof e.aggregated_output=="string"?e.aggregated_output:"",m!=null?`
4
- (exit ${String(m)})`:""].join("");b(t,n,r,c,o,i,"command_execution",s,a,l,{command:u},p);return}if(c==="patch_apply_end"){b(t,n,r,c,o,i,"apply_patch",s,a,l,e.changes,{success:e.success,stdout:e.stdout,stderr:e.stderr});return}if(c==="mcp_tool_call_end"){const u=typeof e.invocation=="object"&&e.invocation!==null?e.invocation:null,m=typeof u?.tool=="string"?u.tool:"mcp_tool_call",p={server:u?.server,...typeof u?.arguments=="object"&&u.arguments!==null?u.arguments:{}};b(t,n,r,c,o,i,m,s,a,l,p,e.result);return}if(c==="dynamic_tool_call_request"){const u=typeof e.tool=="string"?e.tool:"dynamic_tool_call";b(t,n,r,c,o,i,u,s,a,l,e.arguments,void 0,{callId:e.callId??e.call_id});return}if(c==="dynamic_tool_call_response"){const u=typeof e.tool=="string"?e.tool:"dynamic_tool_call";b(t,n,r,c,o,i,u,s,a,l,void 0,e.result??e.output,{callId:e.callId??e.call_id,completed:!0});return}if(c==="web_search_end"){const u=typeof e.action=="object"&&e.action!==null?e.action:null;b(t,n,r,c,o,i,"web_search",s,a,l,u??e.query,e.query);return}if(c==="image_generation_end"){const u=typeof e.saved_path=="string"?e.saved_path.trim():"";if(!u)return;v(t,n,r,c,o,i,D(u),s,a,l);return}c==="view_image_tool_call"&&b(t,n,r,c,o,i,"view_image",s,a,l,e.path??e.image_path??e)}}function E(t=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:t}}function V(t){if((typeof t.thread_source=="string"?t.thread_source.trim().toLowerCase():"")==="subagent")return!0;const r=typeof t.source=="object"&&t.source!==null?t.source:null;if(r&&typeof r.subagent=="object"&&r.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 gt(t){const n=j.openSync(t,"r");try{const r=[];let e=0,o="";for(;e<1024*1024;){const l=Buffer.alloc(16384),c=j.readSync(n,l,0,l.length,e);if(c<=0)break;const u=l.subarray(0,c);r.push(u),e+=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=q(o);if(!i||i.type!=="session_meta")return E();const s=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!s)return E();if(V(s))return E(!0);const a=typeof s.id=="string"?s.id:"";return{sourceSessionKey:a,workDir:typeof s.cwd=="string"?s.cwd:"",modelId:X(s),title:z(a)??"",skipImport:!1}}finally{j.closeSync(n)}return E()}function X(t){return(typeof t.model=="string"?t.model.trim():"")||null}function yt(t,n){const r=[],e=gt(t);let{sourceSessionKey:o,workDir:i,modelId:s,title:a,skipImport:l}=e,c=e.title;if(l||L(i))return{nextOffset:j.statSync(t).size,events:[]};let u=null;const m=new Set,p=et(t,O(t,n),(T,I)=>{const _=q(T);if(!_)return;const M=typeof _.type=="string"?_.type:"",f=typeof _.payload=="object"&&_.payload!==null?_.payload:null,C=nt(_.timestamp);if(!(!f||!C)){if(M==="session_meta"){if(V(f)){r.length=0,l=!0;return}if(o=typeof f.id=="string"?f.id:o,i=typeof f.cwd=="string"?f.cwd:i,L(i)){r.length=0,l=!0;return}s=X(f)??s;const g=z(o);g&&(c=g,a=g);return}if(!l&&o){if(M==="response_item"){const g=J(f);g?.role==="user"&&g.titleText&&!a&&(a=g.titleText.slice(0,80)),ft(r,t,I,f,o,C,a,s,i);return}if(M==="event_msg"){const g=typeof f.type=="string"?f.type:"",S=ut(g);if(S){let x=!1;for(let h=r.length-1;h>=0;h-=1){const d=r[h];if(d?.agentType==="codex"&&d.role==="agent"&&!k(d.payload)){d.terminal=!0,d.terminalState=S,I>=n&&Number(d.cursor?.slice(t.length+1))<n&&m.add(d.sourceEventKey),x=!0,u=null;break}}if(!x)for(let h=r.length-1;h>=0;h-=1){const d=r[h];if(!(d?.agentType!=="codex"||d.role!=="agent")){d.terminal=!0,d.terminalState=S,I>=n&&Number(d.cursor?.slice(t.length+1))<n&&m.add(d.sourceEventKey),x=!0,u=null;break}}x||(u=S);return}if(g==="user_message"){const x=w(f);if(x?.titleText&&!a&&(a=x.titleText.slice(0,80)),x){const h=$(r,"user",x.payload,C);h>=0&&r.splice(h,1)}}else if(g==="agent_message"){const x=typeof f.message=="string"?y(f.message):"";if(x&&!A(x)){const h=$(r,"agent",x,C);h>=0&&r.splice(h,1)}}const Y=r.length;pt(r,t,I,f,o,C,a,s,i),u&&r.length>Y&&(r[r.length-1].terminal=!0,r[r.length-1].terminalState=u,u=null)}}}});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(t.length+1))>=n)}}export{Mt as listCodexRolloutFiles,At as lookupCodexThreadName,yt as parseCodexRolloutChunk};
3
+ `)):""}function pe(e){if(!Array.isArray(e))return[];const r=[];for(const n of e){if(typeof n!="object"||n===null)continue;const t=n;if(t.type!=="input_image")continue;const o=R(t);o&&r.push(o)}return r}function J(e){if(e.type!=="message")return null;const r=e.role==="assistant"?"agent":e.role==="user"?"user":null;if(!r)return null;if(r==="agent"){const i=fe(e.content,"output_text");return A(i)?null:i?{role:r,payload:i,titleText:i}:null}const n=ce(e.content),t=n.text,o=[...pe(e.content)];for(const i of n.imagePaths){const s=R(i);s&&(o.some(a=>a.path===s.path)||o.push(s))}return!t&&o.length===0||A(t)&&o.length===0||B(t)&&o.length===0?null:{role:r,payload:o.length>0?k(t,o):t,titleText:t,repairHint:n.strippedWrapper?"codex_app_context_wrapper":void 0}}function G(e){if(!e)return"";try{const r=JSON.parse(e);if(typeof r=="object"&&r!==null){const n=r;if(n.type==="user"&&typeof n.content=="string")return y(n.content)}}catch{}return y(e)}function ge(e,r,n,t){return e.agentType!=="codex"||e.role!==r||L(e.payload)||Math.abs(e.ts-t)>300*1e3?!1:G(e.payload)===G(n)}function N(e,r,n,t){for(let o=e.length-1;o>=0;o-=1){const i=e[o];if(i){if(ge(i,r,n,t))return o;if(i.agentType==="codex"&&Math.abs(i.ts-t)>300*1e3)break}}return-1}function I(e,r,n,t,o,i,s,a,l,c,u="agent",m=!1,p){s&&e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:Q(o,n,t),cursor:H(r,n),role:u,ts:i,payload:s,title:a,modelId:l,workDir:c,terminal:m,...m?{terminalState:t==="turn_aborted"?"aborted":t==="error"?"failed":"completed"}:{},repairHint:p})}function C(e,r,n,t,o,i,s,a,l,c,u,m,p){const v=Q(o,n,t),_=typeof p?.callId=="string"&&p.callId.trim()?p.callId.trim():null,x=_?D("sha256").update(`${o}\0${_}`).digest("hex").slice(0,32):null,M=x?`tool:${x}`:void 0,f=x?`native:tool:${x}`:`native:${v}`,T=p?.completed&&x?2:1,g=m!==void 0?"completed":"running";e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:v,cursor:H(r,n),role:"agent",ts:i,payload:ee({name:s,toolCallId:f,detailRevision:T,status:g}),title:a,modelId:l,workDir:c,...M?{canonicalMessageKey:M,canonicalMutation:p?.completed?"upsert":"append"}:{},localToolDetail:{toolCallId:f,detailRevision:T,name:s,...u!==void 0?{args:u}:{},...m!==void 0?{result:m}:{},status:g}})}function Q(e,r,n){return`codex:${e}:${r}:${n}`}function de(e,r,n,t,o,i,s,a,l){const c=typeof t.type=="string"?t.type:"";if(c){if(c==="message"){const u=J(t);if(!u||N(e,u.role,u.payload,i)>=0)return;I(e,r,n,`${c}:${u.role}`,o,i,u.payload,s,a,l,u.role,!1,u.repairHint);return}if(c==="function_call"){const u=typeof t.name=="string"?t.name:"function_call";C(e,r,n,c,o,i,u,s,a,l,ne(t.arguments),void 0,{callId:t.call_id});return}if(c==="function_call_output"){const u=ue(t);if(u){I(e,r,n,"delegation:user",o,i,u,s||u.slice(0,80),a,l,"user");return}C(e,r,n,c,o,i,"function_call_output",s,a,l,void 0,t.output,{callId:t.call_id,completed:!0});return}if(c==="custom_tool_call"){const u=typeof t.name=="string"?t.name:"custom_tool_call",m=t.input!==void 0?{input:t.input}:void 0;C(e,r,n,c,o,i,u,s,a,l,m,void 0,{callId:t.call_id});return}if(c==="custom_tool_call_output"){const u=typeof t.name=="string"?t.name:"custom_tool_call_output",m=t.output??t.result??t.content;C(e,r,n,c,o,i,u,s,a,l,void 0,m,{callId:t.call_id,completed:!0});return}if(c==="web_search_call"){const u=typeof t.action=="object"&&t.action!==null?t.action:null;C(e,r,n,c,o,i,"web_search",s,a,l,u??t.query);return}}}function _e(e,r,n,t,o,i,s,a,l){const c=typeof t.type=="string"?t.type:"";if(c){if(c==="user_message"){const u=F(t);if(!u)return;I(e,r,n,c,o,i,u.payload,s,a,l,"user",!1,u.repairHint);return}if(c==="agent_message"){const u=typeof t.message=="string"?y(t.message):"";if(!u||A(u))return;I(e,r,n,c,o,i,u,s,a,l);return}if(c==="turn_aborted"||c==="error"){const u=typeof t.message=="string"?y(t.message):typeof t.error=="string"?y(t.error):"";if(!u)return;I(e,r,n,c,o,i,u,s,a,l,"agent",!0);return}if(le(c)){I(e,r,n,c,o,i,me(t),s,a,l,"agent");return}if(c==="exec_command_end"){const u=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("");C(e,r,n,c,o,i,"command_execution",s,a,l,{command:u},p);return}if(c==="patch_apply_end"){C(e,r,n,c,o,i,"apply_patch",s,a,l,t.changes,{success:t.success,stdout:t.stdout,stderr:t.stderr});return}if(c==="mcp_tool_call_end"){const u=typeof t.invocation=="object"&&t.invocation!==null?t.invocation:null,m=typeof u?.tool=="string"?u.tool:"mcp_tool_call",p={server:u?.server,...typeof u?.arguments=="object"&&u.arguments!==null?u.arguments:{}};C(e,r,n,c,o,i,m,s,a,l,p,t.result);return}if(c==="dynamic_tool_call_request"){const u=typeof t.tool=="string"?t.tool:"dynamic_tool_call";C(e,r,n,c,o,i,u,s,a,l,t.arguments,void 0,{callId:t.callId??t.call_id});return}if(c==="dynamic_tool_call_response"){const u=typeof t.tool=="string"?t.tool:"dynamic_tool_call";C(e,r,n,c,o,i,u,s,a,l,void 0,t.result??t.output,{callId:t.callId??t.call_id,completed:!0});return}if(c==="web_search_end"){const u=typeof t.action=="object"&&t.action!==null?t.action:null;C(e,r,n,c,o,i,"web_search",s,a,l,u??t.query,t.query);return}if(c==="image_generation_end"){const u=typeof t.saved_path=="string"?t.saved_path.trim():"";if(!u)return;I(e,r,n,c,o,i,O(u),s,a,l);return}c==="view_image_tool_call"&&C(e,r,n,c,o,i,"view_image",s,a,l,t.path??t.image_path??t)}}function E(e=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:e}}function V(e){if((typeof e.thread_source=="string"?e.thread_source.trim().toLowerCase():"")==="subagent")return!0;const n=typeof e.source=="object"&&e.source!==null?e.source:null;if(n&&typeof n.subagent=="object"&&n.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 xe(e){const r=j.openSync(e,"r");try{const n=[];let t=0,o="";for(;t<1024*1024;){const l=Buffer.alloc(16384),c=j.readSync(r,l,0,l.length,t);if(c<=0)break;const u=l.subarray(0,c);n.push(u),t+=c;const m=Buffer.concat(n),p=m.indexOf(10);if(p>=0){o=m.subarray(0,p).toString("utf8");break}}!o&&n.length>0&&(o=Buffer.concat(n).toString("utf8"));const i=W(o);if(!i||i.type!=="session_meta")return E();const s=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!s)return E();if(V(s))return E(!0);const a=typeof s.id=="string"?s.id:"";return{sourceSessionKey:a,workDir:typeof s.cwd=="string"?s.cwd:"",modelId:X(s),title:w(a)??"",skipImport:!1}}finally{j.closeSync(r)}return E()}function X(e){return(typeof e.model=="string"?e.model.trim():"")||null}function Y(e,r){const n=[],t=xe(e);let{sourceSessionKey:o,workDir:i,modelId:s,title:a,skipImport:l}=t,c=t.title;if(l||q(i))return{nextOffset:j.statSync(e).size,events:[]};let u=null;const m=new Set,p=(v,_)=>{const x=W(v);if(!x)return;const M=typeof x.type=="string"?x.type:"",f=typeof x.payload=="object"&&x.payload!==null?x.payload:null,T=ie(x.timestamp);if(!(!f||!T)){if(M==="session_meta"){if(V(f)){n.length=0,l=!0;return}if(o=typeof f.id=="string"?f.id:o,i=typeof f.cwd=="string"?f.cwd:i,q(i)){n.length=0,l=!0;return}s=X(f)??s;const g=w(o);g&&(c=g,a=g);return}if(!l&&o){if(M==="response_item"){const g=J(f);g?.role==="user"&&g.titleText&&!a&&(a=g.titleText.slice(0,80)),de(n,e,_,f,o,T,a,s,i);return}if(M==="event_msg"){const g=typeof f.type=="string"?f.type:"",S=ae(g);if(S){let h=!1;for(let b=n.length-1;b>=0;b-=1){const d=n[b];if(d?.agentType==="codex"&&d.role==="agent"&&!L(d.payload)){d.terminal=!0,d.terminalState=S,_>=r&&Number(d.cursor?.slice(e.length+1))<r&&m.add(d.sourceEventKey),h=!0,u=null;break}}if(!h)for(let b=n.length-1;b>=0;b-=1){const d=n[b];if(!(d?.agentType!=="codex"||d.role!=="agent")){d.terminal=!0,d.terminalState=S,_>=r&&Number(d.cursor?.slice(e.length+1))<r&&m.add(d.sourceEventKey),h=!0,u=null;break}}h||(u=S);return}if(g==="user_message"){const h=F(f);if(h?.titleText&&!a&&(a=h.titleText.slice(0,80)),h){const b=N(n,"user",h.payload,T);b>=0&&n.splice(b,1)}}else if(g==="agent_message"){const h=typeof f.message=="string"?y(f.message):"";if(h&&!A(h)){const b=N(n,"agent",h,T);b>=0&&n.splice(b,1)}}const P=n.length;_e(n,e,_,f,o,T,a,s,i),u&&n.length>P&&(n[n.length-1].terminal=!0,n[n.length-1].terminalState=u,u=null)}}}};return{readOffset:te(e,r),onLine:p,finish:v=>{for(const _ of n)_.titleSource=c&&_.title===c?"agent_native":"first_user_fallback";return{nextOffset:v,events:n.filter(_=>m.has(_.sourceEventKey)||Number(_.cursor?.slice(e.length+1))>=r)}}}}function Z(e){return"nextOffset"in e}function Me(e,r){const n=Y(e,r);return Z(n)?n:n.finish(re(e,n.readOffset,n.onLine))}async function Ae(e,r){const n=Y(e,r);return Z(n)?n:n.finish(await oe(e,n.readOffset,n.onLine))}export{Ee as listCodexRolloutFiles,Re as lookupCodexThreadName,Me as parseCodexRolloutChunk,Ae as parseCodexRolloutChunkCooperatively};
@@ -1,8 +1,13 @@
1
- export declare const MAX_JSONL_SCAN_BYTES_PER_PASS: number;
2
1
  export declare function normalizeText(text: string): string;
3
2
  export declare function readTimestamp(value: unknown): number | null;
4
3
  export declare function safeParse(line: string): Record<string, unknown> | null;
5
4
  export declare function readJsonlLines(filePath: string, startOffset: number, onLine: (line: string, lineOffset: number) => void): number;
5
+ /**
6
+ * Drain the complete JSONL snapshot that exists when the read starts while
7
+ * yielding between disk chunks. The chunk size bounds one synchronous slice;
8
+ * it is deliberately not a transcript or reconciliation limit.
9
+ */
10
+ export declare function readJsonlLinesCooperatively(filePath: string, startOffset: number, onLine: (line: string, lineOffset: number) => void): Promise<number>;
6
11
  export declare function findJsonlLookbackOffset(filePath: string, startOffset: number, lineCount?: number): number;
7
12
  export declare function readClaudeEventCwd(parsed: Record<string, unknown>): string | null;
8
13
  export declare function isClaudeSubagentTranscript(filePath: string): boolean;
@@ -1,4 +1,4 @@
1
- import{createHash as C}from"node:crypto";import l from"node:fs";import b from"node:os";import i from"node:path";const _=64*1024*1024,I=2*1024*1024,E=["/root/.claude-mem/observer-session",i.join(b.tmpdir(),"shennian-agent-e2e")],k=E.map(D);function B(e){return A(e.replace(/\r\n/g,`
2
- `).trim())}const w=/^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;function A(e){if(!e)return"";if(!/(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(e))return e;const n=e.replace(w,"");return n.trim()?n.replace(/[ \t]+\n/g,`
3
- `).replace(/\n+$/g,""):""}function J(e){if(typeof e!="string")return null;const t=Date.parse(e);return Number.isFinite(t)?t:null}function v(e){try{const t=JSON.parse(e);return typeof t=="object"&&t!==null?t:null}catch{return null}}function F(e,t,n){const r=l.statSync(e).size;if(t>=r)return r;const f=l.openSync(e,"r");try{const y=Math.min(r,t+I),a=Buffer.allocUnsafe(262144);let m=t,d=t,g=Buffer.alloc(0),h=!1;for(;m<r;){const S=l.readSync(f,a,0,Math.min(262144,r-m),m);if(S<=0)break;m+=S;let c=a.subarray(0,S);g.length>0&&(c=Buffer.concat([g,c]),g=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),T=d;d+=s-u+1,u=s+1,!h&&x.length>0&&x.length<=_&&n(x.toString("utf8"),T),h=!1}if(u<c.length){const s=c.subarray(u);h||s.length>_?(h=!0,d+=s.length,g=Buffer.alloc(0)):g=Buffer.from(s)}if(m>=y&&d>t&&!h)break}return d}finally{l.closeSync(f)}}function G(e,t,n=64){if(t<=0||n<=0)return 0;const o=l.openSync(e,"r");try{let r=t,f=n;for(;r>0;){const p=Math.min(r,65536);r-=p;const y=Buffer.allocUnsafe(p);l.readSync(o,y,0,p,r);for(let a=p-1;a>=0;a-=1)if(!(y[a]!==10||r+a+1===t)&&(f-=1,f===0))return r+a+1}return 0}finally{l.closeSync(o)}}function U(e){return typeof e.cwd=="string"&&e.cwd.trim()?e.cwd:null}function V(e){return i.basename(i.dirname(e))==="subagents"}function N(e){const t=i.resolve(e);return process.platform==="win32"?t.toLowerCase():t}function z(e,t){const n=N(e),o=N(t);return n===o||n.startsWith(o+i.sep)}function D(e){return i.resolve(e).replace(/\//g,"-")}function H(e){return E.some(t=>z(e,t))}function L(e){return k.includes(e)}function W(e){const t=i.join(b.homedir(),".claude","projects"),n=i.relative(t,e);if(!n||n.startsWith("..")||i.isAbsolute(n))return!1;const[o]=n.split(i.sep);return o?L(o):!1}function X(e,t){return`${e}:${t}`}function Y(e,t,n){return`${C("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 q(e){return e.replace(/\r\n/g,`
4
- `).trim()}function Q(e){if(typeof e!="string")return e;const t=e.trim();if(!t)return"";try{return JSON.parse(t)}catch{return e}}function Z(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 P(e){return e.replace(/[[\]\\]/g,"\\$&")}function M(e,t="Generated image"){return`![${P(t)}](<${e}>)`}function ee(e){const t=i.basename(e)||"generated-image.png";return JSON.stringify({v:1,type:"image",content:M(e,t),attachments:[{path:e,name:t,mimeType:"image/png",kind:"image"}]})}function te(e){const t=i.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{I as MAX_JSONL_SCAN_BYTES_PER_PASS,ee as buildImagePayload,M as buildMarkdownImageRef,Z as buildToolPayload,D as encodeClaudeProjectDir,P as escapeMarkdownLabel,G as findJsonlLookbackOffset,te as inferMimeType,V as isClaudeSubagentTranscript,z as isSameOrChildPath,X as makeCursor,Y as makeEventKey,N as normalizePathForCompare,B as normalizeText,q as normalizeToolText,Q as parseStructuredString,U as readClaudeEventCwd,F as readJsonlLines,J as readTimestamp,v as safeParse,K as safeStringify,L as shouldIgnoreClaudeProjectDir,W as shouldIgnoreClaudeTranscriptPath,H as shouldIgnoreNativeScanPath};
1
+ import{createHash as E}from"node:crypto";import d from"node:fs";import w from"node:os";import c from"node:path";const x=64*1024*1024,S=256*1024,I=["/root/.claude-mem/observer-session",c.join(w.tmpdir(),"shennian-agent-e2e")],N=I.map(O);function J(e){return _(e.replace(/\r\n/g,`
2
+ `).trim())}const T=/^\s*::git-[a-z-]+\{[^\n]*\}\s*$/gm;function _(e){if(!e)return"";if(!/(^|\n)\s*::git-[a-z-]+\{[^\n]*\}\s*(?=\n|$)/.test(e))return e;const n=e.replace(T,"");return n.trim()?n.replace(/[ \t]+\n/g,`
3
+ `).replace(/\n+$/g,""):""}function P(e){if(typeof e!="string")return null;const t=Date.parse(e);return Number.isFinite(t)?t:null}function $(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=d.statSync(e).size;if(t>=i)return i;const g=d.openSync(e,"r");try{const p=Buffer.allocUnsafe(S);let l=t,o=t,u=Buffer.alloc(0),m=!1;for(;l<i;){const h=d.readSync(g,p,0,Math.min(S,i-l),l);if(h<=0)break;l+=h;let a=p.subarray(0,h);u.length>0&&(a=Buffer.concat([u,a]),u=Buffer.alloc(0));let s=0;for(;s<a.length;){const r=a.indexOf(10,s);if(r<0)break;const y=a.subarray(s,r),b=o;o+=r-s+1,s=r+1,!m&&y.length>0&&y.length<=x&&n(y.toString("utf8"),b),m=!1}if(s<a.length){const r=a.subarray(s);m||r.length>x?(m=!0,o+=r.length,u=Buffer.alloc(0)):u=Buffer.from(r)}}return o}finally{d.closeSync(g)}}async function M(e,t,n){const i=(await d.promises.stat(e)).size;if(t>=i)return i;const g=await d.promises.open(e,"r");try{const p=Buffer.allocUnsafe(S);let l=t,o=t,u=Buffer.alloc(0),m=!1;for(;l<i;){const{bytesRead:h}=await g.read(p,0,Math.min(S,i-l),l);if(h<=0)break;l+=h;let a=p.subarray(0,h);u.length>0&&(a=Buffer.concat([u,a]),u=Buffer.alloc(0));let s=0;for(;s<a.length;){const r=a.indexOf(10,s);if(r<0)break;const y=a.subarray(s,r),b=o;o+=r-s+1,s=r+1,!m&&y.length>0&&y.length<=x&&n(y.toString("utf8"),b),m=!1}if(s<a.length){const r=a.subarray(s);m||r.length>x?(m=!0,o+=r.length,u=Buffer.alloc(0)):u=Buffer.from(r)}l<i&&await new Promise(r=>setImmediate(r))}return o}finally{await g.close()}}function U(e,t,n=64){if(t<=0||n<=0)return 0;const f=d.openSync(e,"r");try{let i=t,g=n;for(;i>0;){const p=Math.min(i,65536);i-=p;const l=Buffer.allocUnsafe(p);d.readSync(f,l,0,p,i);for(let o=p-1;o>=0;o-=1)if(!(l[o]!==10||i+o+1===t)&&(g-=1,g===0))return i+o+1}return 0}finally{d.closeSync(f)}}function F(e){return typeof e.cwd=="string"&&e.cwd.trim()?e.cwd:null}function G(e){return c.basename(c.dirname(e))==="subagents"}function C(e){const t=c.resolve(e);return process.platform==="win32"?t.toLowerCase():t}function B(e,t){const n=C(e),f=C(t);return n===f||n.startsWith(f+c.sep)}function O(e){return c.resolve(e).replace(/\//g,"-")}function H(e){return I.some(t=>B(e,t))}function k(e){return N.includes(e)}function V(e){const t=c.join(w.homedir(),".claude","projects"),n=c.relative(t,e);if(!n||n.startsWith("..")||c.isAbsolute(n))return!1;const[f]=n.split(c.sep);return f?k(f):!1}function K(e,t){return`${e}:${t}`}function W(e,t,n){return`${E("sha1").update(e).digest("hex").slice(0,16)}:${t}:${n}`}function Y(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 q(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 D(e){return e.replace(/[[\]\\]/g,"\\$&")}function L(e,t="Generated image"){return`![${D(t)}](<${e}>)`}function Z(e){const t=c.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 ee(e){const t=c.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{Z as buildImagePayload,L as buildMarkdownImageRef,Q as buildToolPayload,O as encodeClaudeProjectDir,D as escapeMarkdownLabel,U as findJsonlLookbackOffset,ee as inferMimeType,G as isClaudeSubagentTranscript,B as isSameOrChildPath,K as makeCursor,W as makeEventKey,C as normalizePathForCompare,J as normalizeText,X as normalizeToolText,q as parseStructuredString,F as readClaudeEventCwd,j as readJsonlLines,M as readJsonlLinesCooperatively,P as readTimestamp,$ as safeParse,Y as safeStringify,k as shouldIgnoreClaudeProjectDir,V as shouldIgnoreClaudeTranscriptPath,H as shouldIgnoreNativeScanPath};
@@ -1,11 +1,13 @@
1
1
  import type { ParsedNativeEvent } from './types.js';
2
- export { listCodexRolloutFiles, lookupCodexThreadName, parseCodexRolloutChunk, } from './codex-parser.js';
2
+ export { listCodexRolloutFiles, lookupCodexThreadName, parseCodexRolloutChunk, parseCodexRolloutChunkCooperatively, } 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
5
  export declare function listNativeTranscriptRoots(): string[];
6
6
  export declare function listClaudeTranscriptFiles(): string[];
7
7
  export declare function lookupClaudeTranscriptCwd(sourceSessionKey: string): string | null;
8
- export declare function parseClaudeTranscriptChunk(filePath: string, startOffset: number): {
8
+ type ClaudeTranscriptChunk = {
9
9
  nextOffset: number;
10
10
  events: ParsedNativeEvent[];
11
11
  };
12
+ export declare function parseClaudeTranscriptChunk(filePath: string, startOffset: number): ClaudeTranscriptChunk;
13
+ export declare function parseClaudeTranscriptChunkCooperatively(filePath: string, startOffset: number): Promise<ClaudeTranscriptChunk>;