shennian 0.3.24 → 0.3.26
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 +12 -12
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/room/attachment-upload.js +1 -1
- package/dist/src/room/managed-room-session-runtime.js +1 -1
- package/dist/src/room/managed-room-session.d.ts +1 -0
- package/dist/src/room/managed-room-session.js +3 -3
- package/dist/src/room/room-action-client.js +1 -1
- package/dist/src/room/room-action-ipc.d.ts +2 -2
- package/dist/src/room/room-action-ipc.js +1 -1
- package/node_modules/@shennian/wire/dist/project.d.ts +3 -0
- package/node_modules/@shennian/wire/dist/project.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -136,8 +136,8 @@
|
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
"file": "src/agents/workbuddy.js",
|
|
139
|
-
"beforeBytes":
|
|
140
|
-
"afterBytes":
|
|
139
|
+
"beforeBytes": 12451,
|
|
140
|
+
"afterBytes": 6549
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"file": "src/commands/agent.js",
|
|
@@ -416,8 +416,8 @@
|
|
|
416
416
|
},
|
|
417
417
|
{
|
|
418
418
|
"file": "src/room/attachment-upload.js",
|
|
419
|
-
"beforeBytes":
|
|
420
|
-
"afterBytes":
|
|
419
|
+
"beforeBytes": 4871,
|
|
420
|
+
"afterBytes": 2617
|
|
421
421
|
},
|
|
422
422
|
{
|
|
423
423
|
"file": "src/room/authorized-action-coordinator.js",
|
|
@@ -471,13 +471,13 @@
|
|
|
471
471
|
},
|
|
472
472
|
{
|
|
473
473
|
"file": "src/room/managed-room-session-runtime.js",
|
|
474
|
-
"beforeBytes":
|
|
475
|
-
"afterBytes":
|
|
474
|
+
"beforeBytes": 6393,
|
|
475
|
+
"afterBytes": 3293
|
|
476
476
|
},
|
|
477
477
|
{
|
|
478
478
|
"file": "src/room/managed-room-session.js",
|
|
479
|
-
"beforeBytes":
|
|
480
|
-
"afterBytes":
|
|
479
|
+
"beforeBytes": 17192,
|
|
480
|
+
"afterBytes": 8762
|
|
481
481
|
},
|
|
482
482
|
{
|
|
483
483
|
"file": "src/room/mirror-sync-service.js",
|
|
@@ -496,13 +496,13 @@
|
|
|
496
496
|
},
|
|
497
497
|
{
|
|
498
498
|
"file": "src/room/room-action-client.js",
|
|
499
|
-
"beforeBytes":
|
|
500
|
-
"afterBytes":
|
|
499
|
+
"beforeBytes": 23011,
|
|
500
|
+
"afterBytes": 11555
|
|
501
501
|
},
|
|
502
502
|
{
|
|
503
503
|
"file": "src/room/room-action-ipc.js",
|
|
504
|
-
"beforeBytes":
|
|
505
|
-
"afterBytes":
|
|
504
|
+
"beforeBytes": 16387,
|
|
505
|
+
"afterBytes": 7413
|
|
506
506
|
},
|
|
507
507
|
{
|
|
508
508
|
"file": "src/room/room-action-schema.js",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createInterface as g}from"node:readline";import{randomUUID as y}from"node:crypto";import{AgentAdapter as I,registerAgent as
|
|
1
|
+
import{createInterface as g}from"node:readline";import{randomUUID as y}from"node:crypto";import{AgentAdapter as I,registerAgent as S}from"./adapter.js";import{resolveBuiltinCommand as k,spawnAgentCommand as _}from"./command-spec.js";import{buildAgentProcessEnv as x}from"../agent-env.js";import{buildPlatformInstructions as w}from"./platform-instructions.js";import{ensureWorkBuddyRoomPluginBundle as E}from"../integrations/workbuddy-plugin-bundle.js";import{roomMcpHostToolName as P}from"../integrations/room-plugin-shared.js";const B=new Set(["minimal","low","medium","high","xhigh","max"]);function T(i){const t=i?.trim();return t&&t!=="auto"?t:void 0}function W(i){const t=i?.trim();if(t){if(B.has(t))return t;throw new Error(`Unsupported WorkBuddy reasoning effort "${t}". Supported values: minimal, low, medium, high, xhigh, max.`)}}class C extends I{type="workbuddy";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,s,e){this.workDir=s,this.seq=0,e&&(this.agentSessionId=e)}async send(t,s,e,o,a,n){await this.killProcess(),this.runId=y(),this.resetRunState();const u=n?.source==="room_activation",r=["-p",t,"--output-format","stream-json","--verbose","--permission-mode",u?"dontAsk":"auto","--setting-sources","user,project,local"],h=T(s);h&&r.push("--model",h);const m=E(),l=P("workbuddy");u?r.push("--allowedTools",l,"--tools",`NoDefer(${l})`,"--mcp-config",m.mcpFile,"--strict-mcp-config","--disallowedTools","mcp__shennian_room__shennian_agent","--max-turns","2"):r.push("--allowedTools",l,"--tools",`*,NoDefer(${l})`),r.push("--plugin-dir",m.pluginRoot);const p=this.shennianSessionId?.startsWith("room-managed-")&&!this.agentSessionId&&!u?null:w(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a);p&&r.push("--append-system-prompt",p);const f=W(e);f&&r.push("--effort",f),this.agentSessionId&&r.push("--resume",this.agentSessionId),this.spawnAndParse(r)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(t){const s=k("workbuddy");if(!s){this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}const e=_(s,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:x(this.extraEnv)});this.process=e,g({input:e.stdout}).on("line",n=>{if(n.trim())try{this.handleStreamEvent(JSON.parse(n))}catch{}});let a="";e.stderr?.on("data",n=>{a=`${a}${n.toString()}`.slice(-8e3)}),e.on("close",n=>{this.process===e&&(this.process=null,this.runPhase=null,this.handleProcessClose(n,a))}),e.on("error",n=>{if(this.process!==e)return;this.process=null,this.runPhase=null;const u=n.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:u?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":d(n.message)})})}handleStreamEvent(t){if(t.type==="system"&&t.subtype==="init"&&t.session_id){this.agentSessionId=t.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:t.session_id});return}if(t.type==="control_request"&&t.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:t.request.tool_name,args:t.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${t.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(t.type==="assistant"){const e=t.message?.content;if(e?.length)for(const o of e)this.handleContentBlock(o);else t.text&&this.emitText(t.text,!!t.thinking);return}if(t.type!=="result")return;if(t.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:t.name,result:c(t.content)});return}if(t.subtype==="error"||t.error){this.emitErrorIfOpen({state:"error",message:d(t.error||t.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&t.result&&this.emitText(t.result,!1),t.session_id&&(this.agentSessionId=t.session_id);const s=t.usage??t.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:s?{inputTokens:s.input_tokens??0,outputTokens:s.output_tokens??0}:void 0})}handleContentBlock(t){t.type==="text"&&t.text?this.emitText(t.text,!1):t.type==="thinking"&&t.thinking?this.emitText(t.thinking,!0):t.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:t.name,args:t.input})):t.type==="tool_result"&&this.emitEvent({state:"tool-result",name:t.name,result:c(t.content)})}emitText(t,s){this.runPhase=s?"thinking":"streaming_text";const e=!s&&this.hasEmittedText?`
|
|
2
2
|
|
|
3
|
-
`:"";this.emitEvent({state:"delta",text:e+t,thinking:s||void 0}),s||(this.hasEmittedText=!0)}handleProcessClose(t,s){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:d(s)||`WorkBuddy exited with code ${t}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(t))}emitEvent(t){this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const t=this.process;t&&(this.process=null,this.runPhase=null,t.kill("SIGTERM"),await new Promise(s=>{let e=!1;const o=()=>{e||(e=!0,s())};t.once("close",o),setTimeout(()=>{t.kill("SIGKILL"),o()},3e3).unref?.()}))}}function c(i){if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return"[unserializable WorkBuddy tool result]"}}function d(i){return i.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}
|
|
3
|
+
`:"";this.emitEvent({state:"delta",text:e+t,thinking:s||void 0}),s||(this.hasEmittedText=!0)}handleProcessClose(t,s){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:d(s)||`WorkBuddy exited with code ${t}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(t))}emitEvent(t){this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const t=this.process;t&&(this.process=null,this.runPhase=null,t.kill("SIGTERM"),await new Promise(s=>{let e=!1;const o=()=>{e||(e=!0,s())};t.once("close",o),setTimeout(()=>{t.kill("SIGKILL"),o()},3e3).unref?.()}))}}function c(i){if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return"[unserializable WorkBuddy tool result]"}}function d(i){return i.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}S("workbuddy",()=>new C);export{C as WorkBuddyAdapter,T as normalizeWorkBuddyModelId,W as normalizeWorkBuddyReasoningEffort,d as sanitizeWorkBuddyError};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import v from"node:crypto";import i from"node:fs";import e from"node:path";const k=100*1024*1024;class h extends Error{code;constructor(t,o){super(o),this.code=t,this.name="RoomAttachmentUploadError"}}class E{deps;constructor(t){this.deps=t}async upload(t){if(t.files.length<1||t.files.length>10)throw s("room_upload_file_count");const o=i.realpathSync(t.workspacePath),f=[];for(const[_,y]of t.files.entries()){const u=e.resolve(o,y);if(i.lstatSync(u).isSymbolicLink())throw s("room_upload_invalid_file");const n=i.realpathSync(u),c=e.relative(o,n);if(c===".."||c.startsWith(`..${e.sep}`)||e.isAbsolute(c))throw s("room_upload_outside_workspace");const r=i.lstatSync(n);if(!r.isFile()||r.size<1||r.size>k)throw s("room_upload_invalid_file");const d=i.readFileSync(n),g=v.createHash("sha256").update(d).digest("hex"),w=e.basename(n),l=A(w),b=l.startsWith("image/")?"image":l.startsWith("audio/")?"audio":"file",I=v.createHash("sha256").update(`${t.bindingId}\0${t.clientMessageId}\0${_}\0${g}`,"utf8").digest("hex"),x={machineToken:t.machineToken,roomId:t.roomId,bindingId:t.bindingId},p=await this.deps.actionClient.initializeAttachment({...x,clientUploadId:I,name:w,mediaKind:b,mimeType:l,byteSize:d.byteLength,sha256:g});let m;try{m=await(this.deps.fetch??fetch)(p.upload.url,{method:"PUT",headers:p.upload.headers,body:d,redirect:"error",signal:AbortSignal.timeout(12e4)})}catch{throw new h("room_upload_unavailable","Private attachment upload service is unavailable.")}if(!m.ok)throw new h(`room_upload_http_${m.status}`,"Private attachment upload failed.");const S=await this.deps.actionClient.completeAttachment({...x,attachmentId:p.attachment.id});f.push(S.attachment.attachmentId)}return f}}function A(a){const t=e.extname(a).toLowerCase();return{".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".heic":"image/heic",".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"}[t]??"application/octet-stream"}function s(a){return new h(a,"Room attachment file is not allowed.")}export{h as RoomAttachmentUploadError,E as RoomAttachmentUploader};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import h from"node:crypto";import{resolveShennianPath as y}from"../config/index.js";import{FileManagedRoomSessionOperationStore as l,ManagedRoomSessionOrchestrator as u}from"./managed-room-session.js";function
|
|
1
|
+
import h from"node:crypto";import{resolveShennianPath as y}from"../config/index.js";import{FileManagedRoomSessionOperationStore as l,ManagedRoomSessionOrchestrator as u}from"./managed-room-session.js";function p(n){return new u({operations:new l(n.operationsDirectory??y("runtime","managed-room-sessions")),createManagedSession:({creationKey:e,agent:o,workDir:t,modelId:a,initialMessage:i})=>n.sessionManager.createManagedRoomSession({creationKey:e,agentType:o,workDir:t,modelId:a,initialMessage:i}),waitForNativeSession:async({nianSessionId:e,runtimeAdapter:o,timeoutMs:t})=>({sourceSessionKey:(await n.sessionManager.waitForManagedNativeSession({nianSessionId:e,agentType:c(o),timeoutMs:t})).sourceSessionKey,hostProcessIdentity:n.hostProcessIdentity()}),registerManagedSession:async e=>{const o=n.getMachineCredentials();if(!o.machineId?.trim()||!o.machineToken?.trim())throw s("cli_not_signed_in","Sign in to the Shennian CLI first.");const t=n.registry.register({machineId:o.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,nianSessionId:e.nianSessionId,hostProcessIdentity:e.hostProcessIdentity,capabilities:e.capabilities});return n.observations.observe({machineId:o.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,hostContextHash:g(e.hostProcessIdentity),workspaceHash:g(e.workDir),workspacePath:e.workDir,lifecycleState:"active",eventName:"ManagedCreate"}),{registryKey:t.registryKey}},joinRegisteredSession:async e=>{const o=n.getMachineCredentials();if(!o.machineToken?.trim())throw s("cli_not_signed_in","Sign in to the Shennian CLI first.");const t=n.registry.getByRegistryKey(e.registryKey);if(!t)throw s("room_session_unavailable","Managed Agent Session is unavailable.");const a={machineToken:o.machineToken,bindingIntentId:e.bindingIntentId,nianSessionId:t.nianSessionId,runtimeAdapter:t.runtimeAdapter,sessionRegistryKey:t.registryKey,inviteToken:I(e.inviteUrl),agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:"managed",activationMode:e.activationMode};let i=null;for(let r=0;r<20;r+=1)try{n.sessionManager.publishManagedRoomSession({nianSessionId:t.nianSessionId,agentType:c(t.runtimeAdapter)}),i=await n.bindingClient.joinRoom(a);break}catch(d){if(!S(d,"nian_session_mismatch")||r===19)throw d;await new Promise(m=>setTimeout(m,100))}if(!i)throw s("managed_room_join_failed","Managed Room join failed.");return n.registry.setBinding({registryKey:t.registryKey,bindingId:i.binding.id,roomId:i.room.id,agentIdentityId:i.binding.agentIdentityId,agentMembershipId:i.binding.agentMembershipId,status:i.binding.status,activationMode:i.binding.activationMode}),{outcome:i.outcome,room:i.room,binding:i.binding,idempotent:i.idempotent}},allowedInviteOrigins:[new URL(n.serverUrl).origin]})}function c(n){return n==="claude_code"?"claude":n}function I(n){const e=new URL(n).pathname.split("/").filter(Boolean).at(-1);if(!e||!/^[A-Za-z0-9_-]{43}$/.test(e))throw s("invalid_managed_room_invite_url","Room invite URL is invalid.");return e}function g(n){return h.createHash("sha256").update(n,"utf8").digest("hex")}function s(n,e){return Object.assign(new Error(e),{code:n})}function S(n,e){return!!(n&&typeof n=="object"&&"code"in n&&n.code===e)}export{p as createManagedRoomSessionRuntime};
|
|
@@ -99,6 +99,7 @@ export type ManagedRoomSessionDependencies = {
|
|
|
99
99
|
registryKey: string;
|
|
100
100
|
inviteUrl: string;
|
|
101
101
|
agentAlias: string;
|
|
102
|
+
agentDescription: string;
|
|
102
103
|
activationMode: RoomActivationMode;
|
|
103
104
|
activationCapability: 'managed';
|
|
104
105
|
}): Promise<ManagedRoomJoinResult>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import S from"node:crypto";import a from"node:fs";import y from"node:path";class h extends Error{code;stage;operationId;phase;nianSessionId;constructor(i,o,t,r,n,s){super(o),this.code=i,this.stage=t,this.operationId=r,this.phase=n,this.nianSessionId=s,this.name="ManagedRoomSessionError"}}const j=/^[A-Za-z0-9_-]{8,96}$/,R=/^[A-Za-z0-9_.:@/-]{1,512}$/,w=/^[a-f0-9]{64}$/,
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&a.chmodSync(r,384),process.platform==="win32"&&a.existsSync(t)){const n=a.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw p();a.unlinkSync(t)}a.renameSync(r,t)}finally{try{a.unlinkSync(r)}catch{}}}filePath(i){return y.join(this.directory,`${i}.json`)}}function I(e,i){(!e||typeof e!="object"||Array.isArray(e))&&d("input");const o=e;Object.keys(o).find(T=>!
|
|
3
|
-
`)}function E(e){if(!e.nianSessionId||!e.result||e.phase!=="joined")throw u("operation_state");return{operationId:e.operationId,phase:"joined",nianSessionId:e.nianSessionId,result:
|
|
1
|
+
import S from"node:crypto";import a from"node:fs";import y from"node:path";class h extends Error{code;stage;operationId;phase;nianSessionId;constructor(i,o,t,r,n,s){super(o),this.code=i,this.stage=t,this.operationId=r,this.phase=n,this.nianSessionId=s,this.name="ManagedRoomSessionError"}}const j=/^[A-Za-z0-9_-]{8,96}$/,R=/^[A-Za-z0-9_.:@/-]{1,512}$/,w=/^[a-f0-9]{64}$/,D=/^[A-Za-z0-9_-]{43}$/,k=new Set(["inviteUrl","agent","workDir","alias","mode","modelId"]),K=new Set(["codex","claude","workbuddy"]),$=new Set(["auto_follow","mention_only","paused"]),N=["https://shennian.net"],P=3e4;class B{deps;mutexTails=new Map;constructor(i){this.deps=i}async execute(i,o){_(i);let t;const r=this.mutexTails.get(i)??Promise.resolve(),n=new Promise(l=>{t=l}),s=r.then(()=>n);this.mutexTails.set(i,s),await r;try{return await this.executeExclusive(i,o)}finally{t(),this.mutexTails.get(i)===s&&this.mutexTails.delete(i)}}async executeExclusive(i,o){let t;try{t=I(o,this.deps.allowedInviteOrigins??N)}catch(s){throw g(s,"validate_input",i,null)}const r=b(t);let n=this.deps.operations.load(i);if(n&&n.inputFingerprint!==r)throw new h("managed_room_operation_conflict","This operation ID was already used with different inputs.","validate_input",i,n.phase,n.nianSessionId);if(!n){const s=(this.deps.now?.()??new Date).toISOString();n={schemaVersion:1,operationId:i,inputFingerprint:r,input:t,runtimeAdapter:v(t.agent),phase:"initialized",nianSessionId:null,sourceSessionKey:null,hostProcessIdentity:null,registryKey:null,result:null,createdAt:s,updatedAt:s},this.save(n)}if(n.phase==="joined")return E(n);if(n.phase==="initialized")try{const s=await this.deps.createManagedSession({creationKey:`room-managed-session:${i}`,agent:t.agent,workDir:t.workDir,modelId:t.modelId,initialMessage:U()});c(s.nianSessionId,"nian_session_id"),n.nianSessionId=s.nianSessionId,n.phase="session_created",this.save(n)}catch(s){throw g(s,"create_session",i,n)}if(n.phase==="session_created")try{const s=await this.deps.waitForNativeSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,timeoutMs:this.deps.nativeSessionTimeoutMs??P});A(s.sourceSessionKey),c(s.hostProcessIdentity,"host_process_identity"),n.sourceSessionKey=s.sourceSessionKey,n.hostProcessIdentity=s.hostProcessIdentity,n.phase="native_session_ready",this.save(n)}catch(s){throw g(s,"wait_for_native_session",i,n)}if(n.phase==="native_session_ready")try{const s=await this.deps.registerManagedSession({nianSessionId:n.nianSessionId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostProcessIdentity:n.hostProcessIdentity,workDir:t.workDir,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}});if(!w.test(s.registryKey))throw u("registry_key");n.registryKey=s.registryKey,n.phase="session_registered",this.save(n)}catch(s){throw g(s,"register_session",i,n)}if(n.phase==="session_registered")try{n.result=await this.deps.joinRegisteredSession({bindingIntentId:`room-managed-session-join:${i}`,registryKey:n.registryKey,inviteUrl:t.inviteUrl,agentAlias:t.alias,agentDescription:`${t.alias}\uFF08\u53D7\u7BA1 ${t.agent} Agent\uFF09`,activationMode:t.mode,activationCapability:"managed"}),O(n.result),n.phase="joined",this.save(n)}catch(s){throw g(s,"join_room",i,n)}return E(n)}save(i){i.updatedAt=(this.deps.now?.()??new Date).toISOString(),this.deps.operations.save(m(i))}}class G{records=new Map;load(i){const o=this.records.get(i);return o?m(o):null}save(i){this.records.set(i.operationId,m(i))}}class Z{directory;constructor(i){this.directory=i}load(i){_(i);const o=this.filePath(i);try{const t=a.lstatSync(o);if(!t.isFile()||t.isSymbolicLink())throw p();process.platform!=="win32"&&(t.mode&63)!==0&&a.chmodSync(o,384);const r=JSON.parse(a.readFileSync(o,"utf8"));return M(r,i),m(r)}catch(t){if(t.code==="ENOENT")return null;throw t}}save(i){_(i.operationId),M(i,i.operationId),a.mkdirSync(this.directory,{recursive:!0,mode:448});const o=a.lstatSync(this.directory);if(!o.isDirectory()||o.isSymbolicLink())throw p();process.platform!=="win32"&&a.chmodSync(this.directory,448);const t=this.filePath(i.operationId),r=`${t}.tmp-${process.pid}-${S.randomBytes(6).toString("hex")}`;try{if(a.writeFileSync(r,`${JSON.stringify(i,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&a.chmodSync(r,384),process.platform==="win32"&&a.existsSync(t)){const n=a.lstatSync(t);if(!n.isFile()||n.isSymbolicLink())throw p();a.unlinkSync(t)}a.renameSync(r,t)}finally{try{a.unlinkSync(r)}catch{}}}filePath(i){return y.join(this.directory,`${i}.json`)}}function I(e,i){(!e||typeof e!="object"||Array.isArray(e))&&d("input");const o=e;Object.keys(o).find(T=>!k.has(T))&&d("unsupported_field");const r=o.agent;(typeof r!="string"||!K.has(r))&&d("agent");const n=f(o.workDir,"work_dir",4096);(!y.isAbsolute(n)||y.normalize(n)!==n)&&d("work_dir");const s=f(o.alias,"alias",80),l=o.mode??"auto_follow";(typeof l!="string"||!$.has(l))&&d("mode");const x=o.modelId==null?null:f(o.modelId,"model_id",191);return{inviteUrl:F(o.inviteUrl,i),agent:r,workDir:n,alias:s,mode:l,modelId:x}}function F(e,i){const o=f(e,"invite_url",2048);let t;try{t=new URL(o)}catch{d("invite_url")}(!new Set(i.map(l=>new URL(l).origin)).has(t.origin)||t.username||t.password||t.search||t.hash)&&d("invite_url");const n=t.pathname.split("/").filter(Boolean),s=n.at(-1);return(!s||!D.test(s)||n.at(-2)!=="join")&&d("invite_url"),t.toString()}function f(e,i,o){typeof e!="string"&&d(i);const t=e.trim();return(!t||t.length>o||/[\0\r\n]/.test(t))&&d(i),t}function _(e){if(!j.test(e))throw new h("invalid_managed_room_operation_id","Managed Room operation ID is invalid.","validate_input",null,"initialized",null)}function v(e){return e==="claude"?"claude_code":e}function b(e){return S.createHash("sha256").update(JSON.stringify(e),"utf8").digest("hex")}function c(e,i){if(!R.test(e))throw u(i)}function A(e){if(!e||e.length>1024||/[\0\r\n]/.test(e))throw u("source_session_key")}function O(e){if(c(e.room?.id,"room_id"),c(e.binding?.id,"binding_id"),c(e.binding?.agentIdentityId,"agent_identity_id"),e.binding.agentMembershipId!==null&&c(e.binding.agentMembershipId,"agent_membership_id"),e.outcome!=="joined"&&e.outcome!=="pending_approval")throw u("join_outcome");if(e.outcome==="joined"&&e.binding.status!=="active"&&e.binding.status!=="paused"||e.outcome==="pending_approval"&&e.binding.status!=="pending_approval"||e.binding.status==="pending_approval"&&e.binding.agentMembershipId!==null||(e.binding.status==="active"||e.binding.status==="paused")&&e.binding.agentMembershipId===null)throw u("join_binding_state")}function U(){return["\u8FD9\u662F\u4E00\u4E2A\u7531\u795E\u5FF5\u521B\u5EFA\u7684\u53D7\u7BA1 Agent \u5BF9\u8BDD\u3002","\u8BF7\u5148\u4FDD\u6301\u7A7A\u95F2\uFF1B\u795E\u5FF5\u6B63\u5728\u4E3A\u8BE5\u7CBE\u786E\u5BF9\u8BDD\u5EFA\u7ACB\u7FA4\u804A\u8FDE\u63A5\uFF0C\u5B8C\u6210\u540E\u4F1A\u53D1\u9001\u540E\u7EED\u6D88\u606F\u3002"].join(`
|
|
3
|
+
`)}function E(e){if(!e.nianSessionId||!e.result||e.phase!=="joined")throw u("operation_state");return{operationId:e.operationId,phase:"joined",nianSessionId:e.nianSessionId,result:m(e.result)}}function g(e,i,o,t){if(e instanceof h)return e;const r=e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"?e.code:`managed_room_${i}_failed`;return new h(r,`Managed Room Session failed during ${i}. The created conversation was preserved for retry.`,i,o,t?.phase??"initialized",t?.nianSessionId??null)}function d(e){throw new h(`invalid_managed_room_${e}`,`Managed Room Session ${e} is invalid.`,"validate_input",null,"initialized",null)}function u(e){return Object.assign(new Error(`Managed Room dependency returned an invalid ${e}.`),{code:`invalid_managed_room_${e}`})}function M(e,i){if(!e||e.schemaVersion!==1||e.operationId!==i||!/^[a-f0-9]{64}$/.test(e.inputFingerprint)||!["initialized","session_created","native_session_ready","session_registered","joined"].includes(e.phase)||!e.input||v(e.input.agent)!==e.runtimeAdapter)throw p();const o=I(e.input,[new URL(e.input.inviteUrl).origin]);if(b(o)!==e.inputFingerprint||(e.nianSessionId!==null&&c(e.nianSessionId,"nian_session_id"),e.sourceSessionKey!==null&&A(e.sourceSessionKey),e.hostProcessIdentity!==null&&c(e.hostProcessIdentity,"host_process_identity"),e.registryKey!==null&&!w.test(e.registryKey)))throw p();e.result!==null&&O(e.result)}function p(){return Object.assign(new Error("Managed Room Session operation store is unsafe or corrupt."),{code:"unsafe_managed_room_operation_store"})}function m(e){return JSON.parse(JSON.stringify(e))}export{Z as FileManagedRoomSessionOperationStore,G as InMemoryManagedRoomSessionOperationStore,h as ManagedRoomSessionError,B as ManagedRoomSessionOrchestrator};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import v from"node:path";class c extends Error{code;constructor(e,o){super(o),this.code=e,this.name="RoomActionHttpError"}}class T{fetchImpl;baseUrl;constructor(e,o=fetch){this.fetchImpl=o,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);return j(t,e,this.baseUrl)}async resolveNativeSession(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);(!t||typeof t!="object")&&r();const n=t;return(typeof n.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(n.nianSessionId)||typeof n.created!="boolean")&&r(),n}async createRoom(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);return w(t,e,this.baseUrl)}async joinRoom(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);return _(t,e,this.baseUrl)}async status(e){const o=await this.request(e,"status");return S(o,e)}async getAttachmentCapability(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!o||typeof o!="object")&&r();const t=o;return(t.attachmentId!==e.attachmentId||t.roomId!==e.roomId||typeof t.safeName!="string"||!A(t.safeName)||typeof t.mimeType!="string"||t.mimeType.length<1||t.mimeType.length>191||typeof t.byteSize!="string"||!d(t.byteSize)||typeof t.sha256!="string"||!/^[a-f0-9]{64}$/.test(t.sha256)||!b(t.downloadUrl)||typeof t.expiresAt!="string"||!Number.isFinite(Date.parse(t.expiresAt)))&&r(),t}async initializeAttachment(e){const o=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})});(!o||typeof o!="object")&&r();const t=o,n=t.attachment,a=t.upload;return(!n||typeof n.id!="string"||n.roomId!==e.roomId||n.name.length<1||n.mimeType!==e.mimeType||n.byteSize!==String(e.byteSize)||n.sha256!==e.sha256||n.status!=="pending"&&n.status!=="uploaded"||!a||a.method!=="PUT"||!b(a.url)||!a.headers||a.headers["content-type"]!==e.mimeType||a.headers["x-oss-meta-sha256"]!==e.sha256||typeof a.expiresAt!="string"||Date.parse(a.expiresAt)<=Date.now()||typeof t.idempotent!="boolean")&&r(),t}async completeAttachment(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!o||typeof o!="object")&&r();const t=o;typeof t.idempotent!="boolean"&&r();const n=t.attachment;return(!n||n.attachmentId!==e.attachmentId||n.mediaKind!=="image"&&n.mediaKind!=="audio"&&n.mediaKind!=="file"||typeof n.name!="string"||typeof n.mimeType!="string"||typeof n.byteSize!="string"||!d(n.byteSize)||typeof n.sha256!="string"||!/^[a-f0-9]{64}$/.test(n.sha256)||n.status!=="ready")&&r(),{attachment:n,idempotent:t.idempotent}}async read(e){const o=new URLSearchParams({after:e.afterSequence,limit:String(e.limit)}),t=await this.request(e,`messages?${o}`);(!t||typeof t!="object")&&r();const n=t;return(!Array.isArray(n.messages)||typeof n.nextSequence!="string"||!d(n.nextSequence)||typeof n.hasMore!="boolean"||typeof n.historyFromSequence!="string"||!d(n.historyFromSequence)||typeof n.roomName!="string")&&r(),{messages:n.messages.map(s=>u(s,e.roomId)),nextSequence:n.nextSequence,hasMore:n.hasMore,historyFromSequence:n.historyFromSequence,roomName:n.roomName}}async send(e){const o=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})});(!o||typeof o!="object")&&r();const t=o;return typeof t.idempotent!="boolean"&&r(),{message:u(t.message,e.roomId),idempotent:t.idempotent}}async mode(e){const o=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!o||typeof o!="object")&&r();const t=o;return(t.bindingId!==e.bindingId||t.roomId!==e.roomId||t.status!=="active"&&t.status!=="paused"||!g(t.activationMode)||t.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&r(),t}async request(e,o,t={}){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${o}`,{...t,headers:{authorization:`Bearer ${e.machineToken}`,...t.body?{"content-type":"application/json"}:{}},signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}if(!n.ok){let a=`room_action_http_${n.status}`;try{const s=await n.json();typeof s.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(s.error)&&(a=s.error)}catch{}throw new c(a,"Shennian Room action failed.")}try{return await n.json()}catch{r()}}}function S(i,e){(!i||typeof i!="object")&&r();const o=i,t=o.room,n=o.agent;(o.bindingId!==e.bindingId||o.roomId!==e.roomId||typeof o.agentIdentityId!="string"||o.agentMembershipId!==null&&typeof o.agentMembershipId!="string"||o.status!=="pending_approval"&&o.status!=="active"&&o.status!=="paused"&&o.status!=="detached"||!g(o.activationMode)||o.activationCapability!==void 0&&o.activationCapability!=="managed"&&o.activationCapability!=="idle_resume"&&o.activationCapability!=="notify_only"||o.managedWorkspacePath!==void 0&&o.managedWorkspacePath!==null&&(typeof o.managedWorkspacePath!="string"||o.managedWorkspacePath.length>4096||!v.isAbsolute(o.managedWorkspacePath))||typeof o.lastDeliveredSequence!="string"||!d(o.lastDeliveredSequence)||typeof o.lastActivatedSequence!="string"||!d(o.lastActivatedSequence)||!t||typeof t!="object"||n!==void 0&&(!n||typeof n!="object"))&&r(),(o.status==="pending_approval"&&o.agentMembershipId!==null||(o.status==="active"||o.status==="paused")&&typeof o.agentMembershipId!="string")&&r();const a=t,s=n,m=a.members;return(s!==void 0&&(typeof s.displayName!="string"||!s.displayName.trim()||typeof s.description!="string")||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"||!d(a.lastSequence)||m!==void 0&&(!Array.isArray(m)||m.some(l=>{if(!l||typeof l!="object")return!0;const f=l;return typeof f.displayName!="string"||f.principalType!=="human"&&f.principalType!=="agent"})))&&r(),o}function u(i,e){(!i||typeof i!="object")&&r();const o=i;if((typeof o.id!="string"||o.roomId!==e||typeof o.sequence!="string"||!d(o.sequence)||!o.author||typeof o.author.membershipId!="string"||!Array.isArray(o.attachments)||typeof o.createdAt!="string")&&r(),o.activationCause!=null){const t=o.activationCause;(typeof t.causeMessageId!="string"||typeof t.rootCauseMessageId!="string"||!Number.isInteger(t.hopCount)||t.hopCount<1||t.hopCount>32)&&r()}return o}function w(i,e,o){(!i||typeof i!="object")&&r();const t=i,n=t.room,a=t.agentIdentity,s=t.binding;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!h(n.webUrl,o,`/spaces/${n.id}`)||!M(n.inviteUrl,o)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==a.id||typeof s.agentMembershipId!="string"||s.status!=="active"&&s.status!=="paused"||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&r(),t}function j(i,e,o){(!i||typeof i!="object")&&r();const t=i,n=t.room;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!h(n.webUrl,o,`/spaces/${n.id}`))&&r(),t}function _(i,e,o){(!i||typeof i!="object")&&r();const t=i,n=t.room,a=t.agentIdentity,s=t.binding;return(t.outcome!=="joined"&&t.outcome!=="pending_approval"||!n||typeof n.id!="string"||typeof n.name!="string"||!h(n.webUrl,o,`/spaces/${n.id}`)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==a.id||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&r(),(t.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof t.joinRequestId!="string")||t.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||t.joinRequestId!==void 0))&&r(),t}function d(i){return/^(0|[1-9]\d{0,19})$/.test(i)}function A(i){return i.length<=255&&i===i.trim()&&i!=="."&&i!==".."&&!/[\\/\0-\x1f\x7f]/.test(i)}function b(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 g(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 y(i,e){let o=`room_action_http_${i}`;if(e&&typeof e=="object"){const t=e.error;typeof t=="string"&&/^[a-z0-9_-]{1,80}$/.test(t)&&(o=t)}return new c(o,"Shennian Room action failed.")}function h(i,e,o){return I(i,e,t=>t.pathname===o)}function M(i,e){return I(i,e,o=>/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(o.pathname))}function I(i,e,o){if(typeof i!="string")return!1;try{const t=new URL(i),n=new URL(e),a=t.origin===n.origin,s=n.protocol==="http:"&&t.protocol==="http:"&&t.hostname===n.hostname,m=t.protocol==="https:"&&t.port===""&&(t.hostname==="shennian.net"||t.hostname==="app.shennian.net");return(a||s||m)&&!t.username&&!t.password&&!t.search&&!t.hash&&o(t)}catch{return!1}}export{T as HttpRoomActionClient,c as RoomActionHttpError};
|
|
1
|
+
import S from"node:path";class c extends Error{code;constructor(e,o){super(o),this.code=e,this.name="RoomActionHttpError"}}class T{fetchImpl;baseUrl;constructor(e,o=fetch){this.fetchImpl=o,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);return A(t,e,this.baseUrl)}async resolveNativeSession(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);(!t||typeof t!="object")&&r();const n=t;return(typeof n.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(n.nianSessionId)||typeof n.created!="boolean")&&r(),n}async createRoom(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);return j(t,e,this.baseUrl)}async joinRoom(e){let o;try{o=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 t=await p(o);if(!o.ok)throw y(o.status,t);return _(t,e,this.baseUrl)}async status(e){const o=await this.request(e,"status");return w(o,e)}async getAttachmentCapability(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!o||typeof o!="object")&&r();const t=o;return(t.attachmentId!==e.attachmentId||t.roomId!==e.roomId||typeof t.safeName!="string"||!b(t.safeName)||typeof t.mimeType!="string"||t.mimeType.length<1||t.mimeType.length>191||typeof t.byteSize!="string"||!d(t.byteSize)||typeof t.sha256!="string"||!/^[a-f0-9]{64}$/.test(t.sha256)||!g(t.downloadUrl)||typeof t.expiresAt!="string"||!Number.isFinite(Date.parse(t.expiresAt)))&&r(),t}async initializeAttachment(e){const o=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})});(!o||typeof o!="object")&&r();const t=o,n=t.attachment,a=t.upload;return(!n||typeof n.id!="string"||n.roomId!==e.roomId||typeof n.name!="string"||!b(n.name)||n.mimeType!==e.mimeType||n.byteSize!==String(e.byteSize)||n.sha256!==e.sha256||n.status!=="pending"&&n.status!=="uploaded"||!a||a.method!=="PUT"||!g(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 t.idempotent!="boolean")&&r(),t}async completeAttachment(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!o||typeof o!="object")&&r();const t=o;typeof t.idempotent!="boolean"&&r();const n=t.attachment;return(!n||n.attachmentId!==e.attachmentId||n.mediaKind!=="image"&&n.mediaKind!=="audio"&&n.mediaKind!=="file"||typeof n.name!="string"||typeof n.mimeType!="string"||typeof n.byteSize!="string"||!d(n.byteSize)||typeof n.sha256!="string"||!/^[a-f0-9]{64}$/.test(n.sha256)||n.status!=="ready")&&r(),{attachment:n,idempotent:t.idempotent}}async read(e){const o=new URLSearchParams({after:e.afterSequence,limit:String(e.limit)}),t=await this.request(e,`messages?${o}`);(!t||typeof t!="object")&&r();const n=t;return(!Array.isArray(n.messages)||typeof n.nextSequence!="string"||!d(n.nextSequence)||typeof n.hasMore!="boolean"||typeof n.historyFromSequence!="string"||!d(n.historyFromSequence)||typeof n.roomName!="string")&&r(),{messages:n.messages.map(s=>u(s,e.roomId)),nextSequence:n.nextSequence,hasMore:n.hasMore,historyFromSequence:n.historyFromSequence,roomName:n.roomName}}async send(e){const o=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})});(!o||typeof o!="object")&&r();const t=o;return typeof t.idempotent!="boolean"&&r(),{message:u(t.message,e.roomId),idempotent:t.idempotent}}async mode(e){const o=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!o||typeof o!="object")&&r();const t=o;return(t.bindingId!==e.bindingId||t.roomId!==e.roomId||t.status!=="active"&&t.status!=="paused"||!I(t.activationMode)||t.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&r(),t}async request(e,o,t={}){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${o}`,{...t,headers:{authorization:`Bearer ${e.machineToken}`,...t.body?{"content-type":"application/json"}:{}},signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}if(!n.ok){let a=`room_action_http_${n.status}`;try{const s=await n.json();typeof s.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(s.error)&&(a=s.error)}catch{}throw new c(a,"Shennian Room action failed.")}try{return await n.json()}catch{r()}}}function w(i,e){(!i||typeof i!="object")&&r();const o=i,t=o.room,n=o.agent;(o.bindingId!==e.bindingId||o.roomId!==e.roomId||typeof o.agentIdentityId!="string"||o.agentMembershipId!==null&&typeof o.agentMembershipId!="string"||o.status!=="pending_approval"&&o.status!=="active"&&o.status!=="paused"&&o.status!=="detached"||!I(o.activationMode)||o.activationCapability!==void 0&&o.activationCapability!=="managed"&&o.activationCapability!=="idle_resume"&&o.activationCapability!=="notify_only"||o.managedWorkspacePath!==void 0&&o.managedWorkspacePath!==null&&(typeof o.managedWorkspacePath!="string"||o.managedWorkspacePath.length>4096||!S.isAbsolute(o.managedWorkspacePath))||typeof o.lastDeliveredSequence!="string"||!d(o.lastDeliveredSequence)||typeof o.lastActivatedSequence!="string"||!d(o.lastActivatedSequence)||!t||typeof t!="object"||n!==void 0&&(!n||typeof n!="object"))&&r(),(o.status==="pending_approval"&&o.agentMembershipId!==null||(o.status==="active"||o.status==="paused")&&typeof o.agentMembershipId!="string")&&r();const a=t,s=n,m=a.members;return(s!==void 0&&(typeof s.displayName!="string"||!s.displayName.trim()||typeof s.description!="string")||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"||!d(a.lastSequence)||m!==void 0&&(!Array.isArray(m)||m.some(l=>{if(!l||typeof l!="object")return!0;const f=l;return typeof f.displayName!="string"||f.principalType!=="human"&&f.principalType!=="agent"})))&&r(),o}function u(i,e){(!i||typeof i!="object")&&r();const o=i;if((typeof o.id!="string"||o.roomId!==e||typeof o.sequence!="string"||!d(o.sequence)||!o.author||typeof o.author.membershipId!="string"||!Array.isArray(o.attachments)||typeof o.createdAt!="string")&&r(),o.activationCause!=null){const t=o.activationCause;(typeof t.causeMessageId!="string"||typeof t.rootCauseMessageId!="string"||!Number.isInteger(t.hopCount)||t.hopCount<1||t.hopCount>32)&&r()}return o}function j(i,e,o){(!i||typeof i!="object")&&r();const t=i,n=t.room,a=t.agentIdentity,s=t.binding;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!h(n.webUrl,o,`/spaces/${n.id}`)||!M(n.inviteUrl,o)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==a.id||typeof s.agentMembershipId!="string"||s.status!=="active"&&s.status!=="paused"||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&r(),t}function A(i,e,o){(!i||typeof i!="object")&&r();const t=i,n=t.room;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!h(n.webUrl,o,`/spaces/${n.id}`))&&r(),t}function _(i,e,o){(!i||typeof i!="object")&&r();const t=i,n=t.room,a=t.agentIdentity,s=t.binding;return(t.outcome!=="joined"&&t.outcome!=="pending_approval"||!n||typeof n.id!="string"||typeof n.name!="string"||!h(n.webUrl,o,`/spaces/${n.id}`)||!a||typeof a.id!="string"||a.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==a.id||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&r(),(t.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof t.joinRequestId!="string")||t.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||t.joinRequestId!==void 0))&&r(),t}function d(i){return/^(0|[1-9]\d{0,19})$/.test(i)}function b(i){return i.length<=255&&i===i.trim()&&i!=="."&&i!==".."&&!/[\\/\0-\x1f\x7f]/.test(i)}function g(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 I(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 y(i,e){let o=`room_action_http_${i}`;if(e&&typeof e=="object"){const t=e.error;typeof t=="string"&&/^[a-z0-9_-]{1,80}$/.test(t)&&(o=t)}return new c(o,"Shennian Room action failed.")}function h(i,e,o){return v(i,e,t=>t.pathname===o)}function M(i,e){return v(i,e,o=>/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(o.pathname))}function v(i,e,o){if(typeof i!="string")return!1;try{const t=new URL(i),n=new URL(e),a=t.origin===n.origin,s=n.protocol==="http:"&&t.protocol==="http:"&&t.hostname===n.hostname,m=t.protocol==="https:"&&t.port===""&&(t.hostname==="shennian.net"||t.hostname==="app.shennian.net");return(a||s||m)&&!t.username&&!t.password&&!t.search&&!t.hash&&o(t)}catch{return!1}}export{T as HttpRoomActionClient,c as RoomActionHttpError};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
|
|
2
2
|
import { RoomInvocationTokenAuthority } from './invocation-token.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type RoomAttachmentCache } from './attachment-cache.js';
|
|
4
4
|
import { HttpRoomActionClient } from './room-action-client.js';
|
|
5
5
|
import { RoomSessionRegistry } from './session-registry.js';
|
|
6
|
-
import type
|
|
6
|
+
import { type RoomAttachmentUploader } from './attachment-upload.js';
|
|
7
7
|
import type { RoomHostSessionObservationStore } from './host-session-observations.js';
|
|
8
8
|
type RoomMirrorWriter = {
|
|
9
9
|
readonly messagesPath?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as
|
|
1
|
+
import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{RoomInvocationTokenError as S}from"./invocation-token.js";import{RoomAttachmentCacheError as k}from"./attachment-cache.js";import{RoomActionHttpError as C}from"./room-action-client.js";import{normalizeRoomActionParameters as q}from"./room-action-schema.js";import{isInterruptedRoomActivation as w}from"./session-registry.js";import{RoomAttachmentUploadError as B}from"./attachment-upload.js";function O(e){return{"room-action.execute":async o=>{const I=x(o),l=e.getMachineCredentials();if(!l.machineId?.trim()||!l.machineToken?.trim())throw new a("machine_not_authorized","This machine is not authorized yet.");const r=_(I.registryKey,"registryKey",64),R=_(I.invocationToken,"invocationToken",128),m=E(I.action),b=q(m,I.actionParameters);return e.registry.runExclusive(r,async()=>{let g;try{g=e.authority.consume({invocationToken:R,machineId:l.machineId,machineCredential:l.machineToken,registryKey:r,action:m,targetTool:"shennian_room",actionParameters:b})}catch(t){throw M(t)}const c=e.registry.getByRegistryKey(r),i=c?.binding;if(!c)throw new a("room_session_unavailable","The exact Agent session is unavailable.");if(m==="room.create"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!g.bindingIntentId)throw new a("binding_intent_missing","Room create intent is missing.");const t=b,n=await e.actionClient.createRoom({machineToken:l.machineToken,bindingIntentId:g.bindingIntentId,nianSessionId:c.nianSessionId,runtimeAdapter:c.runtimeAdapter,sessionRegistryKey:r,name:t.name,agentAlias:t.agentAlias,agentDescription:t.agentDescription,activationCapability:A(c.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:r,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),u(n)}if(m==="room.join"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!g.bindingIntentId)throw new a("binding_intent_missing","Room join intent is missing.");const t=b,n=await e.actionClient.joinRoom({machineToken:l.machineToken,bindingIntentId:g.bindingIntentId,nianSessionId:c.nianSessionId,runtimeAdapter:c.runtimeAdapter,sessionRegistryKey:r,inviteToken:j(t.inviteUrl),message:t.message,agentAlias:t.agentAlias,agentDescription:t.agentDescription,activationCapability:A(c.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:r,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),u(n)}if(!i)throw new a("room_not_bound","This Agent session is not bound to a Room.");if(m!=="room.status"&&(i.status==="detached"||i.status==="pending_approval"))throw new a(i.status==="pending_approval"?"room_binding_pending_approval":"room_binding_detached",i.status==="pending_approval"?"This Agent is waiting for Room approval.":"This Agent Room binding must be repaired before use.");const v={machineToken:l.machineToken,roomId:i.roomId,bindingId:i.bindingId};if(m==="room.status"){const t=await e.actionClient.status(v),n=e.mirrorFactory?.(t.roomId);e.registry.setBinding({registryKey:r,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:t.status,activationMode:t.activationMode}),t.status==="detached"&&n?.markAccessRevoked();const h=e.registry.getByRegistryKey(r),d=h?.binding,s=e.observations?.getBySource(c);return u({...t,localHistoryPath:n?.messagesPath??null,hostSession:{state:h?.lifecycleState??"closed",lastReportedEvent:s?.lastEventName??null,lastActiveAt:s?.lastActiveAt??null,lastIdleAt:s?.lastIdleAt??null,closedAt:s?.closedAt??null,closedConfirmed:s?.lifecycleState==="closed",activationCapability:A(c.capabilities)},localActivation:{queued:d?.activationQueue.length??0,inFlight:!!(d?.activeActivation&&!w(d.activeActivation)),interrupted:!!(d?.activeActivation&&w(d.activeActivation)),pausedUntil:d?.loopBreaker.pausedUntil??null}})}if(m==="room.read"){const t=b;if(t.query){const s=e.mirrorFactory?.(i.roomId);if(!s)throw new a("room_mirror_unavailable","Room mirror is unavailable.");return u({source:"local_mirror",matches:s.search({query:t.query,limit:t.limit})})}const n=await e.actionClient.read({...v,afterSequence:t.afterSequence,limit:t.limit});e.mirrorFactory?.(i.roomId).append({messages:n.messages,historyFromSequence:n.historyFromSequence,roomName:n.roomName});const h=BigInt(n.nextSequence)>BigInt(i.lastDeliveredSequence)?n.nextSequence:i.lastDeliveredSequence;e.registry.advanceCursor({registryKey:r,cursor:"delivered",sequence:h});const d=[];for(const s of t.attachmentIds??[]){const y=n.messages.flatMap(p=>p.attachments).find(p=>p.attachmentId===s);if(!y||y.status!=="ready")throw new a("attachment_not_in_read_result","Requested attachment is not available in this read result.");if(!e.attachmentCache)throw new a("attachment_cache_unavailable","Room attachment cache is unavailable.");d.push({attachmentId:s,...await e.attachmentCache.materialize({...v,attachmentId:s,expected:{downloadRef:y.downloadRef,byteSize:y.byteSize,sha256:y.sha256}})})}return u({...n,materializedAttachments:d})}if(m==="room.send"){const t=b;let n;if(t.files?.length){const h=e.observations?.getBySource(c);if(!h?.workspacePath||!e.attachmentUploader)throw new a("room_attachment_upload_unavailable","The exact Agent workspace is unavailable for attachment upload.");n=await e.attachmentUploader.upload({...v,workspacePath:h.workspacePath,clientMessageId:t.clientMessageId,files:t.files})}return u(await e.actionClient.send({...v,clientMessageId:t.clientMessageId,text:t.text,mentionMembershipIds:t.mentionMembershipIds,replyToMessageId:t.replyToMessageId,attachmentIds:n,activationCauseMessageId:i.activeActivation&&!w(i.activeActivation)?i.activeActivation.causeMessageId:void 0}))}const T=b,f=await e.actionClient.mode({...v,...T});return e.registry.setBinding({registryKey:r,bindingId:i.bindingId,roomId:i.roomId,agentIdentityId:i.agentIdentityId,agentMembershipId:i.agentMembershipId,status:f.status,activationMode:f.activationMode}),u(f)}).catch(g=>{throw M(g)})}}}function x(e){if(!e||Array.isArray(e)||typeof e!="object")throw new a("invalid_params","Room action parameters are required.");return e}function _(e,o,I){if(typeof e!="string"||!e.trim()||e.length>I)throw new a("invalid_params",`${o} is invalid.`);return e.trim()}function E(e){const o=_(e,"action",64);if(o!=="room.read"&&o!=="room.create"&&o!=="room.join"&&o!=="room.send"&&o!=="room.mode"&&o!=="room.status")throw new a("room_action_not_implemented","Room action is not available yet.");return o}function j(e){const o=new URL(e).pathname.split("/").at(-1);if(!o||!/^[A-Za-z0-9_-]{43}$/.test(o))throw new a("invalid_params","inviteUrl is invalid.");return o}function A(e){return e.canResumeIdleSession||e.canActivateWhileHostOpen?"idle_resume":"notify_only"}function M(e){return e instanceof a?e:e instanceof S||e instanceof C||e instanceof k||e instanceof B?new a(e.code,e.message):new a("room_invocation_failed","Room invocation failed.")}function u(e){return JSON.parse(JSON.stringify(e))}export{O as createRoomActionIpcHandlers};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentType } from './session.js';
|
|
1
2
|
export type NianProject = {
|
|
2
3
|
id: string;
|
|
3
4
|
machineId: string;
|
|
@@ -5,6 +6,8 @@ export type NianProject = {
|
|
|
5
6
|
machineOnline: boolean;
|
|
6
7
|
workDir: string;
|
|
7
8
|
displayName: string;
|
|
9
|
+
defaultAgentType?: AgentType | null;
|
|
10
|
+
defaultModelId?: string | null;
|
|
8
11
|
lastActivityAt: string;
|
|
9
12
|
createdAt: string;
|
|
10
13
|
updatedAt: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA"}
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,SAAS,GAAG,IAAI,CAAA;IACnC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA"}
|