shennian 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +57 -52
- package/dist/src/agents/acp/client-services.js +1 -1
- package/dist/src/agents/acp/process-client.js +1 -1
- package/dist/src/agents/config-status.js +1 -1
- package/dist/src/agents/model-registry/cache.js +1 -1
- package/dist/src/agents/model-registry/opencode-api.js +1 -1
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/pi-coding-agent.js +3 -3
- package/dist/src/agents/pi-turn-diagnostics.js +1 -0
- package/dist/src/agents/process-tree.js +1 -0
- package/dist/src/commands/agent-readiness.js +2 -2
- package/dist/src/commands/room.js +3 -3
- package/dist/src/daemon-ipc/agent-readiness.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/manager/runtime.js +4 -4
- package/dist/src/room/activation-service.js +1 -1
- package/dist/src/room/attachment-upload.js +1 -1
- package/dist/src/room/managed-room-ipc.js +2 -2
- package/dist/src/room/mirror-sync-service.js +1 -1
- package/dist/src/room/room-action-client.js +1 -1
- package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
- package/dist/src/session/handlers/chat-recovery.js +1 -1
- package/dist/src/session/handlers/chat.js +1 -1
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue.js +1 -1
- package/node_modules/@shennian/wire/dist/session.js +1 -1
- package/package.json +1 -1
- package/dist/src/agents/acp/process-tree.js +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}catch(a){if(a instanceof S){g(t,a.code,a.message,a.nextAction);return}if(a instanceof q){g(t,a.code,a.message,C(a.code));return}g(t,"room_command_failed","Shennian Room command failed.",null)}}function h(){return process.env[L]?.trim()||null}async function v(e,t,o){const a=process.env[N]?.trim();if(!a||!
|
|
3
|
-
`),e.fail()}export{
|
|
1
|
+
import R 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 k,PublicRoomClientError as S}from"../room/public-room-client.js";import{callDaemonIpc as D}from"../daemon-ipc/client.js";import{DaemonIpcError as q}from"../daemon-ipc/protocol.js";import{MANAGED_ROOM_IPC_ROOT_ENV as N,resolveManagedRoomIpcPaths as P}from"../daemon-ipc/paths.js";import{MANAGED_ROOM_CONTEXT_ENV as L}from"../room/managed-context.js";import{createRoomInviteQr as $}from"../room/invite-qr.js";import{externalRoomNextAction as I,managedRoomNextAction as C}from"../room/room-error-guidance.js";import{attachmentMediaKind as T}from"../room/attachment-upload.js";const E={write:e=>{process.stdout.write(e)},fail:()=>{process.exitCode=1}};function ie(e,t={}){const o=t.client??new k,a=t.cache??new j({client:o}),s=t.io??E,u=t.createInviteQr??$,c=e.command("room").description("Use one explicitly referenced Shennian Room");c.command("schedule").description("Manage scheduled work in the current managed conversation").requiredOption("--operation <operation>","list/create/update/pause/resume/delete").option("--id <id>").option("--version <version>").option("--operation-id <operationId>").option("--time-description <timeDescription>").option("--timezone <timezone>").option("--prompt <prompt>").action(async r=>p("room.schedule",s,async()=>{const n=h();return n||i("managed_room_context_required","Scheduling is only available in managed Room context.",I()),v(n,"schedule",{...r,version:r.version?Number(r.version):void 0})})),c.command("create").description("Create a Room as the paired user").requiredOption("--name <name>","Room name").action(async r=>p("room.create",s,async()=>{w("create");const n=r.name.trim();(!n||n.length>80)&&i("invalid_room_name","Room name must contain 1 to 80 characters.");const m=await o.create(n),d=_(m.room,"invalid_room_response");typeof d.inviteUrl!="string"&&i("invalid_room_response","Shennian returned an invalid Room invitation.");const f=await u(d.inviteUrl).catch(()=>({imagePath:null,mimeType:"image/png",payload:d.inviteUrl}));return{...m,sharing:{inviteUrl:d.inviteUrl,inviteQr:f}}})),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(r,n)=>p("room.join",s,async()=>{w("join");const m=A(r,"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(m.roomId,m.inviteToken,d)})),c.command("open <room-url>").description("Open one Room where the paired user is already a member").action(async r=>p("room.open",s,async()=>{w("open");const n=A(r,"stable");return o.open(n.roomId)})),c.command("status [room-ref]").description("Show the managed Room, or one explicitly referenced Room").action(async r=>p("room.status",s,()=>{const n=h();return n?(r&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),v(n,"status",{})):o.status(x(r))})),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(r,n)=>{await p("room.read",s,async()=>{n.before&&n.after&&i("ambiguous_sequence_cursor","--before and --after are mutually exclusive."),n.before&&!z(n.before)&&i("invalid_sequence","--before sequence is invalid."),n.after&&!z(n.after)&&i("invalid_sequence","--after sequence is invalid.");const m=Number(n.limit);(!Number.isInteger(m)||m<1||m>50)&&i("invalid_limit","--limit must be between 1 and 50.");const d=h();if(d)return r&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),v(d,"read",{...n.before?{before:n.before}:{},...n.after?{after:n.after}:{},limit:m});const f=x(r),y=await o.read(f,{before:n.before,after:n.after,limit:m});return H(y,f,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",F,[]).action(async(r,n)=>{await p("room.send",s,async()=>{const m=n.text?.trim()??"";!m&&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 r&&i("managed_room_ref_forbidden","Do not pass a Room reference in managed Room context."),v(d,"send",{clientMessageId:`managed:${R.randomUUID()}`,text:m,files:n.file});const f=x(r),y=[];for(const O of n.file)y.push(await J(o,f,O));return o.send(f,{clientMessageId:`cli:${R.randomUUID()}`,...m?{text:m}:{},...y.length>0?{attachmentIds:y}:{}})})}),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 r=>p("room.pause",s,()=>{const n=h();return n||i("managed_room_context_required","Pause is only available in managed Room context.",I()),v(n,"pause",{reason:r.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(r,n)=>{await p("room.attachment.get",s,async()=>{const m=h(),d=m?r:n;return(!d||!/^ratt_[A-Za-z0-9_-]{1,191}$/.test(d))&&i("invalid_attachment_ref","attachment-id is invalid."),m?v(m,"attachment.get",{attachmentId:d}):a.materialize(x(r),d)})}),c.command("leave <room-ref>").description("Leave one explicitly referenced Room").action(async r=>p("room.leave",s,()=>(w("leave"),o.leave(M(r))))),c.command("list",{hidden:!0}).action(()=>g(s,"command_removed","Room listing is not available to Agents.",null)),c.command("search",{hidden:!0}).allowUnknownOption(!0).action(()=>g(s,"command_removed","Room search is not available to Agents.",null)),c.command("agent",{hidden:!0}).allowUnknownOption(!0).action(()=>g(s,"command_removed","Managed Agents are created in the Shennian mobile App.",null))}async function p(e,t,o){try{const a=await o();t.write(`${JSON.stringify({ok:!0,command:e,result:a})}
|
|
2
|
+
`)}catch(a){if(a instanceof S){g(t,a.code,a.message,a.nextAction);return}if(a instanceof q){g(t,a.code,a.message,C(a.code));return}g(t,"room_command_failed","Shennian Room command failed.",null)}}function h(){return process.env[L]?.trim()||null}async function v(e,t,o){const a=process.env[N]?.trim();if(!a||!b.isAbsolute(a))throw new q("managed_room_context_unavailable","Managed Room IPC context is unavailable.");return D({method:"managed-room.execute",params:{context:e,action:t,...o},paths:P(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.",I()),M(e)}function M(e){return A(e,"stable").roomId}function A(e,t){let o;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{o=new URL(e)}catch{i(a,s)}const u=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||!u||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,s),{roomId:u[1],...c?{inviteToken:c}:{}}}function z(e){return/^(0|[1-9]\d{0,19})$/.test(e)}function F(e,t){return[...t,e]}async function J(e,t,o){const a=b.resolve(o);let s;try{s=await U.promises.lstat(a)}catch{i("file_not_found",`File does not exist: ${o}`)}(!s.isFile()||s.isSymbolicLink())&&i("invalid_file",`Not a regular file: ${o}`),(s.size<1||s.size>100*1024*1024)&&i("invalid_file_size","Each file must be between 1 byte and 100 MB.");const u=await U.promises.readFile(a),c=R.createHash("sha256").update(u).digest("hex"),l=Q(a),r=await e.initializeAttachment(t,{clientUploadId:`cli:${R.randomUUID()}`,name:b.basename(a),mediaKind:T(b.basename(a),l),mimeType:l,byteSize:s.size,sha256:c}),n=_(r.attachment,"invalid_attachment_response"),m=_(r.upload,"invalid_attachment_response");(typeof n.id!="string"||typeof m.url!="string"||m.method!=="PUT")&&i("invalid_attachment_response","Shennian returned an invalid attachment response.");const d=_(m.headers,"invalid_attachment_response");return Object.values(d).some(f=>typeof f!="string")&&i("invalid_attachment_response","Shennian returned invalid upload headers."),Z(m.url),await e.upload(m.url,d,u),await e.completeAttachment(t,n.id),n.id}function Q(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",".heif":"image/heif",".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 H(e,t,o){return Array.isArray(e.messages)||i("invalid_server_response","Shennian returned invalid messages."),{messages:e.messages.map(a=>{const s=_(a,"invalid_server_response"),u=Array.isArray(s.attachments)?s.attachments:[];return{...s,attachments:u.map(c=>{const l=_(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 _(e,t){return(!e||typeof e!="object"||Array.isArray(e))&&i(t,"Invalid structured response."),e}function Z(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,o=null){throw new S(e,t,o)}function g(e,t,o,a){e.write(`${JSON.stringify({ok:!1,error:t,message:o,nextAction:a})}
|
|
3
|
+
`),e.fail()}export{ie as registerRoomCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAvailableAgentType as
|
|
1
|
+
import{isAvailableAgentType as a}from"@shennian/wire";import{DaemonIpcError as n}from"./protocol.js";function g(s){let d=!1;const l=()=>{const e=s.getStatus();if(e.remoteAccessDisabled)throw new n("daemon_paused","Shennian is paused; resume it only when requested.");if(!e.running)throw new n("daemon_unavailable","Shennian is not running.")};return{"agent.discover":async e=>{l();const o=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:null,u=!!(e&&typeof e=="object"&&!Array.isArray(e)&&e.compact===!0),p=!!(e&&typeof e=="object"&&!Array.isArray(e)&&e.refresh===!0),y=(p?await s.discover(o??void 0):s.getSnapshot()).filter(t=>!o||t.type===o);return{modelTested:!1,compact:u,refreshed:p,agents:y.map(t=>{const i=t.models.find(r=>r.isDefault),c=(i?[i,...t.models.filter(r=>r.id!==i.id)]:t.models).slice(0,10).map(r=>({id:r.id,name:r.name,isDefault:r.isDefault===!0}));return{type:t.type,name:t.display?.name??t.type,authState:t.authState??"unknown",...t.runtime?.cliVersion?{version:t.runtime.cliVersion}:{},...t.runtime?.compatibility?{compatibility:t.runtime.compatibility}:{},...t.runtimePlatform?{runtimePlatform:t.runtimePlatform}:{},...t.runtimeIsWsl!==void 0?{runtimeIsWsl:t.runtimeIsWsl}:{},supportsStandardSession:t.supportsStandardSession===!0,...t.supportsAgent!==void 0?{supportsAgent:t.supportsAgent}:{},...t.supportedAgentExecutionModes?{supportedAgentExecutionModes:t.supportedAgentExecutionModes}:{},supportsManagedAgent:t.supportsManagedAgent===!0,supportsManagedNativeFull:t.supportsManagedNativeFull===!0,supportsRoomTools:t.supportsRoomTools===!0,supportsWorker:t.supportsWorker===!0,supportsManagerMode:t.supportsManagerMode===!0,supportsExternalWorkerTarget:t.supportsExternalWorkerTarget===!0,modelCount:t.models.length,defaultModelId:i?.id??null,...u?{}:{models:c,modelsTruncated:t.models.length>c.length}}})}},"agent.check":async e=>{if(l(),!e||typeof e!="object"||Array.isArray(e)||typeof e.agentType!="string"||!a(e.agentType)||typeof e.workDir!="string"||!e.workDir.trim()||e.modelId!=null&&typeof e.modelId!="string")throw new n("agent_validation_invalid_configuration","Select an AI and its absolute working directory.");if(d)throw new n("agent_check_in_progress","An AI reply check is already running.");d=!0;try{const o=await s.validate({runtimeAdapter:e.agentType,workDir:e.workDir,modelId:e.modelId??null,sessionMode:"standard"});if(!o.ok)throw new n(o.errorCode,o.errorCode);return{ready:!0,agentType:e.agentType,modelId:e.modelId??null,scope:"hello_reply"}}finally{d=!1}}}}export{g as createAgentReadinessHandlers};
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as Se}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ye}from"./schedules/personal-context.js";import{executeServerSchedule as oe,deliverServerSchedule as we}from"./schedules/server-client.js";import{Command as Ce}from"commander";import
|
|
2
|
-
`),process.exitCode=1}function qo(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Ce;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(z),i.enablePositionalOptions(),Ke(i),qe(i),Se(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Re.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await _e(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=G("env.json");try{const e=JSON.parse(F.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);le()}catch{}Te(ne(process.env,p()));const g=fo();ae()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=Ne(),w=co({lockPath:zo,instanceId:c});w.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(w.ownerPid?` (PID ${w.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=Jo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),w.release(),process.exit(0)}catch{}}catch{}const K=Ve(process.env,fe)?[]:xe(process.pid);K.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${K[0]}), skipping duplicate start`),w.release(),process.exit(0)),se(process.pid,c,{version:z}),ce(process.pid,void 0,c),process.on("exit",()=>{te(process.pid,c),re(c)}),process.on("exit",w.release);let $=null;try{$=ro()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const D=await oo();if(D){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{ao({fromVersion:z,targetVersion:D.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:D.jobId},{readiness:$}),console.error(`[upgrade] New version crashed ${D.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Bo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??W.cn.url,h=de(),l=await so({serverUrl:u,authToken:m.machineToken??m.accessToken}),C=l.map(e=>e.type),V=O(l),f=new Ro,U=new Po,R=new $o(f),S=new bo(u),ve=new xo({actionClient:S}),Ie=new Oo({actionClient:S});let y=null,r;const q=Vo({authority:R,actionClient:S,attachmentCache:ve,attachmentUploader:Ie,observations:U,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),scheduleExecutor:(e,n,o)=>oe({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{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"}),R.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let L=null,j=null,H=null;const b=await me({paths:ho(),handlers:{...Io({daemonVersion:h,protocolVersion:Co,getStatus:()=>ie(),getAgents:()=>l,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!L)throw new Error("upgrade_unavailable");return L(e)},prepareExternalUpgrade:e=>{if(!j)throw new Error("external_upgrade_unavailable");return j(e)},cancelExternalUpgrade:()=>{if(!H)throw new Error("external_upgrade_unavailable");return H()}}),...So({getStatus:()=>ie(),getSnapshot:()=>O(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await yo(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=O(l);return d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(B=>B.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return wo(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let k;try{k=await me({paths:vo(),handlers:{"managed-room.execute":e=>q(e),"schedule.personal":async e=>{const n=e,o=ye(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await oe({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let Q=!1;const _=()=>{Q||(Q=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",_),process.once("SIGTERM",_),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",_),process.removeListener("SIGTERM",_);const Y=`${Ko(u)}/relay/machine`,M=m.machineToken;if(!M)return;try{await Ho({serverUrl:u,machineToken:M,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}m.machineId&&mo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${Y}... (v${h}) agents: ${C.join(",")}`),T({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:C}});let P=null,E=null,N=0,v=no();const Z=Ee(m.autoUpgrade),d=new Ae({serverUrl:Y,machineToken:M,cliVersion:h,agentList:C,autoUpgradePolicy:Z,sessionSyncProtocolVersion:pe,sessionSyncCapabilities:ge,daemonInstallationId:Lo(),dataEpoch:ue,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),T({level:"info",wsEvent:"daemon.connected"}),V.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:V}}),lo(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(V)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:o}})}).catch(()=>{}),v&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),E?.start();const e=++N,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&io(o.jobId,o.to),eo(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),po({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===N&&d.getState()==="connected"&&P?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===N&&d.getState()==="connected"){if(o instanceof Go)return;const t=o instanceof Fo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{N+=1,r?.revokeProjectFsHandles(),E?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${qo(e)}`),T({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),T({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){Ao(d,E,e);return}r.handleReq(e)}});P=go()?new uo(d):null,E=new Eo({registry:f,observations:U,actionClient:S,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new To().hasSession(e),onMessages:async(e,n,o,t)=>{await y?.processMessages(e,n,o,t)},onBindingPaused:e=>{R.revokeSession(e.registryKey),e.lifecycleState==="closed"?r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new to({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:pe,dataEpoch:ue,advertisedCapabilities:ge,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});x.setPolicy(Z),r=new Qe(d,P,h,x,new ko({registry:f,observations:U,bindingClient:S,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Oe(),isDisabled:()=>ae(),shutdownAfterResponse:()=>{setTimeout(()=>{$e(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>R.revokeToken(e),q,null,Ye(M),()=>O(l)),L=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await Ge(d,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:B=>r.prepareForUpgrade(B),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},j=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},H=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),y=new _o({registry:f,observations:U,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!Uo(f,e)||!Do(e,t))throw new Error("managed_room_binding_unavailable");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,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return R.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new No,resolveManagedAgentPolicy:e=>Mo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>we({serverUrl:u,machineToken:p().machineToken,req:e,client:d,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await S.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>y.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>y.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?y.prepareDirectConversationTurn(e.sessionId,jo):e.source==="manager_event"?y.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:y.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),F.mkdirSync(fe,{recursive:!0}),se(process.pid,c,{version:h}),ce(process.pid,void 0,c),d.connect(),process.stdin.resume();let X=!1;const ee=async()=>{if(!X){X=!0,console.log(A.gray(`
|
|
3
|
-
Disconnecting...`)),T({level:"info",wsEvent:"daemon.stop"}),clearInterval(g);try{await r.cleanup(),x.stop(),P?.stop(),
|
|
1
|
+
import{registerScheduleCommand as Se}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ye}from"./schedules/personal-context.js";import{executeServerSchedule as oe,deliverServerSchedule as we}from"./schedules/server-client.js";import{Command as Ce}from"commander";import E from"chalk";import Re from"node:os";import F from"node:fs";import{loadConfig as p,saveConfig as be,getConfigPath as ke,getShennianDir as Pe,resolveShennianPath as G,normalizeAutoUpgradePolicy as Ae}from"./config/index.js";import{CliRelayClient as Ee}from"./relay/client.js";import{collectKnownLogSecrets as ne,installCliLogRedaction as Te,sanitizeCliLogValue as De}from"./log-redaction.js";import{registerPairCommand as Ue,runSmartStart as Me}from"./commands/pair.js";import{registerLogoutCommand as _e}from"./commands/logout.js";import{clearDaemonPidIfOwner as te,createDaemonInstanceId as Ne,clearDaemonLauncher as re,findRunningDaemonProcessIds as xe,isRemoteAccessDisabled as ae,prepareRemoteAccessPause as Oe,stopPreparedRemoteAccessService as $e,isIsolatedDaemonE2eInstance as Ve,getDaemonStatus as ie,registerDaemonCommand as Le,writeDaemonPid as se,writeDaemonLauncher as ce}from"./commands/daemon.js";import{registerRuntimeCommand as je}from"./commands/runtime.js";import{registerAgentCommand as He}from"./commands/agent.js";import{registerManagerCommand as Be}from"./commands/manager.js";import{registerWorkerCommand as Fe}from"./commands/worker.js";import{handleUpgradeStart as Ge,registerUpgradeCommand as We}from"./commands/upgrade.js";import{registerToolsCommand as ze}from"./commands/tools.js";import{registerQueueCommand as Je}from"./commands/queue.js";import{registerIntegrationCommand as Ke}from"./commands/integration.js";import{registerRoomCommand as qe}from"./commands/room.js";import{SessionManager as Qe}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Ye}from"./session/handlers/project-fs.js";import{SERVERS as W,regionToUrl as Ze,urlToRegion as Xe}from"./region.js";import{clearUpgradeAttempt as eo,getCurrentVersion as de,handleStartupCrashCheck as oo,readUpgradeAttempt as no}from"./upgrade/engine.js";import{AutoUpgradeScheduler as to}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as ro}from"./upgrade/updater-bootstrap.js";import{launchUpdater as ao,markUpdaterHealthy as io}from"./upgrade/updater-launcher.js";import{detectAgents as so}from"./agents/detect.js";import{augmentProcessPath as le}from"./env-path.js";import{acquireDaemonInstanceLock as co}from"./commands/daemon-instance-lock.js";le();const z=de();import{getCachedAgentInfos as O,resolveAgentInfos as lo}from"./agents/model-registry.js";import{initCliLogReporter as mo,reportLog as T,reportUpgradeAggregateMetric as po}from"./log-reporter.js";import{isNativeFusionEnabled as go}from"./native-fusion/config.js";import{NativeSessionFusionService as uo}from"./native-fusion/service.js";import{startDaemonLogRetention as fo}from"./daemon-log.js";import{startDaemonIpcServer as me}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as ho,resolveManagedRoomIpcPaths as vo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as Io}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as So}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as yo}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as wo}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as Co}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as Ro}from"./room/session-registry.js";import{HttpRoomBindingClient as bo}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as ko}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Po}from"./room/host-session-observations.js";import{RoomMirrorSyncService as Ao}from"./room/mirror-sync-service.js";import{handleLiveRoomDelivery as Eo}from"./room/live-delivery.js";import{NativeSourceDeletionStore as To}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as Do,isCurrentRegistration as Uo}from"./room/registration-authority.js";import{RoomActivationService as Mo,resolveManagedAgentPolicy as _o}from"./room/activation-service.js";import{RoomActivationAudit as No}from"./room/activation-audit.js";import{RoomAttachmentCache as xo}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Oo}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as $o}from"./room/managed-context.js";import{createManagedRoomExecutor as Vo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as pe,SESSION_SYNC_ADVERTISED_CAPABILITIES as ge}from"@shennian/wire";import{getDaemonInstallationId as Lo}from"./session/store.js";import{OWNER_DIRECT_TURN_OPTIONS as jo}from"./session/queue-helpers.js";import{synchronizeMachinePersonalAuthority as Ho}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as ue,ensurePersonalDataEpoch as Bo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as Fo,DeletionSyncTerminalError as Go}from"./session/deletion-sync-state.js";const fe=Pe(),Wo=G("daemon.pid"),zo=G("daemon.instance.lock");function Jo(){try{const a=F.readFileSync(Wo,"utf-8").trim();if(a.startsWith("{")){const g=JSON.parse(a),c=Number(g.pid);return Number.isInteger(c)&&c>0?c:null}const s=parseInt(a,10);return Number.isInteger(s)&&s>0?s:null}catch{return null}}function Ko(a){return a.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function J(){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 qo(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Ce;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(z),i.enablePositionalOptions(),Ke(i),qe(i),Se(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Re.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Me(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=G("env.json");try{const e=JSON.parse(F.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);le()}catch{}Te(ne(process.env,p()));const g=fo();ae()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=Ne(),w=co({lockPath:zo,instanceId:c});w.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(w.ownerPid?` (PID ${w.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=Jo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),w.release(),process.exit(0)}catch{}}catch{}const K=Ve(process.env,fe)?[]:xe(process.pid);K.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${K[0]}), skipping duplicate start`),w.release(),process.exit(0)),se(process.pid,c,{version:z}),ce(process.pid,void 0,c),process.on("exit",()=>{te(process.pid,c),re(c)}),process.on("exit",w.release);let $=null;try{$=ro()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const D=await oo();if(D){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{ao({fromVersion:z,targetVersion:D.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:D.jobId},{readiness:$}),console.error(`[upgrade] New version crashed ${D.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Bo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??W.cn.url,h=de(),l=await so({serverUrl:u,authToken:m.machineToken??m.accessToken}),C=l.map(e=>e.type),V=O(l),f=new Ro,U=new Po,R=new $o(f),S=new bo(u),ve=new xo({actionClient:S}),Ie=new Oo({actionClient:S});let y=null,r;const q=Vo({authority:R,actionClient:S,attachmentCache:ve,attachmentUploader:Ie,observations:U,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>oe({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{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"}),R.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let L=null,j=null,H=null;const b=await me({paths:ho(),handlers:{...Io({daemonVersion:h,protocolVersion:Co,getStatus:()=>ie(),getAgents:()=>l,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!L)throw new Error("upgrade_unavailable");return L(e)},prepareExternalUpgrade:e=>{if(!j)throw new Error("external_upgrade_unavailable");return j(e)},cancelExternalUpgrade:()=>{if(!H)throw new Error("external_upgrade_unavailable");return H()}}),...So({getStatus:()=>ie(),getSnapshot:()=>O(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await yo(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=O(l);return d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(B=>B.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return wo(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let k;try{k=await me({paths:vo(),handlers:{"managed-room.execute":e=>q(e),"schedule.personal":async e=>{const n=e,o=ye(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await oe({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let Q=!1;const M=()=>{Q||(Q=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",M),process.once("SIGTERM",M),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",M),process.removeListener("SIGTERM",M);const Y=`${Ko(u)}/relay/machine`,_=m.machineToken;if(!_)return;try{await Ho({serverUrl:u,machineToken:_,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}m.machineId&&mo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${Y}... (v${h}) agents: ${C.join(",")}`),T({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:C}});let P=null,A=null,N=0,v=no();const Z=Ae(m.autoUpgrade),d=new Ee({serverUrl:Y,machineToken:_,cliVersion:h,agentList:C,autoUpgradePolicy:Z,sessionSyncProtocolVersion:pe,sessionSyncCapabilities:ge,daemonInstallationId:Lo(),dataEpoch:ue,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),T({level:"info",wsEvent:"daemon.connected"}),V.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:V}}),lo(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(V)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:o}})}).catch(()=>{}),v&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),A?.start();const e=++N,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&io(o.jobId,o.to),eo(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),po({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===N&&d.getState()==="connected"&&P?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===N&&d.getState()==="connected"){if(o instanceof Go)return;const t=o instanceof Fo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{N+=1,r?.revokeProjectFsHandles(),A?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${qo(e)}`),T({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),T({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){Eo(d,A,e);return}r.handleReq(e)}});P=go()?new uo(d):null,A=new Ao({registry:f,observations:U,actionClient:S,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new To().hasSession(e),onMessages:async(e,n,o,t)=>{await y?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(R.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new to({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:pe,dataEpoch:ue,advertisedCapabilities:ge,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});x.setPolicy(Z),r=new Qe(d,P,h,x,new ko({registry:f,observations:U,bindingClient:S,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Oe(),isDisabled:()=>ae(),shutdownAfterResponse:()=>{setTimeout(()=>{$e(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>R.revokeToken(e),q,null,Ye(_),()=>O(l)),L=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await Ge(d,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:B=>r.prepareForUpgrade(B),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},j=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},H=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),y=new Mo({registry:f,observations:U,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!Uo(f,e)||!Do(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return R.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new No,resolveManagedAgentPolicy:e=>_o({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>we({serverUrl:u,machineToken:p().machineToken,req:e,client:d,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await S.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>y.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>y.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?y.prepareDirectConversationTurn(e.sessionId,jo):e.source==="manager_event"?y.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:y.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),F.mkdirSync(fe,{recursive:!0}),se(process.pid,c,{version:h}),ce(process.pid,void 0,c),d.connect(),process.stdin.resume();let X=!1;const ee=async()=>{if(!X){X=!0,console.log(E.gray(`
|
|
3
|
+
Disconnecting...`)),T({level:"info",wsEvent:"daemon.stop"}),clearInterval(g);try{await r.cleanup(),x.stop(),P?.stop(),A?.stop(),d.disconnect(),await Promise.allSettled([b.close(),k.close()]),te(process.pid,c),re(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{ee()}),process.on("SIGTERM",()=>{ee()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>J()),i.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(()=>J()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>J());const he=i.command("config").description("View or modify configuration");he.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${ke()}`);const a=p(),s=De(a,ne(process.env,a));if(a.serverUrl){const g=Xe(a.serverUrl);console.log(`Region: ${g} (${W[g].label})`)}console.log(JSON.stringify(s,null,2))}),he.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((a,s)=>{const g=p();if(a==="server"){s!=="cn"&&s!=="global"&&(console.error(E.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;g.serverUrl=Ze(c),be(g),console.log(E.green(`\u2713 Server set to ${W[c].label}`)),console.log(E.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(E.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Ue(i),_e(i),Le(i),je(i),He(i),Be(i),Fe(i),We(i),ze(i),Je(i),i.parse();
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import _ from"node:http";import{createHash as N,randomBytes as W,randomUUID as k}from"node:crypto";import
|
|
2
|
-
`).trim()}function G(g){return g==="worker.final"?"completed":g==="worker.error"?"failed":g==="worker.aborted"?"stopped":"progress"}function F(g,e){return`manager-inbox:${N("sha256").update(g).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function J(g){const e=[];let t=0;for await(const r of g){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>j)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function K(g,e,t,r){g.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function x(){return V()?M.join(A.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function V(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class le{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=W(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;pendingWorkerOrigins=new Map;prepareWorkerTurnOrigin(e,t){t?this.pendingWorkerOrigins.set(e,t):this.pendingWorkerOrigins.delete(e)}constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new D}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.registry.clearWorkerRunOrigins(),this.server=_.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=y,this.server.timeout=y,this.server.keepAliveTimeout=y,this.server.headersTimeout=y+5e3,await new Promise((t,r)=>{this.server.once("error",r),this.server.listen(0,"127.0.0.1",()=>t())});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.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}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=x();p.mkdirSync(M.dirname(e),{recursive:!0}),p.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),p.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=x(),t=JSON.parse(p.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&p.unlinkSync(e)}catch{}}getInjectedEnv(e,t,r,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:t??"",SHENNIAN_MANAGER_WORKDIR:f(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:f(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,t){if(t){this.managedPolicies.set(e,t);const r=this.registry.getManager(e);r&&!r.managedRoomBound&&this.registry.upsertManager({...r,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,t){const r=this.registry.getManager(e),n=this.managedPolicies.get(e);return!r||!n||f(t)!==r.workDir?null:n}isExternalWorkerController(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="ephemeral"&&f(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const t=this.registry.retireManager(e);this.managedPolicies.delete(e);const r=this.opts.getRuntime(),n=[],a=async(s,u)=>{const i=[];try{r.runJournal?.markSessionAborted(s)}catch(h){i.push(h)}try{r.recoveryBarrier?.end(s)}catch(h){i.push(h)}try{r.chatQueue?.purgeSession(s)}catch(h){i.push(h)}const o=r.sessions.get(s);let c=!o;if(o){if(o.managedRoomContextToken)try{r.revokeManagedRoomContextToken?.(o.managedRoomContextToken),o.managedRoomContextToken=null}catch(h){i.push(h)}try{await o.adapter.stop(),c=!0}catch(h){i.push(h)}}if(o&&c&&i.length===0&&(r.sessions.delete(s),o.heartbeatTimer&&clearInterval(o.heartbeatTimer),o.adapter.removeAllListeners()),c&&i.length===0)try{u?.()}catch(h){i.push(h),n.push(h)}else n.push(...i)};await a(e);for(const s of t)await a(s.sessionId,()=>this.registry.completeRetiredWorker(e,s.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return t.map(s=>s.sessionId)}setManagerWorkerDefaults(e,t,r){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:t??null,defaultWorkerModelId:r??null})}getManagerWorkerDefaults(e){const t=this.registry.getManager(e);return{agentType:t?.defaultWorkerAgentType??null,modelId:t?.defaultWorkerModelId??null}}noteManagerAgentSession(e,t,r,n){const a=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:f(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:a?.agentType,providerModelId:a?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};if(t.agentSessionId&&(n.agentSessionId=t.agentSessionId),t.state==="start"){const i=this.pendingWorkerOrigins.get(e);this.pendingWorkerOrigins.delete(e),i&&(n.runRoomOrigins={...r.runRoomOrigins??{},[t.runId]:i})}const a=`${e}:${t.runId}`;let s=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const i=(this.workerTextAcc.get(a)??"")+t.text;this.workerTextAcc.set(a,i);const o=R(i);o&&(n.summary=o.length>160?`${o.slice(0,160)}...`:o)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const i=R(this.workerTextAcc.get(a)??"");if(i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i,s=t.state==="final"?i:t.message||i),this.workerTextAcc.delete(a),r.runRoomOrigins?.[t.runId]){const o={...r.runRoomOrigins};delete o[t.runId],n.runRoomOrigins=o}}else t.state==="start"&&(n.status="running");const u=this.registry.updateWorker(e,n);u&&t.errorCode==="auth_retrying"&&this.registry.enqueueInboxEvent({managerSessionId:u.managedBy,workerSessionId:u.sessionId,kind:"worker.health",priority:"normal",runId:t.runId,roomOrigin:r.runRoomOrigins?.[t.runId]??null,summary:t.message||"Worker authentication is retrying.",code:"auth_retrying"}),u&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(u.managedBy,u,t.state,t.runId,s,r.runRoomOrigins?.[t.runId]??null,t.errorCode==="auth_required"||t.errorCode==="acp_auth_required"?"auth_required":void 0)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){K(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){m(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await J(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){m(t,410,{ok:!1,error:"feature_retired"});return}const a={registry:this.registry,getRuntime:this.opts.getRuntime,getAgentSnapshot:this.opts.getAgentSnapshot,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},s=await H(a,r.pathname,n)??await S(a,r.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(s){m(t,s.status,s.body);return}m(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){if(r instanceof B){m(t,r.status,{ok:!1,error:r.code,message:r.message,nextAction:r.nextAction});return}m(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r,n){const a={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await S({registry:this.registry,getRuntime:this.opts.getRuntime,getAgentSnapshot:this.opts.getAgentSnapshot,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},a[t],r,e,{roomOrigin:n});if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,t,r,n,a,s,u,i){if((await this.opts.dispatchReq(v({type:"req",id:`manager-enqueue-${k()}`,method:"chat.enqueue",params:{...w(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:a,modelId:s,managedWorkerParentSessionId:u,managerWorkerRoomOrigin:i}})))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind==="managed_session"&&(O(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),q(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,a,s,u){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,roomOrigin:s??null,summary:a||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)",...u?{code:u}:{}}),o=this.registry.getManager(e);i&&o?.controllerKind==="managed_session"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),t=this.registry.load(),r=this.opts.getRuntime();for(const n of Object.values(t.workers)){if(n.status!=="running")continue;const a=e-Date.parse(n.createdAt);if(a<10*6e4)continue;const s=e-Date.parse(n.lastActivityAt),u=r.sessions.get(n.sessionId)?.currentRunId,i=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!u){if(!i&&s>=10*6e4){const E="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",I=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:E});I&&this.enqueueWorkerEvent(I.managedBy,I,"error",n.runId??void 0,E)}continue}const o=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(o&&e-o<10*6e4)continue;const c=t.managers[n.managedBy];if(!c)continue;const h=Math.max(0,Math.floor(s/6e4)),d=R(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),l=`\u5DF2\u8FD0\u884C ${Math.floor(a/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
1
|
+
import _ from"node:http";import{createHash as N,randomBytes as W,randomUUID as k}from"node:crypto";import f from"node:fs";import A from"node:os";import M from"node:path";import{ManagerRegistry as T}from"./registry.js";import{appendMessage as O}from"../session/store.js";import{requestSessionIndexPublish as D}from"../session/index-publisher.js";import{RetiredChannelRuntime as q}from"./retired-external-channel-compat.js";import{resolveShennianPath as P}from"../config/index.js";import{buildExternalChannelInstructions as C}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as w}from"../personal-sync/epoch.js";import{EphemeralWorkerError as B,handleEphemeralWorkerIpcRoute as H,handleWorkerIpcRoute as S}from"./worker-ipc.js";import{deriveWorkerTitle as $,formatWorkerMarkdown as U}from"./worker-markdown.js";import{markTrustedManagerEvent as v}from"../session/manager-event-authority.js";const j=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),y=12*6e4;let b=null;function de(g){b=g}function he(){return b}function m(g){return M.resolve(g||A.homedir())}function p(g,e,r){g.writeHead(e,{"content-type":"application/json; charset=utf-8"}),g.end(JSON.stringify(r))}function R(g){return g.replace(/\r\n/g,`
|
|
2
|
+
`).trim()}function G(g){return g==="worker.final"?"completed":g==="worker.error"?"failed":g==="worker.aborted"?"stopped":"progress"}function F(g,e){return`manager-inbox:${N("sha256").update(g).update("\0").update(e.map(t=>t.id).join("\0")).digest("hex").slice(0,32)}`}async function J(g){const e=[];let r=0;for await(const t of g){const n=Buffer.isBuffer(t)?t:Buffer.from(t);if(r+=n.length,r>j)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function K(g,e,r,t){g.client.sendRes({type:"res",id:e,ok:r,...r?{payload:t}:{error:String(t.error||"unknown error")}})}function x(){return V()?M.join(A.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function V(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class le{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=W(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;pendingWorkerOrigins=new Map;prepareWorkerTurnOrigin(e,r){r?this.pendingWorkerOrigins.set(e,r):this.pendingWorkerOrigins.delete(e)}constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new q}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.registry.clearWorkerRunOrigins(),this.server=_.createServer((r,t)=>{this.handleIpc(r,t)}),this.server.requestTimeout=y,this.server.timeout=y,this.server.keepAliveTimeout=y,this.server.headersTimeout=y+5e3,await new Promise((r,t)=>{this.server.once("error",t),this.server.listen(0,"127.0.0.1",()=>r())});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.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}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=x();f.mkdirSync(M.dirname(e),{recursive:!0}),f.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),f.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=x(),r=JSON.parse(f.readFileSync(e,"utf-8"));(r.url===this.ipcUrl||r.token===this.ipcToken)&&f.unlinkSync(e)}catch{}}getInjectedEnv(e,r,t,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:r??"",SHENNIAN_MANAGER_WORKDIR:m(t),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:m(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,r){if(r){this.managedPolicies.set(e,r);const t=this.registry.getManager(e);t&&!t.managedRoomBound&&this.registry.upsertManager({...t,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,r){const t=this.registry.getManager(e),n=this.managedPolicies.get(e);return!t||!n||m(r)!==t.workDir?null:n}isExternalWorkerController(e,r){const t=this.registry.getManager(e);return t?.controllerKind==="ephemeral"&&m(r)===t.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}getWorkerControllerSessionId(e,r){const t=this.findWorker(e);if(!t||m(r)!==t.workDir)return null;const n=this.registry.getManager(t.managedBy);return!n||n.workDir!==t.workDir?null:n.sessionId}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const r=this.registry.retireManager(e);this.managedPolicies.delete(e);const t=this.opts.getRuntime(),n=[],a=async(s,u)=>{const i=[];try{t.runJournal?.markSessionAborted(s)}catch(h){i.push(h)}try{t.recoveryBarrier?.end(s)}catch(h){i.push(h)}try{t.chatQueue?.purgeSession(s)}catch(h){i.push(h)}const o=t.sessions.get(s);let c=!o;if(o){if(o.managedRoomContextToken)try{t.revokeManagedRoomContextToken?.(o.managedRoomContextToken),o.managedRoomContextToken=null}catch(h){i.push(h)}try{await o.adapter.stop(),c=!0}catch(h){i.push(h)}}if(o&&c&&i.length===0&&(t.sessions.delete(s),o.heartbeatTimer&&clearInterval(o.heartbeatTimer),o.adapter.removeAllListeners()),c&&i.length===0)try{u?.()}catch(h){i.push(h),n.push(h)}else n.push(...i)};await a(e);for(const s of r)await a(s.sessionId,()=>this.registry.completeRetiredWorker(e,s.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return r.map(s=>s.sessionId)}setManagerWorkerDefaults(e,r,t){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:r??null,defaultWorkerModelId:t??null})}getManagerWorkerDefaults(e){const r=this.registry.getManager(e);return{agentType:r?.defaultWorkerAgentType??null,modelId:r?.defaultWorkerModelId??null}}noteManagerAgentSession(e,r,t,n){const a=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:r,workDir:m(t),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:a?.agentType,providerModelId:a?.providerModelId})}noteAgentEvent(e,r){const t=this.findWorker(e);if(!t)return;const n={lastActivityAt:new Date().toISOString(),runId:r.runId};if(r.agentSessionId&&(n.agentSessionId=r.agentSessionId),r.state==="start"){const i=this.pendingWorkerOrigins.get(e);this.pendingWorkerOrigins.delete(e),i&&(n.runRoomOrigins={...t.runRoomOrigins??{},[r.runId]:i})}const a=`${e}:${r.runId}`;let s=r.message||t.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(r.state==="delta"&&r.text&&!r.thinking){const i=(this.workerTextAcc.get(a)??"")+r.text;this.workerTextAcc.set(a,i);const o=R(i);o&&(n.summary=o.length>160?`${o.slice(0,160)}...`:o)}if(r.state==="final"||r.state==="error"||r.state==="aborted"){n.status=r.state;const i=R(this.workerTextAcc.get(a)??"");if(i&&(n.summary=i.length>240?`${i.slice(0,240)}...`:i,s=r.state==="final"?i:r.message||i),this.workerTextAcc.delete(a),t.runRoomOrigins?.[r.runId]){const o={...t.runRoomOrigins};delete o[r.runId],n.runRoomOrigins=o}}else r.state==="start"&&(n.status="running");const u=this.registry.updateWorker(e,n);u&&r.errorCode==="auth_retrying"&&this.registry.enqueueInboxEvent({managerSessionId:u.managedBy,workerSessionId:u.sessionId,kind:"worker.health",priority:"normal",runId:r.runId,roomOrigin:t.runRoomOrigins?.[r.runId]??null,summary:r.message||"Worker authentication is retrying.",code:"auth_retrying"}),u&&(r.state==="final"||r.state==="error"||r.state==="aborted")&&this.enqueueWorkerEvent(u.managedBy,u,r.state,r.runId,s,t.runRoomOrigins?.[r.runId]??null,r.errorCode==="auth_required"||r.errorCode==="acp_auth_required"?"auth_required":void 0)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){K(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,r){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){p(r,401,{ok:!1,error:"Unauthorized"});return}try{const t=new URL(e.url??"/","http://127.0.0.1"),n=await J(e);if(t.pathname.startsWith("/channel/")||t.pathname.startsWith("/external/")||t.pathname.startsWith("/wechat-rpa/")){p(r,410,{ok:!1,error:"feature_retired"});return}const a={registry:this.registry,getRuntime:this.opts.getRuntime,getAgentSnapshot:this.opts.getAgentSnapshot,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},s=await H(a,t.pathname,n)??await S(a,t.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(s){p(r,s.status,s.body);return}p(r,404,{ok:!1,error:`Unknown manager IPC path: ${t.pathname}`})}catch(t){if(t instanceof B){p(r,t.status,{ok:!1,error:t.code,message:t.message,nextAction:t.nextAction});return}p(r,400,{ok:!1,error:t instanceof Error?t.message:String(t)})}}async executeScopedWorkerAction(e,r,t,n){const a={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await S({registry:this.registry,getRuntime:this.opts.getRuntime,getAgentSnapshot:this.opts.getAgentSnapshot,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},a[r],t,e,{roomOrigin:n});if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,r,t,n,a,s,u,i){if((await this.opts.dispatchReq(v({type:"req",id:`manager-enqueue-${k()}`,method:"chat.enqueue",params:{...w(),sessionId:e,text:n,agentType:r,workDir:t,agentSessionId:a,modelId:s,managedWorkerParentSessionId:u,managerWorkerRoomOrigin:i}})))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,r,t){this.registry.getManager(e)?.controllerKind==="managed_session"&&(O(e,{id:t,sessionId:e,role:"agent",ts:Date.now(),payload:r},"committed"),D(this.opts.getRuntime().client))}enqueueWorkerEvent(e,r,t,n,a,s,u){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:r.sessionId,kind:t==="final"?"worker.final":t==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,roomOrigin:s??null,summary:a||r.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)",...u?{code:u}:{}}),o=this.registry.getManager(e);i&&o?.controllerKind==="managed_session"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),r=this.registry.load(),t=this.opts.getRuntime();for(const n of Object.values(r.workers)){if(n.status!=="running")continue;const a=e-Date.parse(n.createdAt);if(a<10*6e4)continue;const s=e-Date.parse(n.lastActivityAt),u=t.sessions.get(n.sessionId)?.currentRunId,i=!!t.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!u){if(!i&&s>=10*6e4){const E="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",I=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:E});I&&this.enqueueWorkerEvent(I.managedBy,I,"error",n.runId??void 0,E)}continue}const o=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(o&&e-o<10*6e4)continue;const c=r.managers[n.managedBy];if(!c)continue;const h=Math.max(0,Math.floor(s/6e4)),d=R(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),l=`\u5DF2\u8FD0\u884C ${Math.floor(a/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
|
|
3
3
|
|
|
4
4
|
\u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
|
|
5
5
|
- \u6700\u8FD1\u6587\u672C\uFF1A${d||n.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)"}
|
|
6
6
|
- \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
|
|
7
|
-
- \u6700\u8FD1 ${h} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:l}),this.registry.enqueueInboxEvent({managerSessionId:c.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,roomOrigin:n.runRoomOrigins?.[n.runId??""]??null,summary:l})&&c.controllerKind==="managed_session"&&this.drainManagerInbox(c.sessionId)}}async interruptAndResumeManager(e,t,
|
|
7
|
+
- \u6700\u8FD1 ${h} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:l}),this.registry.enqueueInboxEvent({managerSessionId:c.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,roomOrigin:n.runRoomOrigins?.[n.runId??""]??null,summary:l})&&c.controllerKind==="managed_session"&&this.drainManagerInbox(c.sessionId)}}async interruptAndResumeManager(e,r,t,n){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${k()}`,method:"chat.abort",params:{...w(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,r,!1,t,n)}bindManagerAdapterEvents(e,r){r.on("agentEvent",t=>{if(t.state==="start"&&t.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,t.agentSessionId,n.workDir,n.modelId)}t.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(t.state==="final"||t.state==="error"||t.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,r){const t=this.registry.getManager(e);t&&this.registry.upsertManager({...t,status:r})}async drainManagerInbox(e){const r=this.registry.getManager(e);if(!r||r.deliveringInboxEventIds?.length)return;const t=this.registry.listInboxEvents(e);if(!t.length)return;const n=t.find(d=>d.priority==="high"),a=this.opts.getRuntime().sessions.get(e);if(r.status!=="idle"&&!n||a?.currentRunId&&!n)return;const u=(n??t[0]).roomOrigin??null,i=JSON.stringify(u),o=n?[n]:t.filter(d=>JSON.stringify(d.roomOrigin??null)===i).slice(0,20),c=o.map(d=>{const l=this.registry.getWorkerForManager(e,d.workerSessionId);return U({kind:G(d.kind),sessionId:d.workerSessionId,title:l?.title||$(l?.userGoal||l?.summary||d.summary),body:d.summary})}).join(`
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
`),h=F(e,o);try{n&&a?.currentRunId?(this.registry.upsertManager({...
|
|
11
|
+
`),h=F(e,o);try{n&&a?.currentRunId?(this.registry.upsertManager({...r,status:"interrupting",deliveringInboxEventIds:o.map(l=>l.id)}),await this.interruptAndResumeManager(r,c,h,u)):(this.registry.upsertManager({...r,status:"running",deliveringInboxEventIds:o.map(l=>l.id)}),await this.dispatchManagerContinuation(r,c,!0,h,u)),this.registry.removeInboxEvents(e,o.map(l=>l.id));const d=this.registry.getManager(e);d&&this.registry.upsertManager({...d,deliveringInboxEventIds:[]})}catch(d){const l=this.registry.getManager(e);l&&this.registry.upsertManager({...l,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${d instanceof Error?d.message:String(d)}`)}}acknowledgeManagerInboxDelivery(e){const r=this.registry.getManager(e),t=r?.deliveringInboxEventIds??[];!r||t.length===0||(this.registry.removeInboxEvents(e,t),this.registry.upsertManager({...r,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load(),r=this.opts.getRuntime();for(const t of Object.values(e.managers)){if(r.sessions.get(t.sessionId)?.currentRunId)continue;(t.status!=="idle"||t.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...t,status:"idle",deliveringInboxEventIds:[]});const a=this.registry.listInboxEvents(t.sessionId).filter(s=>s.kind==="worker.health"&&!r.sessions.get(s.workerSessionId)?.currentRunId).map(s=>s.id);this.registry.removeInboxEvents(t.sessionId,a),this.registry.listInboxEvents(t.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(t.sessionId)})}}async dispatchManagerContinuation(e,r,t=!1,n,a){const s=e.agentType??"manager",u=s==="manager"?e.modelId:e.providerModelId??void 0,i={...w(),sessionId:e.sessionId,text:r,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:u,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n,...a?{managerEventRoomOrigin:a}:{}};if((await this.opts.dispatchReq(v({type:"req",id:`manager-${t?"enqueue":"send"}-${k()}`,method:"chat.enqueue",params:i})))?.durableQueueHandoff===!1)throw new Error("manager_event_queue_handoff_unconfirmed")}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(r=>C(r,void 0,e,"manager")).join(`
|
|
12
12
|
|
|
13
13
|
`).trim()}}export{le as ManagerRuntimeService,he as getManagerRuntimeService,de as setManagerRuntimeService};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{isAvailableBuiltinAgentType as k,managedAgentExecutionMode as P}from"@shennian/wire";import{getBuiltinAgentVersion as S,resolveBuiltinCommand as B}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as K,resolveCustomRuntimeExecutableFingerprint as $}from"../agents/custom-managed-runtime.js";import{loadConfig as x}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as W}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as T}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as R}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as q}from"./activation-policy.js";import{buildManagedAgentPermissionPrompt as E,composeManagedRoomPrompt as D}from"./managed-prompt-composer.js";import{isCurrentRegistration as f}from"./registration-authority.js";const N=4,O=6e4,F=6,V=5*6e4;class ce{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,o,t,a={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],m=[],c=new Map,u=new Set,g=[...o].sort((i,s)=>J(i.sequence,s.sequence));for(const i of t){const s=this.deps.registry.getByRegistryKey(i.registryKey);!s?.binding||!f(this.deps.registry,i)||s.binding.roomId!==e||s.binding.status!=="active"&&s.binding.status!=="paused"&&s.binding.activationQueue.length}for(const i of t){const s=this.deps.registry.getByRegistryKey(i.registryKey);if(!s?.binding||!f(this.deps.registry,i)||s.binding.roomId!==e||s.binding.activationQueue.length===0&&!s.binding.activeActivation||(u.add(s.registryKey),s.binding.status!=="active"||s.binding.activationMode==="paused")||s.binding.activationQueue.length===0)continue;const l=this.deps.observations.getBySource(s);if(!l?.workspacePath||l.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(s.registryKey,a);for(const r of s.binding.activationQueue)d.push({registration:s,input:{params:{sessionId:s.nianSessionId,text:r.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:y(s.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:l.workspacePath,agentSessionId:s.sourceSessionKey,queueMessageId:`room-activation-${s.binding.bindingId}-${r.causeMessageId}`,clientMessageId:`room-activation-${s.binding.bindingId}-${r.causeMessageId}`},context:{registryKey:s.registryKey,causeMessageId:r.causeMessageId,rootCauseMessageId:r.rootCauseMessageId,sequence:r.sequence,hopCount:r.hopCount}}})}for(const i of g){const s=new Set(q(i,t).map(l=>l.registration.registryKey));for(const l of t){const r=this.deps.registry.getByRegistryKey(l.registryKey);if(!r?.binding||!f(this.deps.registry,l)||r.binding.roomId!==e||r.binding.status!=="active"&&r.binding.status!=="paused"||a.agentsByIdentityId?.[r.binding.agentIdentityId]?.configurationPending||BigInt(i.sequence)<=BigInt(r.binding.lastActivatedSequence))continue;const I=c.get(r.registryKey)??[...r.binding.mentionContext??[]];if(c.set(r.registryKey,I),s.has(r.registryKey)){const p=(i.activationCause?.hopCount??0)+(i.author.principalType==="agent"?1:0),h=i.activationCause?.rootCauseMessageId??i.id;if(p>N)this.audit(r,i.id,h,p,"suppressed","max_hops");else{const w=this.deps.registry.claimActivation({registryKey:r.registryKey,causeMessageId:i.id,windowMs:O,maxActivations:F,pauseMs:V,now:this.deps.now?.()});if(!w.allowed){w.newlyPaused&&this.audit(r,i.id,h,p,"suppressed","rate_limited"),m.push({registryKey:r.registryKey,sequence:i.sequence});continue}const b=this.deps.observations.getBySource(r);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const M=v([i]),C=r.binding.activationMode==="mention_only"?Q([...I.map(_=>_.deliveryText),M]):M;this.latestContexts.set(r.registryKey,a),d.push({registration:r,input:{params:{sessionId:r.nianSessionId,text:C,agentType:y(r.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:r.sourceSessionKey,queueMessageId:`room-activation-${r.binding.bindingId}-${i.id}`,clientMessageId:`room-activation-${r.binding.bindingId}-${i.id}`,roomMessageCreatedAt:i.createdAt},context:{registryKey:r.registryKey,causeMessageId:i.id,rootCauseMessageId:h,sequence:i.sequence,hopCount:p}}}),r.binding.activationMode==="mention_only"&&c.set(r.registryKey,[])}}else r.binding.activationMode==="mention_only"&&i.kind!=="system"&&i.author.membershipId!==r.binding.agentMembershipId&&(I.some(p=>p.sequence===i.sequence)||I.push({sequence:i.sequence,deliveryText:v([i])}));m.push({registryKey:r.registryKey,sequence:i.sequence})}}await this.deps.enqueueConversationBatch(d.map(i=>i.input));for(const i of u)this.deps.registry.retireLegacyActivationState(i,this.deps.now?.()??new Date);for(const[i,s]of c)this.deps.registry.getByRegistryKey(i)?.lifecycleState!=="closed"&&this.deps.registry.replaceMentionContext(i,s,this.deps.now?.()??new Date);for(const i of d)this.audit(i.registration,i.input.context.causeMessageId,i.input.context.rootCauseMessageId,i.input.context.hopCount,"queued","conversation_queue");for(const i of m)this.deps.registry.getByRegistryKey(i.registryKey)?.lifecycleState!=="closed"&&this.deps.registry.advanceCursor({registryKey:i.registryKey,cursor:"activated",sequence:i.sequence})}async prepareConversationTurn(e){const o=e.at(-1);if(!o)throw new Error("managed_room_queue_context_missing");const t=this.deps.registry.getByRegistryKey(o.registryKey);if(!t)throw new Error("managed_room_binding_unavailable");const a=await this.prepareManagedTurn(t,o.causeMessageId);return this.audit(t,o.causeMessageId,o.rootCauseMessageId,o.hopCount,"activated","conversation_queue_dispatch"),a}async prepareDirectConversationTurn(e,o={}){const t=o.roomOrigin?this.deps.registry.getByRegistryKey(o.roomOrigin.registryKey):this.deps.registry.getByNianSessionId(e);return t?.nianSessionId!==e||!t||o.roomOrigin&&(t.registrationEpoch!==o.roomOrigin.registrationEpoch||t.binding?.bindingId!==o.roomOrigin.bindingId||t.binding.roomId!==o.roomOrigin.roomId)?null:this.prepareManagedTurn(t,void 0,o)}async prepareManagedTurn(e,o,t){if(!f(this.deps.registry,e)||!e.binding||e.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const a=this.deps.loadContext?await this.deps.loadContext(e):this.latestContexts.get(e.registryKey);if(!a)throw new Error("managed_room_prompt_context_unavailable");const d=a.agentsByIdentityId?.[e.binding.agentIdentityId];if(d?.configurationPending)throw new Error("managed_agent_configuration_pending");if(!d?.permissionPolicy)throw new Error("managed_agent_policy_missing");const m=d.sessionMode==="manager"?"manager":"standard",c=m==="manager"?{...d.managerConfig,defaultWorkerAgentType:d.managerConfig?.defaultWorkerAgentType??y(e.runtimeAdapter),defaultWorkerModelId:d.managerConfig?.defaultWorkerModelId??null}:null,u=this.deps.observations.getBySource(e);if(!u?.workspacePath||u.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const g=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:u.workspacePath}):await U({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:u.workspacePath});if(!f(this.deps.registry,e))throw new Error("managed_room_binding_unavailable");const i=H(a,e,g),s=this.deps.issueManagedRoomContext(e.registryKey,o,t?.allowedRoomActions,t?.propagateWorkerRoomOrigin);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(s))throw new Error("managed_room_context_unavailable");return{agentType:y(e.runtimeAdapter),workDir:g.adapterPolicy.workDir,agentSessionId:e.sourceSessionKey,systemPrompt:t?G({managerEvent:t.managerEvent===!0,roomSendAllowed:t.allowedRoomActions?.includes("send")===!0,agentSystemPrompt:a.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId],managedAgentPolicy:g,runtimeAdapter:y(e.runtimeAdapter)}):i.systemPrompt,sessionMode:m,managerConfig:c,managerDefaultWorkerAgentType:c?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:c?.defaultWorkerModelId??null,roomContextRegistryKey:e.registryKey,managedRoomContextToken:s,managedAgentPolicy:g,managedRoomPromptSnapshot:i.snapshot}}audit(e,o,t,a,d,m){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:o,rootCauseMessageId:t,hopCount:a,outcome:d,reason:m,at:this.deps.now?.()})}}function v(n){const e=n.map(o=>{const t=o.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",a=o.attachments.length>0?`
|
|
1
|
+
import{isAvailableBuiltinAgentType as k,managedAgentExecutionMode as P}from"@shennian/wire";import{getBuiltinAgentVersion as S,resolveBuiltinCommand as B}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as K,resolveCustomRuntimeExecutableFingerprint as $}from"../agents/custom-managed-runtime.js";import{loadConfig as x}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as W}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as T}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as R}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as q}from"./activation-policy.js";import{buildManagedAgentPermissionPrompt as E,composeManagedRoomPrompt as D}from"./managed-prompt-composer.js";import{isCurrentRegistration as f}from"./registration-authority.js";const N=4,O=6e4,F=6,V=5*6e4;class ce{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,o,t,a={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],m=[],c=new Map,u=new Set,g=[...o].sort((i,s)=>J(i.sequence,s.sequence));for(const i of t){const s=this.deps.registry.getByRegistryKey(i.registryKey);!s?.binding||!f(this.deps.registry,i)||s.binding.roomId!==e||s.binding.status!=="active"&&s.binding.status!=="paused"&&s.binding.activationQueue.length}for(const i of t){const s=this.deps.registry.getByRegistryKey(i.registryKey);if(!s?.binding||!f(this.deps.registry,i)||s.binding.roomId!==e||s.binding.activationQueue.length===0&&!s.binding.activeActivation||(u.add(s.registryKey),s.binding.status!=="active"||s.binding.activationMode==="paused")||s.binding.activationQueue.length===0)continue;const l=this.deps.observations.getBySource(s);if(!l?.workspacePath||l.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(s.registryKey,a);for(const r of s.binding.activationQueue)d.push({registration:s,input:{params:{sessionId:s.nianSessionId,text:r.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:y(s.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:l.workspacePath,agentSessionId:s.sourceSessionKey,queueMessageId:`room-activation-${s.binding.bindingId}-${r.causeMessageId}`,clientMessageId:`room-activation-${s.binding.bindingId}-${r.causeMessageId}`},context:{registryKey:s.registryKey,causeMessageId:r.causeMessageId,rootCauseMessageId:r.rootCauseMessageId,sequence:r.sequence,hopCount:r.hopCount}}})}for(const i of g){const s=new Set(q(i,t).map(l=>l.registration.registryKey));for(const l of t){const r=this.deps.registry.getByRegistryKey(l.registryKey);if(!r?.binding||!f(this.deps.registry,l)||r.binding.roomId!==e||r.binding.status!=="active"&&r.binding.status!=="paused"||a.agentsByIdentityId?.[r.binding.agentIdentityId]?.configurationPending||BigInt(i.sequence)<=BigInt(r.binding.lastActivatedSequence))continue;const I=c.get(r.registryKey)??[...r.binding.mentionContext??[]];if(c.set(r.registryKey,I),s.has(r.registryKey)){const p=(i.activationCause?.hopCount??0)+(i.author.principalType==="agent"?1:0),h=i.activationCause?.rootCauseMessageId??i.id;if(p>N)this.audit(r,i.id,h,p,"suppressed","max_hops");else{const w=this.deps.registry.claimActivation({registryKey:r.registryKey,causeMessageId:i.id,windowMs:O,maxActivations:F,pauseMs:V,now:this.deps.now?.()});if(!w.allowed){w.newlyPaused&&this.audit(r,i.id,h,p,"suppressed","rate_limited"),m.push({registryKey:r.registryKey,sequence:i.sequence});continue}const b=this.deps.observations.getBySource(r);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const M=v([i]),C=r.binding.activationMode==="mention_only"?Q([...I.map(_=>_.deliveryText),M]):M;this.latestContexts.set(r.registryKey,a),d.push({registration:r,input:{params:{sessionId:r.nianSessionId,text:C,agentType:y(r.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[r.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:r.sourceSessionKey,queueMessageId:`room-activation-${r.binding.bindingId}-${i.id}`,clientMessageId:`room-activation-${r.binding.bindingId}-${i.id}`,roomMessageCreatedAt:i.createdAt},context:{registryKey:r.registryKey,causeMessageId:i.id,rootCauseMessageId:h,sequence:i.sequence,hopCount:p}}}),r.binding.activationMode==="mention_only"&&c.set(r.registryKey,[])}}else r.binding.activationMode==="mention_only"&&i.kind!=="system"&&i.author.membershipId!==r.binding.agentMembershipId&&(I.some(p=>p.sequence===i.sequence)||I.push({sequence:i.sequence,deliveryText:v([i])}));m.push({registryKey:r.registryKey,sequence:i.sequence})}}await this.deps.enqueueConversationBatch(d.map(i=>i.input));for(const i of u)this.deps.registry.retireLegacyActivationState(i,this.deps.now?.()??new Date);for(const[i,s]of c)this.deps.registry.getByRegistryKey(i)?.lifecycleState!=="closed"&&this.deps.registry.replaceMentionContext(i,s,this.deps.now?.()??new Date);for(const i of d)this.audit(i.registration,i.input.context.causeMessageId,i.input.context.rootCauseMessageId,i.input.context.hopCount,"queued","conversation_queue");for(const i of m)this.deps.registry.getByRegistryKey(i.registryKey)?.lifecycleState!=="closed"&&this.deps.registry.advanceCursor({registryKey:i.registryKey,cursor:"activated",sequence:i.sequence})}async prepareConversationTurn(e){const o=e.at(-1);if(!o)throw new Error("managed_room_queue_context_missing");const t=this.deps.registry.getByRegistryKey(o.registryKey);if(!t)throw new Error("managed_room_binding_unavailable");const a=await this.prepareManagedTurn(t,o.causeMessageId);return this.audit(t,o.causeMessageId,o.rootCauseMessageId,o.hopCount,"activated","conversation_queue_dispatch"),a}async prepareDirectConversationTurn(e,o={}){const t=o.roomOrigin?this.deps.registry.getByRegistryKey(o.roomOrigin.registryKey):this.deps.registry.getByNianSessionId(e);return t?.nianSessionId!==e||!t||o.roomOrigin&&(t.registrationEpoch!==o.roomOrigin.registrationEpoch||t.binding?.bindingId!==o.roomOrigin.bindingId||t.binding.roomId!==o.roomOrigin.roomId)?null:this.prepareManagedTurn(t,void 0,o)}async prepareManagedTurn(e,o,t){if(!f(this.deps.registry,e)||!e.binding||e.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const a=this.deps.loadContext?await this.deps.loadContext(e):this.latestContexts.get(e.registryKey);if(!a)throw new Error("managed_room_prompt_context_unavailable");const d=a.agentsByIdentityId?.[e.binding.agentIdentityId];if(d?.configurationPending)throw new Error("managed_agent_configuration_pending");if(!d?.permissionPolicy)throw new Error("managed_agent_policy_missing");const m=d.sessionMode==="manager"?"manager":"standard",c=m==="manager"?{...d.managerConfig,defaultWorkerAgentType:d.managerConfig?.defaultWorkerAgentType??y(e.runtimeAdapter),defaultWorkerModelId:d.managerConfig?.defaultWorkerModelId??null}:null,u=this.deps.observations.getBySource(e);if(!u?.workspacePath||u.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const g=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:u.workspacePath}):await U({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:u.workspacePath});if(!f(this.deps.registry,e))throw new Error("managed_room_binding_unavailable");const i=H(a,e,g),s=this.deps.issueManagedRoomContext(e.registryKey,o,t?.allowedRoomActions,t?.propagateWorkerRoomOrigin);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(s))throw new Error("managed_room_context_unavailable");return{agentType:y(e.runtimeAdapter),effectiveModelId:a.effectiveModelsBySessionId?.[e.nianSessionId],workDir:g.adapterPolicy.workDir,agentSessionId:e.sourceSessionKey,systemPrompt:t?G({managerEvent:t.managerEvent===!0,roomSendAllowed:t.allowedRoomActions?.includes("send")===!0,agentSystemPrompt:a.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId],managedAgentPolicy:g,runtimeAdapter:y(e.runtimeAdapter)}):i.systemPrompt,sessionMode:m,managerConfig:c,managerDefaultWorkerAgentType:c?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:c?.defaultWorkerModelId??null,roomContextRegistryKey:e.registryKey,managedRoomContextToken:s,managedAgentPolicy:g,managedRoomPromptSnapshot:i.snapshot}}audit(e,o,t,a,d,m){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:o,rootCauseMessageId:t,hopCount:a,outcome:d,reason:m,at:this.deps.now?.()})}}function v(n){const e=n.map(o=>{const t=o.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",a=o.attachments.length>0?`
|
|
2
2
|
${o.attachments.map(L).join(`
|
|
3
3
|
`)}`:"";return{speaker:o.author.displayName,body:`${t}${a}`}});return A(e)}function L(n){return`\u9644\u4EF6\uFF1A${`${j(n.name)} \xB7 ${z(n.byteSize)}`}\uFF5C${n.mimeType}\uFF5CattachmentId=${n.attachmentId}`}function j(n){return String(n||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function z(n){const e=Number(n);if(!Number.isFinite(e)||e<0)return`${n} B`;if(e<1024)return`${e} B`;const o=["KB","MB","GB","TB"];let t=e,a=-1;do t/=1024,a+=1;while(t>=1024&&a<o.length-1);const d=t>=100?0:t>=10?1:2;return`${t.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${o[a]}`}function Q(n){return A(n.map(X))}function X(n){const e=n.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const o=n.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(o)return{speaker:o[1],body:o[2]};const t=n.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(t)return{speaker:t[1],body:t[2]};const a=n.match(/^([^\n]+)\n([\s\S]*)$/);return a?{speaker:a[1],body:a[2]}:{speaker:"\u7FA4\u6210\u5458",body:n.trim()}}function A(n){if(n.length===1){const[e]=n;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
|
|
4
4
|
${e.body}`}return`[\u7FA4\u6D88\u606F]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import x from"node:crypto";import
|
|
1
|
+
import x from"node:crypto";import i from"node:fs";import c from"node:path";const k=100*1024*1024;class p extends Error{code;constructor(e,a){super(a),this.code=e,this.name="RoomAttachmentUploadError"}}class O{deps;constructor(e){this.deps=e}async upload(e){if(e.files.length<1||e.files.length>10)throw s("room_upload_file_count");const a=i.realpathSync(e.workspacePath),o=[];for(const[n,r]of e.files.entries()){const l=c.resolve(a,r);if(i.lstatSync(l).isSymbolicLink())throw s("room_upload_invalid_file");const m=i.realpathSync(l),d=c.relative(a,m);if(d===".."||d.startsWith(`..${c.sep}`)||c.isAbsolute(d))throw s("room_upload_outside_workspace");if(L(d))throw s("room_upload_sensitive_file");const h=z(m),_=x.createHash("sha256").update(h).digest("hex"),f=c.basename(m),v=F(f),S=$(f,v),A=x.createHash("sha256").update(`${e.bindingId}\0${e.clientMessageId}\0${n}\0${_}`,"utf8").digest("hex"),y={machineToken:e.machineToken,roomId:e.roomId,bindingId:e.bindingId},u=await this.deps.actionClient.initializeAttachment({...y,clientUploadId:A,name:f,mediaKind:S,mimeType:v,byteSize:h.byteLength,sha256:_});let g;try{g=await(this.deps.fetch??fetch)(u.upload.url,{method:"PUT",headers:u.upload.headers,body:new Uint8Array(h),redirect:"error",signal:AbortSignal.timeout(12e4)})}catch{throw new p("room_upload_unavailable","Private attachment upload service is unavailable.")}if(!g.ok)throw new p(`room_upload_http_${g.status}`,"Private attachment upload failed.");const I=await this.deps.actionClient.completeAttachment({...y,attachmentId:u.attachment.id});o.push(I.attachment.attachmentId)}return o}}function z(t){const e=i.lstatSync(t);if(!b(e))throw s("room_upload_invalid_file");let a=null;try{a=i.openSync(t,i.constants.O_RDONLY|(i.constants.O_NOFOLLOW??0));const o=i.fstatSync(a);if(!b(o)||!w(e,o))throw s("room_upload_file_changed");const n=i.readFileSync(a),r=i.fstatSync(a),l=i.lstatSync(t);if(!w(o,r)||!w(o,l)||o.size!==r.size||o.mtimeMs!==r.mtimeMs||n.byteLength!==o.size)throw s("room_upload_file_changed");return n}catch(o){throw o instanceof p?o:s("room_upload_invalid_file")}finally{a!==null&&i.closeSync(a)}}function b(t){return t.isFile()&&t.nlink===1&&t.size>0&&t.size<=k}function w(t,e){return t.dev===e.dev&&t.ino===e.ino}function L(t){const a=t.replaceAll("\\","/").split("/").filter(Boolean).map(n=>n.toLowerCase()),o=a.at(-1)??"";return o===".env"||o.startsWith(".env.")||o===".netrc"||o===".npmrc"||o.endsWith(".key")||o.endsWith(".pem")||a.includes(".ssh")||a.includes(".aws")||a.includes(".azure")?!0:a.some((n,r)=>n===".config"&&a[r+1]==="gcloud")}function F(t){const e=c.extname(t).toLowerCase();return{".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".heic":"image/heic",".heif":"image/heif",".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4",".aac":"audio/aac",".ogg":"audio/ogg",".flac":"audio/flac",".pdf":"application/pdf",".txt":"text/plain",".md":"text/markdown",".json":"application/json",".csv":"text/csv",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation"}[e]??"application/octet-stream"}function $(t,e){return/^image\/hei[cf](?:-sequence)?$/.test(e)||/\.(?:heic|heif)$/i.test(t)?"file":e.startsWith("image/")?"image":e.startsWith("audio/")?"audio":"file"}function s(t){return new p(t,"Room attachment file is not allowed.")}export{p as RoomAttachmentUploadError,O as RoomAttachmentUploader,$ as attachmentMediaKind};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import M from"node:crypto";import g from"node:fs";import{DaemonIpcError as o}from"../daemon-ipc/protocol.js";import{ManagedRoomContextError as A}from"./managed-context.js";function L(e){const t=E(e);return{"managed-room.execute":a=>t(a)}}const
|
|
1
|
+
import M from"node:crypto";import g from"node:fs";import{DaemonIpcError as o}from"../daemon-ipc/protocol.js";import{ManagedRoomContextError as A}from"./managed-context.js";function L(e){const t=E(e);return{"managed-room.execute":a=>t(a)}}const C=1024*1024,O=5*1024*1024;function E(e){return async(t,a)=>{let h=null;try{if(a?.signal?.aborted)throw new o("managed_room_context_revoked","Managed Room context was revoked.");const n=N(t),c=w(n.context,"context",128),i=z(n.action);i==="send"&&(h=c);const d=e.getMachineCredentials().machineToken?.trim();if(!d)throw new o("machine_not_authorized","This machine is not authorized yet.");const{grant:f,registration:m,binding:u}=e.authority.resolve({token:c,action:S(i)?"manager":i==="schedule"?"status":i,machineCredential:d});if(i==="schedule"){if(!e.scheduleExecutor)throw new Error("schedules_unavailable");if((await e.actionClient.status({machineToken:d,roomId:u.roomId,bindingId:u.bindingId,scheduleAuthorization:!0,causeMessageId:f.activationCauseMessageId})).scheduleAuthorized!==!0)throw new Error("schedule_owner_authorization_required");return l(await e.scheduleExecutor(m.nianSessionId,n.operation,n))}if(S(i)){if(!e.managerExecutor)throw new o("managed_manager_unavailable","Managed Manager tools are unavailable.");const{context:r,action:s,managerSessionId:T,...x}=n;return await e.managerExecutor({managerSessionId:m.nianSessionId,roomOrigin:f.allowedActions.has("send")&&f.propagateWorkerRoomOrigin?{registryKey:m.registryKey,registrationEpoch:m.registrationEpoch,bindingId:u.bindingId,roomId:u.roomId}:void 0,action:i,params:x})}const p={machineToken:d,roomId:u.roomId,bindingId:u.bindingId};if(i==="status"){const r=await e.actionClient.status({...p,signal:a?.signal});return l({room:{name:r.room.name,status:r.room.status,memberCount:r.room.memberCount??r.room.members?.length??0,members:r.room.members??[]},agent:r.agent??null,activationMode:r.activationMode})}if(i==="read"){const r=v(n.before,"before"),s=v(n.after,"after");if(r&&s)throw new o("ambiguous_sequence_cursor","before and after are mutually exclusive.");const T=$(n.limit),x=await e.actionClient.read({...p,beforeSequence:r,afterSequence:s,limit:T,signal:a?.signal});return l({messages:x.messages.map(I)})}if(i==="attachment.get"){const r=w(n.attachmentId,"attachmentId",191),s=await e.attachmentCache.materialize({...p,attachmentId:r,contextKey:c,signal:a?.signal});return a?.attachmentOutput==="codex-inline"?l({codexContentItems:k(s)}):l(s)}if(i==="pause"){const r=w(n.reason,"reason",500),s=await e.actionClient.pause({...p,reason:r,signal:a?.signal});return e.onPaused?.({registryKey:m.registryKey,sessionId:m.nianSessionId}),e.authority.revokeToken(c),l({status:s.status,activationMode:s.activationMode,reason:r,message:I(s.message),idempotent:s.idempotent})}const y=typeof n.text=="string"?n.text.trim():"",_=Array.isArray(n.files)?n.files.map(r=>w(r,"file",4096)):[];if(!y&&_.length===0)throw new o("room_message_empty","Provide text or at least one file.");if(_.length>10)throw new o("room_attachments_too_many","A message can contain at most 10 files.");if(_.length)throw new o("managed_room_attachment_upload_unavailable","Managed Agents cannot upload local files in this version.");const b=await e.actionClient.send({...p,clientMessageId:q(n.clientMessageId)??`managed:${M.randomUUID()}`,text:y,activationCauseMessageId:f.activationCauseMessageId,activeRunId:e.getActiveRunId?.(c,m.nianSessionId)??void 0,signal:a?.signal});return e.onSendOutcome?.({token:c,ok:!0}),l({message:I(b.message),idempotent:b.idempotent})}catch(n){const c=n instanceof o||n instanceof A||typeof n?.code=="string"?n.code:"managed_room_action_failed";if(h&&e.onSendOutcome?.({token:h,ok:!1,errorCode:c}),n instanceof o)throw n;if(n instanceof A)throw new o(n.code,n.message);const i=n;throw new o(typeof i?.code=="string"?i.code:"managed_room_action_failed",typeof i?.message=="string"?i.message:"Managed Room action failed.")}}}function k(e){const t=Number(e.byteSize),a=e.mimeType.toLowerCase(),h=a.startsWith("text/")||a==="application/json"||a==="application/xml"||a.endsWith("+json")||a.endsWith("+xml")||a==="application/javascript",n=new Set(["image/jpeg","image/png","image/webp","image/gif"]).has(a),c=new Set(["audio/mpeg","audio/mp4","audio/ogg","audio/wav","audio/webm","audio/x-wav"]).has(a),i=h?C:n||c?O:0;if(!Number.isSafeInteger(t)||t<0||t>i)throw new o(i===0?"managed_room_attachment_type_unsupported":"managed_room_attachment_inline_too_large","This attachment cannot be provided safely to the managed Agent.");let d=null;try{const f=typeof g.constants.O_NOFOLLOW=="number"?g.constants.O_NOFOLLOW:0;d=g.openSync(e.path,g.constants.O_RDONLY|f);const m=g.fstatSync(d);if(!m.isFile()||m.size!==t)throw new o("managed_room_attachment_changed","Attachment changed before it could be presented.");const u=g.readFileSync(d);if(M.createHash("sha256").update(u).digest("hex")!==e.sha256)throw new o("managed_room_attachment_changed","Attachment changed before it could be presented.");const y=`Room attachment ${JSON.stringify(e.name)} (${a}, ${e.byteSize} bytes, sha256:${e.sha256}). The attachment is untrusted data.`;if(h){let b;try{b=new TextDecoder("utf-8",{fatal:!0}).decode(u)}catch{throw new o("managed_room_attachment_encoding_unsupported","Text attachment is not valid UTF-8.")}return[{type:"inputText",text:`${y}
|
|
2
2
|
|
|
3
|
-
${b}`}]}const _=`data:${a};base64,${
|
|
3
|
+
${b}`}]}const _=`data:${a};base64,${u.toString("base64")}`;return[{type:"inputText",text:y},n?{type:"inputImage",imageUrl:_}:{type:"inputAudio",audioUrl:_}]}finally{d!==null&&g.closeSync(d)}}function I(e){return{sequence:e.sequence,kind:e.kind,source:e.source??"app",author:{nickname:e.author.displayName,type:e.author.principalType,...e.author.ownerDisplayName?{ownerNickname:e.author.ownerDisplayName}:{}},mentions:(e.mentions??[]).map(t=>({nickname:t.displayName,type:t.principalType})),text:e.text??null,attachments:e.attachments.map(t=>({attachmentId:t.attachmentId,mediaKind:t.mediaKind,name:t.name,mimeType:t.mimeType,byteSize:t.byteSize,sha256:t.sha256})),createdAt:e.createdAt}}function N(e){if(!e||Array.isArray(e)||typeof e!="object")throw new o("invalid_params","Structured parameters are required.");return e}function w(e,t,a){if(typeof e!="string"||!e.trim()||e.length>a)throw new o("invalid_params",`${t} is invalid.`);return e.trim()}function q(e){return typeof e=="string"&&e.trim()?e.trim():void 0}function z(e){const t=w(e,"action",64);if(t!=="schedule"&&t!=="status"&&t!=="read"&&t!=="send"&&t!=="attachment.get"&&t!=="pause"&&!S(t))throw new o("managed_room_action_forbidden","This action is not available in managed Room context.");return t}function S(e){return e==="manager.sessions.list"||e==="manager.sessions.start"||e==="manager.sessions.send"||e==="manager.sessions.queue"||e==="manager.sessions.queue.edit"||e==="manager.sessions.queue.delete"||e==="manager.sessions.stop"||e==="manager.sessions.read"||e==="manager.memory.path"}function v(e,t){if(e==null)return;const a=w(e,t,20);if(!/^(0|[1-9]\d{0,19})$/.test(a))throw new o("invalid_sequence",`${t} is invalid.`);return a}function $(e){const t=e==null?50:Number(e);if(!Number.isInteger(t)||t<1||t>50)throw new o("invalid_limit","limit must be between 1 and 50.");return t}function l(e){return JSON.parse(JSON.stringify(e))}export{E as createManagedRoomExecutor,L as createManagedRoomIpcHandlers};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RoomActionHttpError as y}from"./room-action-client.js";import p from"node:crypto";import{isCurrentRegistration as h,matchesRoomOwner as f}from"./registration-authority.js";class
|
|
1
|
+
import{RoomActionHttpError as y}from"./room-action-client.js";import p from"node:crypto";import{isCurrentRegistration as h,matchesRoomOwner as f}from"./registration-authority.js";class B{deps;timer=null;running=!1;started=!1;idleStreak=0;sawActivity=!1;constructor(i){this.deps=i}start(){this.started||(this.started=!0,this.idleStreak=0,this.sawActivity=!1,this.arm(this.deps.initialDelayMs??0))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}async deliverLive(i){const n=this.deps.registry.list().filter(s=>s.binding?.roomId!==i.roomId||s.binding.status!=="active"||s.binding.activationMode==="paused"||!h(this.deps.registry,s)?!1:BigInt(i.sequence)>BigInt(s.binding.lastDeliveredSequence));if(n.length===0)return{accepted:!1,registrationCount:0};this.sawActivity=!0,this.deps.onMessages&&await this.deps.onMessages(i.roomId,[i],n,{roomName:"\u5F53\u524D\u7FA4\u804A"});for(const s of n){const o=this.deps.registry.getByRegistryKey(s.registryKey);o?.binding?.status==="active"&&o.binding.activationMode!=="paused"&&BigInt(i.sequence)>BigInt(o.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:o.registryKey,cursor:"delivered",sequence:i.sequence})}return{accepted:!0,registrationCount:n.length}}async runOnce(){if(this.running)return;const i=this.deps.getMachineCredentials().machineToken?.trim();if(i){this.running=!0;try{for(const s of this.deps.registry.list())s.lifecycleState!=="closed"&&this.deps.isSessionDeleted?.(s.nianSessionId)&&this.retire(s);const n=v(this.deps.registry.list());for(const[s,o]of n)await this.syncRoom(s,o,i)}finally{this.running=!1}}}async syncRoom(i,n,s){const o=n.filter(t=>t.binding&&(t.binding.status==="active"||t.binding.status==="paused"));if(o.length===0)return;let g=0,c=null,r=null;for(const t of o){if(!h(this.deps.registry,t))continue;const a=t.binding,u={machineToken:s,roomId:i,bindingId:a.bindingId};try{const e=await this.deps.actionClient.status(u);if(!h(this.deps.registry,t))continue;if((a.status!==e.status||a.activationMode!==e.activationMode)&&(this.sawActivity=!0),!f(t,e)||this.deps.isSessionDeleted?.(t.nianSessionId)){this.retire(t);continue}e.activationCapability==="managed"&&e.managedWorkspacePath&&!this.deps.observations?.getBySource(t)&&this.deps.observations?.observe({machineId:t.machineId,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:l(t.hostProcessIdentity),workspaceHash:l(e.managedWorkspacePath),workspacePath:e.managedWorkspacePath,lifecycleState:"active",eventName:"ManagedSync"});const m=this.deps.registry.setBinding({registryKey:t.registryKey,bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:e.activationMode});if((e.status==="paused"||e.activationMode==="paused")&&this.deps.onBindingPaused?.(m),e.status==="detached"){g+=1;continue}if(e.status==="pending_approval")continue;c??=u,r??={roomName:e.room.name,memberCount:e.room.memberCount,members:e.room.members,agentsByIdentityId:{},agentSystemPromptsByIdentityId:{}},e.effectiveModelId!==void 0&&(r.effectiveModelsBySessionId??={},r.effectiveModelsBySessionId[t.nianSessionId]=e.effectiveModelId),e.agent&&(r.agentsByIdentityId[e.agentIdentityId]=e.agent,r.agentSystemPromptsByIdentityId[e.agentIdentityId]=e.agent.systemPrompt??"")}catch(e){if(I(e)){this.deps.registry.detachBinding(t.registryKey),this.sawActivity=!0,g+=1;continue}return}}if(c){const t=n.filter(a=>h(this.deps.registry,a)).map(a=>this.deps.registry.getByRegistryKey(a.registryKey)??a);await this.pullPages(c,i,t,r);return}}async pullPages(i,n,s,o){if(s.length===0)return;const g=this.deps.maxPagesPerRun??100;let c=s.map(r=>r.binding?.lastDeliveredSequence??"0").reduce((r,t)=>BigInt(r)<BigInt(t)?r:t);for(let r=0;r<g;r+=1){const t=await this.deps.actionClient.read({...i,afterSequence:c,limit:100});t.messages.length>0&&(this.sawActivity=!0);const a=s.filter(u=>h(this.deps.registry,u));if(a.length===0)return;this.deps.onMessages&&await this.deps.onMessages(n,t.messages,a,o??{roomName:t.roomName});for(const u of a){const e=this.deps.registry.getByRegistryKey(u.registryKey);e?.binding&&(e.binding.status==="active"||e.binding.status==="paused")&&BigInt(t.nextSequence)>BigInt(e.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:u.registryKey,cursor:"delivered",sequence:t.nextSequence})}if(!t.hasMore)return;if(t.nextSequence===c)throw new y("room_sync_no_progress","Room mirror synchronization made no progress.");c=t.nextSequence}}retire(i){this.sawActivity=!0;const n=this.deps.registry.updateLifecycle(i.registryKey,"closed");this.deps.onBindingPaused?.(n)}arm(i){if(!this.started||this.timer)return;const n=this.deps.setTimeout??setTimeout;this.timer=n(()=>{this.timer=null,this.runOnce().catch(()=>{console.warn("[room.sync] delivery failed; retrying on next poll")}).finally(()=>{this.idleStreak=this.sawActivity?0:Math.min(this.idleStreak+1,10),this.sawActivity=!1,this.started&&this.arm(this.nextDelayMs())})},i),this.timer.unref?.()}nextDelayMs(){const i=this.deps.intervalMs??5e3,n=Math.max(i,this.deps.maxIntervalMs??3e4),s=Math.min(n,i*2**Math.max(0,this.idleStreak-1));return Math.round(s*(.8+.4*(this.deps.random??Math.random)()))}}function l(d){return p.createHash("sha256").update(d,"utf8").digest("hex")}function v(d){const i=new Map;for(const n of d){if(!n.binding||n.lifecycleState==="closed")continue;const s=i.get(n.binding.roomId)??[];s.push(n),i.set(n.binding.roomId,s)}return i}function I(d){return d instanceof y&&(d.code==="room_binding_unavailable"||d.code==="room_action_http_403")}export{B as RoomMirrorSyncService};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAvailableAgentType as j,parseManagedAgentPermissionPolicy as M}from"@shennian/wire";import _ from"node:path";import{RoomActionHttpError as c}from"./room-action-client-types.js";export*from"./room-action-client-types.js";class z{fetchImpl;baseUrl;constructor(e,t=fetch){this.fetchImpl=t,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/operator/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({name:e.name}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);return $(n,e,this.baseUrl)}async resolveNativeSession(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/resolve-session`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);(!n||typeof n!="object")&&r();const o=n;return(typeof o.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(o.nianSessionId)||typeof o.created!="boolean")&&r(),o}async createRoom(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,name:e.name,agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);return T(n,e,this.baseUrl)}async joinRoom(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/join`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,inviteToken:e.inviteToken,message:e.message,agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);return N(n,e,this.baseUrl)}async status(e){const t=e.scheduleAuthorization?`?scheduleAuthorization=1${e.causeMessageId?`&causeMessageId=${encodeURIComponent(e.causeMessageId)}`:""}`:"",n=await this.request(e,`status${t}`);return q(n,e)}async getAttachmentCapability(e){const t=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!t||typeof t!="object")&&r();const n=t;return(n.attachmentId!==e.attachmentId||n.roomId!==e.roomId||typeof n.safeName!="string"||!I(n.safeName)||typeof n.mimeType!="string"||n.mimeType.length<1||n.mimeType.length>191||typeof n.byteSize!="string"||!m(n.byteSize)||typeof n.sha256!="string"||!/^[a-f0-9]{64}$/.test(n.sha256)||!S(n.downloadUrl)||typeof n.expiresAt!="string"||!Number.isFinite(Date.parse(n.expiresAt)))&&r(),n}async initializeAttachment(e){const t=await this.request(e,"attachments/init",{method:"POST",body:JSON.stringify({clientUploadId:e.clientUploadId,name:e.name,mediaKind:e.mediaKind,mimeType:e.mimeType,byteSize:e.byteSize,sha256:e.sha256})});(!t||typeof t!="object")&&r();const n=t,o=n.attachment,a=n.upload;return(!o||typeof o.id!="string"||o.roomId!==e.roomId||typeof o.name!="string"||!I(o.name)||o.mimeType!==e.mimeType||o.byteSize!==String(e.byteSize)||o.sha256!==e.sha256||o.status!=="pending"&&o.status!=="uploaded"||!a||a.method!=="PUT"||!S(a.url)||!a.headers||typeof a.headers!="object"||a.headers["content-type"]!==e.mimeType||a.headers["x-oss-meta-sha256"]!==e.sha256||typeof a.expiresAt!="string"||!Number.isFinite(Date.parse(a.expiresAt))||Date.parse(a.expiresAt)<=Date.now()||typeof n.idempotent!="boolean")&&r(),n}async completeAttachment(e){const t=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!t||typeof t!="object")&&r();const n=t;typeof n.idempotent!="boolean"&&r();const o=n.attachment;return(!o||o.attachmentId!==e.attachmentId||o.mediaKind!=="image"&&o.mediaKind!=="audio"&&o.mediaKind!=="file"||typeof o.name!="string"||typeof o.mimeType!="string"||typeof o.byteSize!="string"||!m(o.byteSize)||typeof o.sha256!="string"||!/^[a-f0-9]{64}$/.test(o.sha256)||o.status!=="ready")&&r(),{attachment:o,idempotent:n.idempotent}}async read(e){if(e.beforeSequence&&e.afterSequence)throw new c("ambiguous_sequence_cursor","beforeSequence and afterSequence are mutually exclusive.");const t=new URLSearchParams({limit:String(e.limit)});e.beforeSequence&&t.set("before",e.beforeSequence),e.afterSequence&&t.set("after",e.afterSequence);const n=await this.request(e,`messages?${t}`);(!n||typeof n!="object")&&r();const o=n;return(!Array.isArray(o.messages)||typeof o.nextSequence!="string"||!m(o.nextSequence)||typeof o.hasMore!="boolean"||typeof o.historyFromSequence!="string"||!m(o.historyFromSequence)||typeof o.roomName!="string")&&r(),{messages:o.messages.map(s=>h(s,e.roomId)),nextSequence:o.nextSequence,hasMore:o.hasMore,historyFromSequence:o.historyFromSequence,roomName:o.roomName}}async send(e){const t=await this.request(e,"messages",{method:"POST",body:JSON.stringify({clientMessageId:e.clientMessageId,text:e.text,mentionMembershipIds:e.mentionMembershipIds,replyToMessageId:e.replyToMessageId,activationCauseMessageId:e.activationCauseMessageId,attachmentIds:e.attachmentIds})});(!t||typeof t!="object")&&r();const n=t;return typeof n.idempotent!="boolean"&&r(),{message:h(n.message,e.roomId),idempotent:n.idempotent}}async mode(e){const t=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!t||typeof t!="object")&&r();const n=t;return(n.bindingId!==e.bindingId||n.roomId!==e.roomId||n.status!=="active"&&n.status!=="paused"||!v(n.activationMode)||n.activationMode!==e.activationMode||typeof n.idempotent!="boolean")&&r(),n}async pause(e){const t=await this.request(e,"pause",{method:"POST",body:JSON.stringify({reason:e.reason})});(!t||typeof t!="object")&&r();const n=t;return(n.bindingId!==e.bindingId||n.roomId!==e.roomId||n.status!=="paused"||n.activationMode!=="paused"||typeof n.idempotent!="boolean")&&r(),{...n,message:h(n.message,e.roomId)}}async request(e,t,n={}){let o;const a=C(e.signal,1e4);try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${t}`,{...n,headers:{authorization:`Bearer ${e.machineToken}`,...n.body?{"content-type":"application/json"}:{}},signal:a.signal})}catch{throw e.signal?.aborted?new c("managed_room_context_revoked","Managed Room context was revoked."):new c("room_action_server_unavailable","Shennian Room service is unavailable.")}finally{a.dispose()}if(!o.ok){let s=`room_action_http_${o.status}`;try{const d=await o.json();typeof d.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(d.error)&&(s=d.error)}catch{}throw new c(s,"Shennian Room action failed.")}try{return await o.json()}catch{r()}}}function C(i,e){const t=new AbortController,n=()=>t.abort();i?.aborted?t.abort():i?.addEventListener("abort",n,{once:!0});const o=setTimeout(()=>t.abort(),e);return o.unref?.(),{signal:t.signal,dispose:()=>{clearTimeout(o),i?.removeEventListener("abort",n)}}}function q(i,e){(!i||typeof i!="object")&&r();const t=i,n=t.room,o=t.agent;(t.nianSessionId!==void 0&&t.nianSessionId!==null&&(typeof t.nianSessionId!="string"||!t.nianSessionId)||t.sessionRegistryKey!==void 0&&(typeof t.sessionRegistryKey!="string"||!/^[a-f0-9]{64}$/.test(t.sessionRegistryKey))||t.bindingId!==e.bindingId||t.roomId!==e.roomId||typeof t.agentIdentityId!="string"||t.agentMembershipId!==null&&typeof t.agentMembershipId!="string"||t.status!=="pending_approval"&&t.status!=="active"&&t.status!=="paused"&&t.status!=="detached"||!v(t.activationMode)||t.activationCapability!==void 0&&t.activationCapability!=="managed"&&t.activationCapability!=="idle_resume"&&t.activationCapability!=="notify_only"||t.managedWorkspacePath!==void 0&&t.managedWorkspacePath!==null&&(typeof t.managedWorkspacePath!="string"||t.managedWorkspacePath.length>4096||!_.isAbsolute(t.managedWorkspacePath))||typeof t.lastDeliveredSequence!="string"||!m(t.lastDeliveredSequence)||typeof t.lastActivatedSequence!="string"||!m(t.lastActivatedSequence)||t.appliedConfigurationVersion!==void 0&&(!Number.isSafeInteger(t.appliedConfigurationVersion)||Number(t.appliedConfigurationVersion)<1)||t.pendingConfigurationVersion!==void 0&&t.pendingConfigurationVersion!==null&&(!Number.isSafeInteger(t.pendingConfigurationVersion)||Number(t.pendingConfigurationVersion)<1)||!n||typeof n!="object"||o!==void 0&&(!o||typeof o!="object"))&&r(),(t.status==="pending_approval"&&t.agentMembershipId!==null||(t.status==="active"||t.status==="paused")&&typeof t.agentMembershipId!="string")&&r();const a=n,s=o,d=s?M(s.permissionPolicy):null,g=s?R(s.permissionConfirmation):null,w=s?Object.prototype.hasOwnProperty.call(s,"permissionConfirmation"):!1,l=a.members;return(s!==void 0&&(typeof s.displayName!="string"||!s.displayName.trim()||typeof s.description!="string"||s.sessionMode!=="standard"&&s.sessionMode!=="manager"||s.configurationVersion!==void 0&&(!Number.isSafeInteger(s.configurationVersion)||Number(s.configurationVersion)<1)||s.configurationPending!==void 0&&typeof s.configurationPending!="boolean"||!d||!w||s.permissionConfirmation!==null&&!g)||typeof a.name!="string"||typeof a.status!="string"||a.memberCount!==void 0&&(typeof a.memberCount!="number"||!Number.isInteger(a.memberCount)||a.memberCount<0)||typeof a.lastSequence!="string"||!m(a.lastSequence)||l!==void 0&&(!Array.isArray(l)||l.some(u=>{if(!u||typeof u!="object")return!0;const y=u;return typeof y.displayName!="string"||y.principalType!=="human"&&y.principalType!=="agent"})))&&r(),{...t,...s&&d?{agent:{...s,permissionPolicy:d,permissionConfirmation:g}}:{}}}function R(i){if(!i||typeof i!="object"||Array.isArray(i))return null;const e=i;return!Number.isSafeInteger(e.policyVersion)||Number(e.policyVersion)<1||typeof e.runtimeAdapter!="string"||!j(e.runtimeAdapter)||typeof e.runtimeVersion!="string"||!e.runtimeVersion.trim()||typeof e.platform!="string"||!e.platform.trim()||typeof e.executableFingerprint!="string"||!/^sha256:[a-f0-9]{64}$/.test(e.executableFingerprint)||typeof e.hostFullAccess!="boolean"||typeof e.limitedProtection!="boolean"||typeof e.confirmedAt!="string"||!Number.isFinite(Date.parse(e.confirmedAt))?null:{policyVersion:Number(e.policyVersion),runtimeAdapter:e.runtimeAdapter,runtimeVersion:e.runtimeVersion,platform:e.platform,executableFingerprint:e.executableFingerprint,executionMode:e.executionMode==="native-full"?"native-full":"restricted",hostFullAccess:e.hostFullAccess,limitedProtection:e.limitedProtection,confirmedAt:e.confirmedAt}}function h(i,e){(!i||typeof i!="object")&&r();const t=i;if((typeof t.id!="string"||t.roomId!==e||typeof t.sequence!="string"||!m(t.sequence)||!t.author||typeof t.author.membershipId!="string"||!Array.isArray(t.attachments)||typeof t.createdAt!="string")&&r(),t.activationCause!=null){const n=t.activationCause;(typeof n.causeMessageId!="string"||typeof n.rootCauseMessageId!="string"||!Number.isInteger(n.hopCount)||n.hopCount<1||n.hopCount>32)&&r()}return t}function T(i,e,t){(!i||typeof i!="object")&&r();const n=i,o=n.room,a=n.agentIdentity,s=n.binding;return(n.outcome!=="created"||!o||typeof o.id!="string"||o.name!==e.name||o.visibility!=="private"||o.joinPolicy!=="open"||!b(o.canonicalUrl,t,`/spaces/${o.id}`)||!P(o.inviteUrl,t,o.id)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==o.id||s.agentIdentityId!==a.id||typeof s.agentMembershipId!="string"||s.status!=="active"&&s.status!=="paused"||s.activationMode!==e.activationMode||typeof n.idempotent!="boolean")&&r(),n}function $(i,e,t){(!i||typeof i!="object")&&r();const n=i,o=n.room;return(n.outcome!=="created"||!o||typeof o.id!="string"||o.name!==e.name||o.visibility!=="private"||o.joinPolicy!=="open"||!b(o.canonicalUrl,t,`/spaces/${o.id}`))&&r(),n}function N(i,e,t){(!i||typeof i!="object")&&r();const n=i,o=n.room,a=n.agentIdentity,s=n.binding;return(n.outcome!=="joined"&&n.outcome!=="pending_approval"||!o||typeof o.id!="string"||typeof o.name!="string"||!b(o.canonicalUrl,t,`/spaces/${o.id}`)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==o.id||s.agentIdentityId!==a.id||s.activationMode!==e.activationMode||typeof n.idempotent!="boolean")&&r(),(n.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof n.joinRequestId!="string")||n.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||n.joinRequestId!==void 0))&&r(),n}function m(i){return/^(0|[1-9]\d{0,19})$/.test(i)}function I(i){return i.length<=255&&i===i.trim()&&i!=="."&&i!==".."&&!/[\\/\0-\x1f\x7f]/.test(i)}function S(i){if(typeof i!="string")return!1;try{const e=new URL(i);return e.protocol==="https:"&&!e.username&&!e.password&&!e.hash}catch{return!1}}function v(i){return i==="auto_follow"||i==="mention_only"||i==="paused"}function r(){throw new c("invalid_room_action_response","Invalid Room action response.")}async function p(i){try{return await i.json()}catch{r()}}function f(i,e){let t=`room_action_http_${i}`;if(e&&typeof e=="object"){const n=e.error;typeof n=="string"&&/^[a-z0-9_-]{1,80}$/.test(n)&&(t=n)}return new c(t,"Shennian Room action failed.")}function b(i,e,t){return A(i,e,n=>n.pathname===t)}function P(i,e,t){if(typeof i!="string")return!1;try{const n=new URL(i);return A(`${n.origin}${n.pathname}`,e,o=>o.pathname===`/spaces/${t}`)?n.searchParams.size===1&&/^[A-Za-z0-9_-]{32,256}$/.test(n.searchParams.get("invite")??"")&&!n.hash&&!n.username&&!n.password:!1}catch{return!1}}function A(i,e,t){if(typeof i!="string")return!1;try{const n=new URL(i),o=new URL(e),a=n.origin===o.origin,s=o.protocol==="http:"&&n.protocol==="http:"&&n.hostname===o.hostname,d=n.protocol==="https:"&&n.port===""&&n.hostname==="app.shennian.net";return(a||s||d)&&!n.username&&!n.password&&!n.search&&!n.hash&&t(n)}catch{return!1}}export{z as HttpRoomActionClient};
|
|
1
|
+
import{isAvailableAgentType as j,parseManagedAgentPermissionPolicy as M}from"@shennian/wire";import _ from"node:path";import{RoomActionHttpError as c}from"./room-action-client-types.js";export*from"./room-action-client-types.js";class z{fetchImpl;baseUrl;constructor(e,t=fetch){this.fetchImpl=t,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/operator/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({name:e.name}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);return $(n,e,this.baseUrl)}async resolveNativeSession(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/resolve-session`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);(!n||typeof n!="object")&&r();const o=n;return(typeof o.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(o.nianSessionId)||typeof o.created!="boolean")&&r(),o}async createRoom(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,name:e.name,agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);return T(n,e,this.baseUrl)}async joinRoom(e){let t;try{t=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/join`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,inviteToken:e.inviteToken,message:e.message,agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const n=await p(t);if(!t.ok)throw f(t.status,n);return N(n,e,this.baseUrl)}async status(e){const t=e.scheduleAuthorization?`?scheduleAuthorization=1${e.causeMessageId?`&causeMessageId=${encodeURIComponent(e.causeMessageId)}`:""}`:"",n=await this.request(e,`status${t}`);return R(n,e)}async getAttachmentCapability(e){const t=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!t||typeof t!="object")&&r();const n=t;return(n.attachmentId!==e.attachmentId||n.roomId!==e.roomId||typeof n.safeName!="string"||!I(n.safeName)||typeof n.mimeType!="string"||n.mimeType.length<1||n.mimeType.length>191||typeof n.byteSize!="string"||!m(n.byteSize)||typeof n.sha256!="string"||!/^[a-f0-9]{64}$/.test(n.sha256)||!S(n.downloadUrl)||typeof n.expiresAt!="string"||!Number.isFinite(Date.parse(n.expiresAt)))&&r(),n}async initializeAttachment(e){const t=await this.request(e,"attachments/init",{method:"POST",body:JSON.stringify({clientUploadId:e.clientUploadId,name:e.name,mediaKind:e.mediaKind,mimeType:e.mimeType,byteSize:e.byteSize,sha256:e.sha256})});(!t||typeof t!="object")&&r();const n=t,o=n.attachment,a=n.upload;return(!o||typeof o.id!="string"||o.roomId!==e.roomId||typeof o.name!="string"||!I(o.name)||o.mimeType!==e.mimeType||o.byteSize!==String(e.byteSize)||o.sha256!==e.sha256||o.status!=="pending"&&o.status!=="uploaded"||!a||a.method!=="PUT"||!S(a.url)||!a.headers||typeof a.headers!="object"||a.headers["content-type"]!==e.mimeType||a.headers["x-oss-meta-sha256"]!==e.sha256||typeof a.expiresAt!="string"||!Number.isFinite(Date.parse(a.expiresAt))||Date.parse(a.expiresAt)<=Date.now()||typeof n.idempotent!="boolean")&&r(),n}async completeAttachment(e){const t=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!t||typeof t!="object")&&r();const n=t;typeof n.idempotent!="boolean"&&r();const o=n.attachment;return(!o||o.attachmentId!==e.attachmentId||o.mediaKind!=="image"&&o.mediaKind!=="audio"&&o.mediaKind!=="file"||typeof o.name!="string"||typeof o.mimeType!="string"||typeof o.byteSize!="string"||!m(o.byteSize)||typeof o.sha256!="string"||!/^[a-f0-9]{64}$/.test(o.sha256)||o.status!=="ready")&&r(),{attachment:o,idempotent:n.idempotent}}async read(e){if(e.beforeSequence&&e.afterSequence)throw new c("ambiguous_sequence_cursor","beforeSequence and afterSequence are mutually exclusive.");const t=new URLSearchParams({limit:String(e.limit)});e.beforeSequence&&t.set("before",e.beforeSequence),e.afterSequence&&t.set("after",e.afterSequence);const n=await this.request(e,`messages?${t}`);(!n||typeof n!="object")&&r();const o=n;return(!Array.isArray(o.messages)||typeof o.nextSequence!="string"||!m(o.nextSequence)||typeof o.hasMore!="boolean"||typeof o.historyFromSequence!="string"||!m(o.historyFromSequence)||typeof o.roomName!="string")&&r(),{messages:o.messages.map(s=>h(s,e.roomId)),nextSequence:o.nextSequence,hasMore:o.hasMore,historyFromSequence:o.historyFromSequence,roomName:o.roomName}}async send(e){const t=await this.request(e,"messages",{method:"POST",body:JSON.stringify({clientMessageId:e.clientMessageId,text:e.text,mentionMembershipIds:e.mentionMembershipIds,replyToMessageId:e.replyToMessageId,activationCauseMessageId:e.activationCauseMessageId,activeRunId:e.activeRunId,attachmentIds:e.attachmentIds})});(!t||typeof t!="object")&&r();const n=t;return typeof n.idempotent!="boolean"&&r(),{message:h(n.message,e.roomId),idempotent:n.idempotent}}async mode(e){const t=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!t||typeof t!="object")&&r();const n=t;return(n.bindingId!==e.bindingId||n.roomId!==e.roomId||n.status!=="active"&&n.status!=="paused"||!v(n.activationMode)||n.activationMode!==e.activationMode||typeof n.idempotent!="boolean")&&r(),n}async pause(e){const t=await this.request(e,"pause",{method:"POST",body:JSON.stringify({reason:e.reason})});(!t||typeof t!="object")&&r();const n=t;return(n.bindingId!==e.bindingId||n.roomId!==e.roomId||n.status!=="paused"||n.activationMode!=="paused"||typeof n.idempotent!="boolean")&&r(),{...n,message:h(n.message,e.roomId)}}async request(e,t,n={}){let o;const a=C(e.signal,1e4);try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${t}`,{...n,headers:{authorization:`Bearer ${e.machineToken}`,...n.body?{"content-type":"application/json"}:{}},signal:a.signal})}catch{throw e.signal?.aborted?new c("managed_room_context_revoked","Managed Room context was revoked."):new c("room_action_server_unavailable","Shennian Room service is unavailable.")}finally{a.dispose()}if(!o.ok){let s=`room_action_http_${o.status}`;try{const d=await o.json();typeof d.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(d.error)&&(s=d.error)}catch{}throw new c(s,"Shennian Room action failed.")}try{return await o.json()}catch{r()}}}function C(i,e){const t=new AbortController,n=()=>t.abort();i?.aborted?t.abort():i?.addEventListener("abort",n,{once:!0});const o=setTimeout(()=>t.abort(),e);return o.unref?.(),{signal:t.signal,dispose:()=>{clearTimeout(o),i?.removeEventListener("abort",n)}}}function R(i,e){(!i||typeof i!="object")&&r();const t=i,n=t.room,o=t.agent;(t.nianSessionId!==void 0&&t.nianSessionId!==null&&(typeof t.nianSessionId!="string"||!t.nianSessionId)||t.sessionRegistryKey!==void 0&&(typeof t.sessionRegistryKey!="string"||!/^[a-f0-9]{64}$/.test(t.sessionRegistryKey))||t.bindingId!==e.bindingId||t.roomId!==e.roomId||typeof t.agentIdentityId!="string"||t.agentMembershipId!==null&&typeof t.agentMembershipId!="string"||t.status!=="pending_approval"&&t.status!=="active"&&t.status!=="paused"&&t.status!=="detached"||!v(t.activationMode)||t.activationCapability!==void 0&&t.activationCapability!=="managed"&&t.activationCapability!=="idle_resume"&&t.activationCapability!=="notify_only"||t.managedWorkspacePath!==void 0&&t.managedWorkspacePath!==null&&(typeof t.managedWorkspacePath!="string"||t.managedWorkspacePath.length>4096||!_.isAbsolute(t.managedWorkspacePath))||typeof t.lastDeliveredSequence!="string"||!m(t.lastDeliveredSequence)||typeof t.lastActivatedSequence!="string"||!m(t.lastActivatedSequence)||t.appliedConfigurationVersion!==void 0&&(!Number.isSafeInteger(t.appliedConfigurationVersion)||Number(t.appliedConfigurationVersion)<1)||t.pendingConfigurationVersion!==void 0&&t.pendingConfigurationVersion!==null&&(!Number.isSafeInteger(t.pendingConfigurationVersion)||Number(t.pendingConfigurationVersion)<1)||!n||typeof n!="object"||o!==void 0&&(!o||typeof o!="object"))&&r(),(t.status==="pending_approval"&&t.agentMembershipId!==null||(t.status==="active"||t.status==="paused")&&typeof t.agentMembershipId!="string")&&r();const a=n,s=o,d=s?M(s.permissionPolicy):null,g=s?q(s.permissionConfirmation):null,w=s?Object.prototype.hasOwnProperty.call(s,"permissionConfirmation"):!1,u=a.members;return(s!==void 0&&(typeof s.displayName!="string"||!s.displayName.trim()||typeof s.description!="string"||s.sessionMode!=="standard"&&s.sessionMode!=="manager"||s.configurationVersion!==void 0&&(!Number.isSafeInteger(s.configurationVersion)||Number(s.configurationVersion)<1)||s.configurationPending!==void 0&&typeof s.configurationPending!="boolean"||!d||!w||s.permissionConfirmation!==null&&!g)||typeof a.name!="string"||typeof a.status!="string"||a.memberCount!==void 0&&(typeof a.memberCount!="number"||!Number.isInteger(a.memberCount)||a.memberCount<0)||typeof a.lastSequence!="string"||!m(a.lastSequence)||u!==void 0&&(!Array.isArray(u)||u.some(l=>{if(!l||typeof l!="object")return!0;const y=l;return typeof y.displayName!="string"||y.principalType!=="human"&&y.principalType!=="agent"})))&&r(),{...t,...s&&d?{agent:{...s,permissionPolicy:d,permissionConfirmation:g}}:{}}}function q(i){if(!i||typeof i!="object"||Array.isArray(i))return null;const e=i;return!Number.isSafeInteger(e.policyVersion)||Number(e.policyVersion)<1||typeof e.runtimeAdapter!="string"||!j(e.runtimeAdapter)||typeof e.runtimeVersion!="string"||!e.runtimeVersion.trim()||typeof e.platform!="string"||!e.platform.trim()||typeof e.executableFingerprint!="string"||!/^sha256:[a-f0-9]{64}$/.test(e.executableFingerprint)||typeof e.hostFullAccess!="boolean"||typeof e.limitedProtection!="boolean"||typeof e.confirmedAt!="string"||!Number.isFinite(Date.parse(e.confirmedAt))?null:{policyVersion:Number(e.policyVersion),runtimeAdapter:e.runtimeAdapter,runtimeVersion:e.runtimeVersion,platform:e.platform,executableFingerprint:e.executableFingerprint,executionMode:e.executionMode==="native-full"?"native-full":"restricted",hostFullAccess:e.hostFullAccess,limitedProtection:e.limitedProtection,confirmedAt:e.confirmedAt}}function h(i,e){(!i||typeof i!="object")&&r();const t=i;if((typeof t.id!="string"||t.roomId!==e||typeof t.sequence!="string"||!m(t.sequence)||!t.author||typeof t.author.membershipId!="string"||!Array.isArray(t.attachments)||typeof t.createdAt!="string")&&r(),t.activationCause!=null){const n=t.activationCause;(typeof n.causeMessageId!="string"||typeof n.rootCauseMessageId!="string"||!Number.isInteger(n.hopCount)||n.hopCount<1||n.hopCount>32)&&r()}return t}function T(i,e,t){(!i||typeof i!="object")&&r();const n=i,o=n.room,a=n.agentIdentity,s=n.binding;return(n.outcome!=="created"||!o||typeof o.id!="string"||o.name!==e.name||o.visibility!=="private"||o.joinPolicy!=="open"||!b(o.canonicalUrl,t,`/spaces/${o.id}`)||!P(o.inviteUrl,t,o.id)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==o.id||s.agentIdentityId!==a.id||typeof s.agentMembershipId!="string"||s.status!=="active"&&s.status!=="paused"||s.activationMode!==e.activationMode||typeof n.idempotent!="boolean")&&r(),n}function $(i,e,t){(!i||typeof i!="object")&&r();const n=i,o=n.room;return(n.outcome!=="created"||!o||typeof o.id!="string"||o.name!==e.name||o.visibility!=="private"||o.joinPolicy!=="open"||!b(o.canonicalUrl,t,`/spaces/${o.id}`))&&r(),n}function N(i,e,t){(!i||typeof i!="object")&&r();const n=i,o=n.room,a=n.agentIdentity,s=n.binding;return(n.outcome!=="joined"&&n.outcome!=="pending_approval"||!o||typeof o.id!="string"||typeof o.name!="string"||!b(o.canonicalUrl,t,`/spaces/${o.id}`)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==o.id||s.agentIdentityId!==a.id||s.activationMode!==e.activationMode||typeof n.idempotent!="boolean")&&r(),(n.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof n.joinRequestId!="string")||n.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||n.joinRequestId!==void 0))&&r(),n}function m(i){return/^(0|[1-9]\d{0,19})$/.test(i)}function I(i){return i.length<=255&&i===i.trim()&&i!=="."&&i!==".."&&!/[\\/\0-\x1f\x7f]/.test(i)}function S(i){if(typeof i!="string")return!1;try{const e=new URL(i);return e.protocol==="https:"&&!e.username&&!e.password&&!e.hash}catch{return!1}}function v(i){return i==="auto_follow"||i==="mention_only"||i==="paused"}function r(){throw new c("invalid_room_action_response","Invalid Room action response.")}async function p(i){try{return await i.json()}catch{r()}}function f(i,e){let t=`room_action_http_${i}`;if(e&&typeof e=="object"){const n=e.error;typeof n=="string"&&/^[a-z0-9_-]{1,80}$/.test(n)&&(t=n)}return new c(t,"Shennian Room action failed.")}function b(i,e,t){return A(i,e,n=>n.pathname===t)}function P(i,e,t){if(typeof i!="string")return!1;try{const n=new URL(i);return A(`${n.origin}${n.pathname}`,e,o=>o.pathname===`/spaces/${t}`)?n.searchParams.size===1&&/^[A-Za-z0-9_-]{32,256}$/.test(n.searchParams.get("invite")??"")&&!n.hash&&!n.username&&!n.password:!1}catch{return!1}}function A(i,e,t){if(typeof i!="string")return!1;try{const n=new URL(i),o=new URL(e),a=n.origin===o.origin,s=o.protocol==="http:"&&n.protocol==="http:"&&n.hostname===o.hostname,d=n.protocol==="https:"&&n.port===""&&n.hostname==="app.shennian.net";return(a||s||d)&&!n.username&&!n.password&&!n.search&&!n.hash&&t(n)}catch{return!1}}export{z as HttpRoomActionClient};
|