shennian 0.3.38 → 0.3.39

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.
@@ -196,8 +196,8 @@
196
196
  },
197
197
  {
198
198
  "file": "src/commands/room.js",
199
- "beforeBytes": 14862,
200
- "afterBytes": 8665
199
+ "beforeBytes": 15343,
200
+ "afterBytes": 8914
201
201
  },
202
202
  {
203
203
  "file": "src/commands/runtime.js",
@@ -406,8 +406,8 @@
406
406
  },
407
407
  {
408
408
  "file": "src/room/managed-prompt-composer.js",
409
- "beforeBytes": 4423,
410
- "afterBytes": 4204
409
+ "beforeBytes": 4431,
410
+ "afterBytes": 4212
411
411
  },
412
412
  {
413
413
  "file": "src/room/managed-room-binding.js",
@@ -471,8 +471,8 @@
471
471
  },
472
472
  {
473
473
  "file": "src/session/handlers/chat.js",
474
- "beforeBytes": 31135,
475
- "afterBytes": 13184
474
+ "beforeBytes": 31111,
475
+ "afterBytes": 13158
476
476
  },
477
477
  {
478
478
  "file": "src/session/handlers/control.js",
@@ -1,3 +1,3 @@
1
- import g from"node:crypto";import x from"node:fs";import R from"node:path";import{PublicAttachmentCache as L}from"../room/public-attachment-cache.js";import{PublicRoomClient as j,PublicRoomClientError as U}from"../room/public-room-client.js";import{callDaemonIpc as z}from"../daemon-ipc/client.js";import{DaemonIpcError as q}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_CONTEXT_ENV as M}from"../room/managed-context.js";const $={write:t=>{process.stdout.write(t)},fail:()=>{process.exitCode=1}};function G(t,e={}){const a=e.client??new j,r=e.cache??new L({client:a}),s=e.io??$,d=t.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 o=>p("room.create",s,async()=>{b("create");const n=o.name.trim();return(!n||n.length>80)&&i("invalid_room_name","Room name must contain 1 to 80 characters."),a.create(n)})),d.command("join <invite-url>").description("Join or request access through an official Room invitation").action(async o=>p("room.join",s,async()=>(b("join"),a.join(k(o))))),d.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async o=>p("room.status",s,()=>{const n=h();return n?(o&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(n,"status",{})):a.status(w(o))})),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(o,n)=>{await p("room.read",s,async()=>{n.before&&n.after&&i("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),n.before&&!S(n.before)&&i("invalid_sequence","--before sequence is invalid."),n.after&&!S(n.after)&&i("invalid_sequence","--after sequence is invalid.");const c=Number(n.limit);(!Number.isInteger(c)||c<1||c>50)&&i("invalid_limit","--limit must be between 1 and 50.");const m=h();if(m)return o&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(m,"read",{...n.before?{before:n.before}:{},...n.after?{after:n.after}:{},limit:c});const l=w(o),f=await a.read(l,{before:n.before,after:n.after,limit:c});return N(f,l,r)})}),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",D,[]).action(async(o,n)=>{await p("room.send",s,async()=>{const c=n.text?.trim()??"";!c&&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 m=h();if(m)return o&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(m,"send",{clientMessageId:`managed:${g.randomUUID()}`,text:c,files:n.file});const l=w(o),f=[];for(const I of n.file)f.push(await C(a,l,I));return a.send(l,{clientMessageId:`cli:${g.randomUUID()}`,...c?{text:c}:{},...f.length>0?{attachmentIds:f}:{}})})}),d.command("attachment").description("Download Room attachments through Shennian").command("get [room-ref] [attachment-id]").description("Download and verify one Room attachment").action(async(o,n)=>{await p("room.attachment.get",s,async()=>{const c=h(),m=c?o:n;return(!m||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(m))&&i("invalid_attachment_ref","attachment-id is invalid."),c?y(c,"attachment.get",{attachmentId:m}):r.materialize(w(o),m)})}),d.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async o=>p("room.leave",s,()=>(b("leave"),a.leave(A(o))))),d.command("list",{hidden:!0}).action(()=>u(s,"command_removed","Room listing is not available to Agents.",null)),d.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>u(s,"command_removed","Room search is not available to Agents.",null)),d.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>u(s,"command_removed","Managed Agents are created in the Shennian App or Web interface.",null))}async function p(t,e,a){try{const r=await a();e.write(`${JSON.stringify({ok:!0,command:t,result:r})}
2
- `)}catch(r){if(r instanceof U){u(e,r.code,r.message,r.nextAction);return}if(r instanceof q){u(e,r.code,r.message,null);return}u(e,"room_command_failed","Shennian Room command failed.",null)}}function h(){return process.env[M]?.trim()||null}async function y(t,e,a){return z({method:"managed-room.execute",params:{context:t,action:e,...a},timeoutMs:e==="send"||e==="attachment.get"?18e4:15e3})}function b(t){h()&&i("managed_room_action_forbidden",`${t} is not available in managed Room context.`)}function w(t){return t||i("room_ref_required","Use an official Room URL returned by Shennian."),A(t)}function A(t){let e;try{e=new URL(t)}catch{i("invalid_room_ref","Use an official Room URL returned by Shennian.")}const a=e.pathname.match(/^\/spaces\/([A-Za-z0-9_-]{1,191})$/);return(e.protocol!=="https:"||e.hostname!=="app.shennian.net"&&e.hostname!=="shennian.net"||e.port||e.username||e.password||e.search||e.hash||!a)&&i("invalid_room_ref","Use an official Room URL returned by Shennian."),a[1]}function k(t){let e;try{e=new URL(t)}catch{i("invalid_invite_url","Use an official Shennian Room invitation URL.")}const a=e.pathname.match(/^\/spaces\/join\/([A-Za-z0-9_-]{32,256})$/);return(e.protocol!=="https:"||e.hostname!=="shennian.net"||e.port||e.username||e.password||e.search||e.hash||!a)&&i("invalid_invite_url","Use an official Shennian Room invitation URL."),a[1]}function S(t){return/^(0|[1-9]\d{0,19})$/.test(t)}function D(t,e){return[...e,t]}async function C(t,e,a){const r=R.resolve(a);let s;try{s=await x.promises.lstat(r)}catch{i("file_not_found",`File does not exist: ${a}`)}(!s.isFile()||s.isSymbolicLink())&&i("invalid_file",`Not a regular file: ${a}`),(s.size<1||s.size>100*1024*1024)&&i("invalid_file_size","Each file must be between 1 byte and 100 MB.");const d=await x.promises.readFile(r),v=g.createHash("sha256").update(d).digest("hex"),o=O(r),n=await t.initializeAttachment(e,{clientUploadId:`cli:${g.randomUUID()}`,name:R.basename(r),mediaKind:o.startsWith("image/")?"image":o.startsWith("audio/")?"audio":"file",mimeType:o,byteSize:s.size,sha256:v}),c=_(n.attachment,"invalid_attachment_response"),m=_(n.upload,"invalid_attachment_response");(typeof c.id!="string"||typeof m.url!="string"||m.method!=="PUT")&&i("invalid_attachment_response","Shennian returned an invalid attachment response.");const l=_(m.headers,"invalid_attachment_response");return Object.values(l).some(f=>typeof f!="string")&&i("invalid_attachment_response","Shennian returned invalid upload headers."),T(m.url),await t.upload(m.url,l,d),await t.completeAttachment(e,c.id),c.id}function O(t){const e=R.extname(t).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"}[e]??"application/octet-stream"}function N(t,e,a){return Array.isArray(t.messages)||i("invalid_server_response","Shennian returned invalid messages."),{messages:t.messages.map(r=>{const s=_(r,"invalid_server_response"),d=Array.isArray(s.attachments)?s.attachments:[];return{...s,attachments:d.map(v=>{const o=_(v,"invalid_server_response");return(typeof o.attachmentId!="string"||typeof o.name!="string"||typeof o.mimeType!="string"||typeof o.byteSize!="string"||typeof o.sha256!="string")&&i("invalid_server_response","Shennian returned invalid attachment metadata."),{...o,...a.lookup(e,o)}})}})}}function _(t,e){return(!t||typeof t!="object"||Array.isArray(t))&&i(e,"Invalid structured response."),t}function T(t){let e;try{e=new URL(t)}catch{i("invalid_attachment_response","Invalid upload URL.")}const a=e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1");(e.protocol!=="https:"&&!a||e.username||e.password||e.hash)&&i("invalid_attachment_response","Invalid upload URL.")}function i(t,e){throw new U(t,e)}function u(t,e,a,r){t.write(`${JSON.stringify({ok:!1,error:e,message:a,nextAction:r})}
3
- `),t.fail()}export{G as registerRoomCommand};
1
+ import g from"node:crypto";import x from"node:fs";import R from"node:path";import{PublicAttachmentCache as L}from"../room/public-attachment-cache.js";import{PublicRoomClient as j,PublicRoomClientError as U}from"../room/public-room-client.js";import{callDaemonIpc as z}from"../daemon-ipc/client.js";import{DaemonIpcError as q}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_CONTEXT_ENV as D}from"../room/managed-context.js";const M="https://shennian.net/install.md",$={write:t=>{process.stdout.write(t)},fail:()=>{process.exitCode=1}};function K(t,e={}){const a=e.client??new j,r=e.cache??new L({client:a}),s=e.io??$,d=t.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 o=>p("room.create",s,async()=>{b("create");const n=o.name.trim();(!n||n.length>80)&&i("invalid_room_name","Room name must contain 1 to 80 characters.");const c=await a.create(n),m=u(c.room,"invalid_room_response");return typeof m.inviteUrl!="string"&&i("invalid_room_response","Shennian returned an invalid Room invitation."),{...c,sharing:{inviteUrl:m.inviteUrl,installGuideUrl:M}}})),d.command("join <invite-url>").description("Join or request access through an official Room invitation").action(async o=>p("room.join",s,async()=>(b("join"),a.join(k(o))))),d.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async o=>p("room.status",s,()=>{const n=_();return n?(o&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(n,"status",{})):a.status(w(o))})),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(o,n)=>{await p("room.read",s,async()=>{n.before&&n.after&&i("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),n.before&&!S(n.before)&&i("invalid_sequence","--before sequence is invalid."),n.after&&!S(n.after)&&i("invalid_sequence","--after sequence is invalid.");const c=Number(n.limit);(!Number.isInteger(c)||c<1||c>50)&&i("invalid_limit","--limit must be between 1 and 50.");const m=_();if(m)return o&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(m,"read",{...n.before?{before:n.before}:{},...n.after?{after:n.after}:{},limit:c});const l=w(o),f=await a.read(l,{before:n.before,after:n.after,limit:c});return T(f,l,r)})}),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",C,[]).action(async(o,n)=>{await p("room.send",s,async()=>{const c=n.text?.trim()??"";!c&&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 m=_();if(m)return o&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),y(m,"send",{clientMessageId:`managed:${g.randomUUID()}`,text:c,files:n.file});const l=w(o),f=[];for(const I of n.file)f.push(await N(a,l,I));return a.send(l,{clientMessageId:`cli:${g.randomUUID()}`,...c?{text:c}:{},...f.length>0?{attachmentIds:f}:{}})})}),d.command("attachment").description("Download Room attachments through Shennian").command("get [room-ref] [attachment-id]").description("Download and verify one Room attachment").action(async(o,n)=>{await p("room.attachment.get",s,async()=>{const c=_(),m=c?o:n;return(!m||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(m))&&i("invalid_attachment_ref","attachment-id is invalid."),c?y(c,"attachment.get",{attachmentId:m}):r.materialize(w(o),m)})}),d.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async o=>p("room.leave",s,()=>(b("leave"),a.leave(A(o))))),d.command("list",{hidden:!0}).action(()=>h(s,"command_removed","Room listing is not available to Agents.",null)),d.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>h(s,"command_removed","Room search is not available to Agents.",null)),d.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>h(s,"command_removed","Managed Agents are created in the Shennian App or Web interface.",null))}async function p(t,e,a){try{const r=await a();e.write(`${JSON.stringify({ok:!0,command:t,result:r})}
2
+ `)}catch(r){if(r instanceof U){h(e,r.code,r.message,r.nextAction);return}if(r instanceof q){h(e,r.code,r.message,null);return}h(e,"room_command_failed","Shennian Room command failed.",null)}}function _(){return process.env[D]?.trim()||null}async function y(t,e,a){return z({method:"managed-room.execute",params:{context:t,action:e,...a},timeoutMs:e==="send"||e==="attachment.get"?18e4:15e3})}function b(t){_()&&i("managed_room_action_forbidden",`${t} is not available in managed Room context.`)}function w(t){return t||i("room_ref_required","Use an official Room URL returned by Shennian."),A(t)}function A(t){let e;try{e=new URL(t)}catch{i("invalid_room_ref","Use an official Room URL returned by Shennian.")}const a=e.pathname.match(/^\/spaces\/([A-Za-z0-9_-]{1,191})$/);return(e.protocol!=="https:"||e.hostname!=="app.shennian.net"&&e.hostname!=="shennian.net"||e.port||e.username||e.password||e.search||e.hash||!a)&&i("invalid_room_ref","Use an official Room URL returned by Shennian."),a[1]}function k(t){let e;try{e=new URL(t)}catch{i("invalid_invite_url","Use an official Shennian Room invitation URL.")}const a=e.pathname.match(/^\/spaces\/join\/([A-Za-z0-9_-]{32,256})$/);return(e.protocol!=="https:"||e.hostname!=="shennian.net"||e.port||e.username||e.password||e.search||e.hash||!a)&&i("invalid_invite_url","Use an official Shennian Room invitation URL."),a[1]}function S(t){return/^(0|[1-9]\d{0,19})$/.test(t)}function C(t,e){return[...e,t]}async function N(t,e,a){const r=R.resolve(a);let s;try{s=await x.promises.lstat(r)}catch{i("file_not_found",`File does not exist: ${a}`)}(!s.isFile()||s.isSymbolicLink())&&i("invalid_file",`Not a regular file: ${a}`),(s.size<1||s.size>100*1024*1024)&&i("invalid_file_size","Each file must be between 1 byte and 100 MB.");const d=await x.promises.readFile(r),v=g.createHash("sha256").update(d).digest("hex"),o=O(r),n=await t.initializeAttachment(e,{clientUploadId:`cli:${g.randomUUID()}`,name:R.basename(r),mediaKind:o.startsWith("image/")?"image":o.startsWith("audio/")?"audio":"file",mimeType:o,byteSize:s.size,sha256:v}),c=u(n.attachment,"invalid_attachment_response"),m=u(n.upload,"invalid_attachment_response");(typeof c.id!="string"||typeof m.url!="string"||m.method!=="PUT")&&i("invalid_attachment_response","Shennian returned an invalid attachment response.");const l=u(m.headers,"invalid_attachment_response");return Object.values(l).some(f=>typeof f!="string")&&i("invalid_attachment_response","Shennian returned invalid upload headers."),E(m.url),await t.upload(m.url,l,d),await t.completeAttachment(e,c.id),c.id}function O(t){const e=R.extname(t).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"}[e]??"application/octet-stream"}function T(t,e,a){return Array.isArray(t.messages)||i("invalid_server_response","Shennian returned invalid messages."),{messages:t.messages.map(r=>{const s=u(r,"invalid_server_response"),d=Array.isArray(s.attachments)?s.attachments:[];return{...s,attachments:d.map(v=>{const o=u(v,"invalid_server_response");return(typeof o.attachmentId!="string"||typeof o.name!="string"||typeof o.mimeType!="string"||typeof o.byteSize!="string"||typeof o.sha256!="string")&&i("invalid_server_response","Shennian returned invalid attachment metadata."),{...o,...a.lookup(e,o)}})}})}}function u(t,e){return(!t||typeof t!="object"||Array.isArray(t))&&i(e,"Invalid structured response."),t}function E(t){let e;try{e=new URL(t)}catch{i("invalid_attachment_response","Invalid upload URL.")}const a=e.protocol==="http:"&&(e.hostname==="localhost"||e.hostname==="127.0.0.1");(e.protocol!=="https:"&&!a||e.username||e.password||e.hash)&&i("invalid_attachment_response","Invalid upload URL.")}function i(t,e){throw new U(t,e)}function h(t,e,a,r){t.write(`${JSON.stringify({ok:!1,error:e,message:a,nextAction:r})}
3
+ `),t.fail()}export{K as registerRoomCommand};
@@ -1,4 +1,4 @@
1
- import $ from"node:crypto";const y="managed-room-cli-v1",d=["shennian room status","shennian room read [--before <sequence> | --after <sequence>] [--limit <1-50>]","shennian room send [--text <\u5185\u5BB9>] [--file <\u672C\u5730\u6587\u4EF6\u8DEF\u5F84> ...]","shennian room attachment get <attachment-id>"];function w(n){const t=e(n.agent.displayName,191,"\u5F53\u524D\u667A\u80FD\u4F53"),r=e(n.agent.description??"",1e3,"\u672A\u8BBE\u7F6E")||"\u672A\u8BBE\u7F6E",c=e(n.room.name,191,"\u5F53\u524D\u7FA4\u804A"),s=(n.room.members??[]).slice(0,100).map(o=>{const g=e(o.displayName,191,"\u7FA4\u6210\u5458"),h=o.principalType==="agent"?"\u667A\u80FD\u4F53":"\u771F\u4EBA",u=o.principalType==="agent"&&o.description?`\uFF5C${e(o.description,1e3,"")}`:"",f=o.principalType==="agent"&&o.ownerDisplayName?`\uFF5C\u6240\u5C5E\u6210\u5458\uFF1A${e(o.ownerDisplayName,191,"")}`:"";return`- ${g}\uFF5C${h}${u}${f}`}),l=[`[\u667A\u80FD\u4F53\u8EAB\u4EFD]
1
+ import $ from"node:crypto";const y="managed-room-cli-v1",d=["shennian room status","shennian room read [--before <sequence> | --after <sequence>] [--limit <1-50>]","shennian room send [--text <\u5185\u5BB9>] [--file <\u672C\u5730\u6587\u4EF6\u8DEF\u5F84> ...]","shennian room attachment get <attachment-id>"];function w(n){const t=e(n.agent.displayName,191,"\u5F53\u524D\u667A\u80FD\u4F53"),r=e(n.agent.description??"",1e3,"\u672A\u8BBE\u7F6E")||"\u672A\u8BBE\u7F6E",c=e(n.room.name,191,"\u5F53\u524D\u7FA4\u804A"),s=(n.room.members??[]).slice(0,100).map(o=>{const g=e(o.displayName,191,"\u7FA4\u6210\u5458"),u=o.principalType==="agent"?"\u667A\u80FD\u4F53":"\u771F\u4EBA",h=o.principalType==="agent"&&o.description?`\uFF5C${e(o.description,1e3,"")}`:"",f=o.principalType==="agent"&&o.ownerDisplayName?`\uFF5C\u6240\u5C5E\u6210\u5458\uFF1A${e(o.ownerDisplayName,191,"")}`:"";return`- ${g}\uFF5C${u}${h}${f}`}),l=[`[\u667A\u80FD\u4F53\u8EAB\u4EFD]
2
2
  \u540D\u79F0\uFF1A${t}
3
3
  \u516C\u5F00\u804C\u8D23\uFF1A${r}`,`[\u5F53\u524D\u7FA4\u804A]
4
4
  \u7FA4\u804A\uFF1A${c}
@@ -21,4 +21,4 @@ ${d.join(`
21
21
  `)}
22
22
  --file \u53EF\u91CD\u590D\uFF0C\u4E00\u6761\u6D88\u606F\u6700\u591A 10 \u4E2A\u6587\u4EF6\uFF1B\u6B63\u6587\u548C\u6587\u4EF6\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u3002\u63D0\u53CA\u6210\u5458\u65F6\u5728\u6B63\u6587\u4E2D\u4F7F\u7528 @\u552F\u4E00\u6635\u79F0\u3002\u4E0D\u8981\u4F20\u5165\u6216\u67E5\u627E\u5185\u90E8 ID\u3002`].filter(Boolean).join(`
23
23
 
24
- `);if(!p.trim()||!t||!c)throw new Error("managed_room_prompt_incomplete");return{systemPrompt:p,snapshot:{providerBaseVersion:"provider-owned",managedRoomPolicyVersion:y,sessionModeInstructionsHash:i?m(i):null,agentSystemPromptHash:a?m(a):null,trustedRoomContextHash:m(l),allowedRoomCommands:d}}}function e(n,t,r){return(String(n).replace(/[\u0000-\u001f\u007f]/g," ").replace(/\s+/g," ").trim().slice(0,t)||r).replace(/[<>\[\]]/g,s=>`\\${s}`)}function M(n){return n==="auto_follow"?"\u81EA\u52A8\u53C2\u4E0E":n==="mention_only"?"\u4EC5\u88AB\u63D0\u53CA\u65F6\u53C2\u4E0E":"\u6682\u505C"}function m(n){return $.createHash("sha256").update(n,"utf8").digest("hex")}export{y as MANAGED_ROOM_POLICY_VERSION,w as composeManagedRoomPrompt};
24
+ `);if(!p.trim()||!t||!c)throw new Error("managed_room_prompt_incomplete");return{systemPrompt:p,snapshot:{providerBaseVersion:"provider-owned",managedRoomPolicyVersion:y,sessionModeInstructionsHash:i?m(i):null,agentSystemPromptHash:a?m(a):null,trustedRoomContextHash:m(l),allowedRoomCommands:d}}}function e(n,t,r){return(String(n).replace(/[\u0000-\u001f\u007f]/g," ").replace(/\s+/g," ").trim().slice(0,t)||r).replace(/[<>\u005b\u005d]/g,s=>`\\${s}`)}function M(n){return n==="auto_follow"?"\u81EA\u52A8\u53C2\u4E0E":n==="mention_only"?"\u4EC5\u88AB\u63D0\u53CA\u65F6\u53C2\u4E0E":"\u6682\u505C"}function m(n){return $.createHash("sha256").update(n,"utf8").digest("hex")}export{y as MANAGED_ROOM_POLICY_VERSION,w as composeManagedRoomPrompt};
@@ -1,2 +1,2 @@
1
1
  import v from"node:os";import{createAgent as X}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Y,buildUserMessagePayload as Z}from"@shennian/wire";import{ManagerModeAdapter as ee}from"../../agents/manager.js";import{reportLog as w}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as te}from"../../native-fusion/parsers.js";import{appendMessage as E,recordSession as F}from"../store.js";import{mergeProjectedSessions as ne}from"../projection.js";import{buildManagedAgentEnv as ae}from"../../agents/config-status.js";import{materializeRemoteChatAttachments as re}from"../remote-attachments.js";import{buildToolArgsSummary as se}from"../tool-summary.js";function oe(e){if(!Array.isArray(e))return;const t=e.map(a=>{if(!a||typeof a!="object")return null;const r=a,o=typeof r.path=="string"?r.path:"",l=typeof r.name=="string"?r.name:"",g=typeof r.mimeType=="string"?r.mimeType:"";if(!o||!l||!g)return null;const f=typeof r.previewData=="string"&&r.previewData.trim()?r.previewData.trim():void 0;return{path:o,name:l,mimeType:g,kind:g.startsWith("image/")?"image":"file",...f?{previewData:f}:{}}}).filter(a=>a!=null);return t.length?t:void 0}function de(e){const t=e.indexOf(`
2
- `),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ie(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const r={runId:e.runId,sourceSeq:e.seq},o=se(e.args);return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:r,...e.name?{name:e.name}:{},...o!==void 0?{argsSummary:o}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const le=3e4;function ue(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function ce(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function L(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function H(e,t,a){e.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:t.sessionId,message:t,session:{id:t.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function ge(e,t){F({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function U(e){return ae(e)}function j(e,t,a){return{}}function B(e,t,a,r,o,l){e.configure?.({sessionId:t,externalChannel:r??null,env:{...U(a),...j(t,r,o),...l?{SHENNIAN_MANAGED_ROOM_CONTEXT:l}:{}}})}function fe(e,t,a){if(e!=="claude"||!a)return t;const r=t.trim();return!!r&&r.startsWith("-")&&!r.includes("/")?te(a)??t:t}function pe(e,t,a,r){let o=null;function l(n,s={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ie(n,t,s),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function f(n){if(!n.currentRunId||!n.currentRunPhase)return;const s=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:s,runPhase:n.currentRunPhase},seq:s,id:`agent-heartbeat-${n.currentRunId}-${s}-${Date.now()}`})}function y(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{f(n)},le),n.heartbeatTimer.unref?.())}function i(n){const s=n?.pendingTextEvent;!n||!s||!s.text||(l({state:"delta",runId:s.runId,seq:s.seq,text:s.text,thinking:s.thinking||void 0}),n.pendingTextEvent=null)}r.on("agentEvent",n=>{const s=e.sessions.get(t),T=ue(n),I=n.state==="final"||n.state==="error"||n.state==="aborted";if(s&&(s.nextEventSeq=n.seq+1,I||(s.currentRunId=n.runId),!I&&T&&(s.currentRunPhase=T,y(s)),n.agentSessionId&&(s.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&w({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?E(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?E(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?E(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:Y(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&E(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message});const h=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(h,(e.runTextAcc.get(h)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,L(a,n.source),n.agentSessionId),s&&F({sessionId:t,agentType:a,workDir:s.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const S=n.text??"";if(!S)return;s?.pendingTextEvent&&s.pendingTextEvent.runId!==n.runId&&i(s),s&&!s.pendingTextEvent&&(s.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),s?.pendingTextEvent&&(s.pendingTextEvent.text+=S,s.pendingTextEvent.seq=n.seq);return}let x={};if(n.state==="final"){i(s);const S=e.runTextAcc.get(h)??"";S&&(x={messageSummary:de(S)}),e.runTextAcc.delete(h)}else n.state==="error"||n.state==="aborted"?(i(s),e.runTextAcc.delete(h)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&i(s);I&&s?.currentRunId===n.runId&&(s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,g(s),e.chatQueue?.noteTerminal(t)),l(n,x)}),r.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function me(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function C(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function J(e,t,a,r,o,l,g){return g?e.send(t,a,r,o,l,g):l?.trim()?e.send(t,a,r,o,l):o?.length?e.send(t,a,r,o):r?e.send(t,a,r):e.send(t,a)}async function he(e,t,a,r,o,l,g,f){e.evictIdleSessions();const y=X(a);if(!y)throw new Error(`Unsupported agent: ${a}`);const i=r==="manager"&&a!=="manager"?new ee(y,a==="claude"?"claude":"codex"):y;B(i,t,a,g,f),await i.start(t,o,l);const n={adapter:i,workDir:o,agentType:a,sessionMode:r,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:g??null,externalReplyTarget:f??null,externalChannelEnv:{...U(a),...j(t,g,f)}};return e.sessions.set(t,n),pe(e,t,a,i),n}function ye(e,t){const a=e.sessions.get(t),r=a?.currentRunId;if(!a||!r)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${r}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:r,seq:o},seq:o,id:`agent-evt-${r}-${o}`}),e.chatQueue?.noteTerminal(t)}async function Me(e,t,a){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}me(e,t.id);const{sessionId:r,text:o,agentType:l,sessionMode:g,managerConfig:f,workDir:y,agentSessionId:i,modelId:n,systemPrompt:s,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:I,reasoningEffort:h,clientMessageId:x,sessionListProjection:S,waitForDispatch:K,responseId:Q,roomContextRegistryKey:Ie,managedRoomContextToken:$,suppressExternalOwnerEcho:V}=t.params,k=Q||t.id;ne(S);const b=null,P="";if(!r||!o){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:"sessionId and text are required"});return}const c=l,R=c==="manager"||g==="manager"?"manager":"standard";R==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,T??null,I??null);const q=(c==="claude"||c==="codex"||c==="workbuddy")&&typeof h=="string"&&h.trim()?h.trim():void 0,A=e.resolvePath(fe(c,y||v.homedir(),i)||v.homedir()),M=A,_=oe(t.params.attachments),p=_?.length?await re({text:o,attachments:_,workDir:A}):{text:o,attachments:_,localized:!1};let d=e.sessions.get(r);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==c||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==R||d.workDir!==A||JSON.stringify(d.externalChannel??null)!==JSON.stringify(b??null)){e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}else if(i&&d.agentSessionId!==i)try{await d.adapter.resume(i),d.agentSessionId=i}catch{e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}}if(!d)try{d=await he(e,r,c,R,A,i,b,P)}catch(u){const m=u instanceof Error&&u.message.startsWith("Unsupported agent:")?u.message:`Failed to start ${l}: ${u instanceof Error?u.message:String(u)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${r} agentType=${l} workDir=${A} agentSessionId=${i??""}: ${m}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:m,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:m});return}$&&/^mrc_[A-Za-z0-9_-]{43}$/.test($)&&B(d.adapter,r,c,b,P,$);const W={id:x??`user-${t.id}`,sessionId:r,role:"user",ts:Date.now(),payload:Z(p.text,p.attachments)};w({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:c,sessionMode:R,managerConfig:f??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null,reasoningEffort:q}});const z=u=>{ge(e,{sessionId:r,agentType:c,sessionMode:R,managerConfig:f??null,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null}),d.currentRunId||(d.nextEventSeq=0),e.nativeFusion?.registerManagedSend({sessionId:r,agentType:c,sourceAgentType:L(c,n),canonicalMessageId:x??null,sourceSessionKey:d.agentSessionId??i??null,text:p.text,managedEchoPolicy:u?.deliveryMode==="external_owner_queue"&&!V?"import_following":"suppress_following"}),E(r,W),H(e,W,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})},N=async(u,m)=>{const D=ce(c,u);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${r} agentType=${l} workDir=${A} agentSessionId=${d.agentSessionId??i??""}: ${D}`),e.sessions.delete(r);try{await C(d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:D,runId:"",seq:0}}),!m){const O={id:`agent-error-${t.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:D};E(r,O),H(e,O,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})}m&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:D}))};if(K){let u;try{const m=p.attachments;u=await J(d.adapter,p.text,n,q,m,s,a),w({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(m){await N(m,!0);return}z(u??void 0),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized||u?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...u?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:u.deliveryMode,queuedSubmissionId:u.queuedSubmissionId}:{}}}:{}}),w({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}z(),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),w({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const G=p.attachments;J(d.adapter,p.text,n,q,G,s,a).then(()=>{w({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(u=>{N(u,!1)})}async function _e(e,t){const{sessionId:a}=t.params,r=e.sessions.get(a);if(r){try{await r.adapter.stop()}catch{}ye(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{_e as handleChatAbort,Me as handleChatSend,ge as sendSessionUpdateEvent};
2
+ `),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ie(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const r={runId:e.runId,sourceSeq:e.seq},o=se(e.args);return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:r,...e.name?{name:e.name}:{},...o!==void 0?{argsSummary:o}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const le=3e4;function ue(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function ce(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function L(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function H(e,t,a){e.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:t.sessionId,message:t,session:{id:t.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function ge(e,t){F({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function U(e){return ae(e)}function j(e,t,a){return{}}function B(e,t,a,r,o,l){e.configure?.({sessionId:t,externalChannel:r??null,env:{...U(a),...j(t,r,o),...l?{SHENNIAN_MANAGED_ROOM_CONTEXT:l}:{}}})}function fe(e,t,a){if(e!=="claude"||!a)return t;const r=t.trim();return!!r&&r.startsWith("-")&&!r.includes("/")?te(a)??t:t}function pe(e,t,a,r){let o=null;function l(n,s={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ie(n,t,s),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function f(n){if(!n.currentRunId||!n.currentRunPhase)return;const s=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:s,runPhase:n.currentRunPhase},seq:s,id:`agent-heartbeat-${n.currentRunId}-${s}-${Date.now()}`})}function y(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{f(n)},le),n.heartbeatTimer.unref?.())}function i(n){const s=n?.pendingTextEvent;!n||!s||!s.text||(l({state:"delta",runId:s.runId,seq:s.seq,text:s.text,thinking:s.thinking||void 0}),n.pendingTextEvent=null)}r.on("agentEvent",n=>{const s=e.sessions.get(t),T=ue(n),I=n.state==="final"||n.state==="error"||n.state==="aborted";if(s&&(s.nextEventSeq=n.seq+1,I||(s.currentRunId=n.runId),!I&&T&&(s.currentRunPhase=T,y(s)),n.agentSessionId&&(s.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&w({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?E(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?E(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?E(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:Y(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&E(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message});const h=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(h,(e.runTextAcc.get(h)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,L(a,n.source),n.agentSessionId),s&&F({sessionId:t,agentType:a,workDir:s.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const S=n.text??"";if(!S)return;s?.pendingTextEvent&&s.pendingTextEvent.runId!==n.runId&&i(s),s&&!s.pendingTextEvent&&(s.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),s?.pendingTextEvent&&(s.pendingTextEvent.text+=S,s.pendingTextEvent.seq=n.seq);return}let x={};if(n.state==="final"){i(s);const S=e.runTextAcc.get(h)??"";S&&(x={messageSummary:de(S)}),e.runTextAcc.delete(h)}else n.state==="error"||n.state==="aborted"?(i(s),e.runTextAcc.delete(h)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&i(s);I&&s?.currentRunId===n.runId&&(s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,g(s),e.chatQueue?.noteTerminal(t)),l(n,x)}),r.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function me(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function C(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function J(e,t,a,r,o,l,g){return g?e.send(t,a,r,o,l,g):l?.trim()?e.send(t,a,r,o,l):o?.length?e.send(t,a,r,o):r?e.send(t,a,r):e.send(t,a)}async function he(e,t,a,r,o,l,g,f){e.evictIdleSessions();const y=X(a);if(!y)throw new Error(`Unsupported agent: ${a}`);const i=r==="manager"&&a!=="manager"?new ee(y,a==="claude"?"claude":"codex"):y;B(i,t,a,g,f),await i.start(t,o,l);const n={adapter:i,workDir:o,agentType:a,sessionMode:r,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:g??null,externalReplyTarget:f??null,externalChannelEnv:{...U(a),...j(t,g,f)}};return e.sessions.set(t,n),pe(e,t,a,i),n}function ye(e,t){const a=e.sessions.get(t),r=a?.currentRunId;if(!a||!r)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${r}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:r,seq:o},seq:o,id:`agent-evt-${r}-${o}`}),e.chatQueue?.noteTerminal(t)}async function qe(e,t,a){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}me(e,t.id);const{sessionId:r,text:o,agentType:l,sessionMode:g,managerConfig:f,workDir:y,agentSessionId:i,modelId:n,systemPrompt:s,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:I,reasoningEffort:h,clientMessageId:x,sessionListProjection:S,waitForDispatch:Q,responseId:K,managedRoomContextToken:$,suppressExternalOwnerEcho:V}=t.params,k=K||t.id;ne(S);const b=null,P="";if(!r||!o){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:"sessionId and text are required"});return}const c=l,D=c==="manager"||g==="manager"?"manager":"standard";D==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,T??null,I??null);const q=(c==="claude"||c==="codex"||c==="workbuddy")&&typeof h=="string"&&h.trim()?h.trim():void 0,A=e.resolvePath(fe(c,y||v.homedir(),i)||v.homedir()),M=A,_=oe(t.params.attachments),p=_?.length?await re({text:o,attachments:_,workDir:A}):{text:o,attachments:_,localized:!1};let d=e.sessions.get(r);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==c||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==D||d.workDir!==A||JSON.stringify(d.externalChannel??null)!==JSON.stringify(b??null)){e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}else if(i&&d.agentSessionId!==i)try{await d.adapter.resume(i),d.agentSessionId=i}catch{e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}}if(!d)try{d=await he(e,r,c,D,A,i,b,P)}catch(u){const m=u instanceof Error&&u.message.startsWith("Unsupported agent:")?u.message:`Failed to start ${l}: ${u instanceof Error?u.message:String(u)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${r} agentType=${l} workDir=${A} agentSessionId=${i??""}: ${m}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:m,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:m});return}$&&/^mrc_[A-Za-z0-9_-]{43}$/.test($)&&B(d.adapter,r,c,b,P,$);const W={id:x??`user-${t.id}`,sessionId:r,role:"user",ts:Date.now(),payload:Z(p.text,p.attachments)};w({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:c,sessionMode:D,managerConfig:f??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null,reasoningEffort:q}});const z=u=>{ge(e,{sessionId:r,agentType:c,sessionMode:D,managerConfig:f??null,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null}),d.currentRunId||(d.nextEventSeq=0),e.nativeFusion?.registerManagedSend({sessionId:r,agentType:c,sourceAgentType:L(c,n),canonicalMessageId:x??null,sourceSessionKey:d.agentSessionId??i??null,text:p.text,managedEchoPolicy:u?.deliveryMode==="external_owner_queue"&&!V?"import_following":"suppress_following"}),E(r,W),H(e,W,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})},N=async(u,m)=>{const R=ce(c,u);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${r} agentType=${l} workDir=${A} agentSessionId=${d.agentSessionId??i??""}: ${R}`),e.sessions.delete(r);try{await C(d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:R,runId:"",seq:0}}),!m){const O={id:`agent-error-${t.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:R};E(r,O),H(e,O,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})}m&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:R}))};if(Q){let u;try{const m=p.attachments;u=await J(d.adapter,p.text,n,q,m,s,a),w({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(m){await N(m,!0);return}z(u??void 0),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized||u?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...u?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:u.deliveryMode,queuedSubmissionId:u.queuedSubmissionId}:{}}}:{}}),w({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}z(),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),w({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const G=p.attachments;J(d.adapter,p.text,n,q,G,s,a).then(()=>{w({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(u=>{N(u,!1)})}async function Me(e,t){const{sessionId:a}=t.params,r=e.sessions.get(a);if(r){try{await r.adapter.stop()}catch{}ye(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{Me as handleChatAbort,qe as handleChatSend,ge as sendSessionUpdateEvent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.38",
3
+ "version": "0.3.39",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {