shennian 0.3.39 → 0.3.41
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/README.md +2 -1
- package/dist/publish-build-manifest.json +37 -37
- package/dist/src/agents/claude.d.ts +1 -0
- package/dist/src/agents/claude.js +4 -4
- package/dist/src/agents/codex.d.ts +9 -0
- package/dist/src/agents/codex.js +10 -10
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/commands/room.d.ts +2 -0
- package/dist/src/commands/room.js +3 -3
- package/dist/src/index.js +3 -3
- package/dist/src/native-fusion/codex-parser.js +4 -4
- package/dist/src/native-fusion/service.d.ts +8 -0
- package/dist/src/native-fusion/service.js +2 -2
- package/dist/src/native-fusion/types.d.ts +10 -0
- package/dist/src/room/host-session-observations.d.ts +1 -1
- package/dist/src/room/host-session-observations.js +2 -2
- package/dist/src/room/invite-qr.d.ts +6 -0
- package/dist/src/room/invite-qr.js +1 -0
- package/dist/src/room/managed-room-binding.d.ts +1 -1
- package/dist/src/room/managed-room-binding.js +1 -1
- package/dist/src/room/public-room-client.d.ts +2 -1
- package/dist/src/room/public-room-client.js +1 -1
- package/dist/src/room/room-action-client.d.ts +5 -5
- package/dist/src/room/room-action-client.js +1 -1
- package/dist/src/room/session-registry.d.ts +1 -1
- package/dist/src/room/session-registry.js +2 -2
- package/dist/src/session/handlers/chat.js +2 -2
- package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +3 -1
- package/node_modules/@shennian/wire/dist/external-message-format.js +1 -1
- package/node_modules/@shennian/wire/dist/room.d.ts +18 -1
- package/node_modules/@shennian/wire/dist/room.js +18 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +10 -0
- package/package.json +3 -1
- package/dist/src/commands/agent.d.ts +0 -7
- package/dist/src/commands/agent.js +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import T from"node:fs";import v from"node:os";import b from"node:path";import{buildApprovalPendingPayload as Q,buildUserMessagePayload as k,isSystemControlPayload as j,isToolPayload as q}from"@shennian/wire";import{buildImagePayload as V,buildToolPayload as X,inferMimeType as E,makeCursor as Y,makeEventKey as Z,normalizeText as d,parseStructuredString as P,readJsonlLines as N,readTimestamp as D,safeParse as I}from"./parser-common.js";function O(e){if(typeof e=="string"){const i=e.trim();return i?{path:i,name:b.basename(i)||"image.png",mimeType:E(i),kind:"image"}:null}if(typeof e!="object"||e===null)return null;const r=e,n=typeof r.path=="string"?r.path:typeof r.file_path=="string"?r.file_path:typeof r.saved_path=="string"?r.saved_path:"";if(!n.trim())return null;const t=typeof r.name=="string"&&r.name.trim()?r.name:b.basename(n)||"image.png",o=typeof r.mimeType=="string"&&r.mimeType.trim()?r.mimeType:E(n);return{path:n,name:t,mimeType:o,kind:"image"}}function R(e){return Array.isArray(e)?e.map(n=>{if(typeof n=="string")return n;if(typeof n=="object"&&n!==null){const t=n;return typeof t.text=="string"?t.text:""}return""}).map(n=>d(n)).filter(Boolean).join(`
|
|
2
2
|
|
|
3
|
-
`):""}function U(e){const r=
|
|
3
|
+
`):""}function U(e){const r=d(e);if(!r)return{text:"",strippedWrapper:!1};const n=r.match(/My request for Codex:\s*([\s\S]*)$/i);return n&&n[1]?{text:K(n[1]),strippedWrapper:!0}:{text:r,strippedWrapper:!1}}function K(e){return d(e.replace(/<image>\s*<\/image>/gi,""))}function w(e){const r=d(e);if(!r)return!1;if(j(r)||/^<environment_context>[\s\S]*<\/environment_context>$/i.test(r)&&r.includes("<current_date>")&&r.includes("<timezone>")&&r.includes("<workspace_roots>"))return!0;const n=r.startsWith("# AGENTS.md instructions for ")&&r.includes("<INSTRUCTIONS>")&&r.includes("</INSTRUCTIONS>"),t=r.includes("<environment_context>")&&r.includes("</environment_context>");return n&&t}function F(e){const r=R(e.text_elements),n=Array.isArray(e.input)?R(e.input):"",t=typeof e.message=="string"?U(e.message):{text:"",strippedWrapper:!1},o=t.text,i=r||n||o,c=t.strippedWrapper?"codex_app_context_wrapper":void 0,f=Array.isArray(e.local_images)?e.local_images.map(O).filter(a=>a!=null):[];return!i&&f.length===0||j(i)&&f.length===0||w(i)&&f.length===0?null:f.length>0?{payload:k(i,f),titleText:i,repairHint:c}:{payload:i,titleText:i,repairHint:c}}function tt(e){return e==="turn_completed"||e==="turn_complete"||e==="task_complete"}function et(e){return/approval|permission|confirm|user[_-]?input|input[_-]?required/i.test(e)}function nt(e){const r=typeof e.command=="string"?e.command:Array.isArray(e.command)?e.command.join(" "):"",n=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:r?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${r}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";return Q({title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",content:n,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"})}function $(e,r){return Array.isArray(e)?d(e.map(n=>{if(typeof n=="string")return n;if(typeof n!="object"||n===null)return"";const t=n;return t.type!==r?"":typeof t.text=="string"?t.text:""}).filter(Boolean).join(`
|
|
4
4
|
|
|
5
|
-
`)):""}function
|
|
6
|
-
(exit ${String(m)})`:""].join("");
|
|
5
|
+
`)):""}function rt(e){if(!Array.isArray(e))return[];const r=[];for(const n of e){if(typeof n!="object"||n===null)continue;const t=n;if(t.type!=="input_image")continue;const o=typeof t.path=="string"?t.path:typeof t.file_path=="string"?t.file_path:typeof t.saved_path=="string"?t.saved_path:typeof t.image_path=="string"?t.image_path:"";o.trim()&&r.push({path:o,name:b.basename(o)||"image.png",mimeType:E(o),kind:"image"})}return r}function z(e){if(e.type!=="message")return null;const r=e.role==="assistant"?"agent":e.role==="user"?"user":null;if(!r)return null;if(r==="agent"){const i=$(e.content,"output_text");return j(i)?null:i?{role:r,payload:i,titleText:i}:null}const n=U($(e.content,"input_text")),t=n.text,o=rt(e.content);return!t&&o.length===0||j(t)&&o.length===0||w(t)&&o.length===0?null:{role:r,payload:o.length>0?k(t,o):t,titleText:t,repairHint:n.strippedWrapper?"codex_app_context_wrapper":void 0}}function B(e){if(!e)return"";try{const r=JSON.parse(e);if(typeof r=="object"&&r!==null){const n=r;if(n.type==="user"&&typeof n.content=="string")return d(n.content)}}catch{}return d(e)}function ot(e,r,n,t){return e.agentType!=="codex"||e.role!==r||q(e.payload)||Math.abs(e.ts-t)>300*1e3?!1:B(e.payload)===B(n)}function W(e,r,n,t){for(let o=e.length-1;o>=0;o-=1){const i=e[o];if(i){if(ot(i,r,n,t))return o;if(i.agentType==="codex"&&Math.abs(i.ts-t)>300*1e3)break}}return-1}function C(e,r,n,t,o,i,c,f,a,u,s="agent",m=!1,p){c&&e.push({agentType:"codex",sourceMode:"codex_rollout_import",sourceSessionKey:o,sourceEventKey:Z(r,n,t),cursor:Y(r,n),role:s,ts:i,payload:c,title:f,modelId:a,workDir:u,terminal:m,repairHint:p})}function x(e,r,n,t,o,i,c,f,a,u,s,m){C(e,r,n,t,o,i,X(c,s,m),f,a,u)}function it(e,r,n,t,o,i,c,f,a){const u=typeof t.type=="string"?t.type:"";if(u){if(u==="message"){const s=z(t);if(!s||W(e,s.role,s.payload,i)>=0)return;C(e,r,n,`${u}:${s.role}`,o,i,s.payload,c,f,a,s.role,!1,s.repairHint);return}if(u==="function_call"){const s=typeof t.name=="string"?t.name:"function_call";x(e,r,n,u,o,i,s,c,f,a,P(t.arguments));return}if(u==="function_call_output"){x(e,r,n,u,o,i,"function_call_output",c,f,a,t.call_id,t.output);return}if(u==="custom_tool_call"){const s=typeof t.name=="string"?t.name:"custom_tool_call",m=t.input!==void 0?{input:t.input}:void 0;x(e,r,n,u,o,i,s,c,f,a,m);return}if(u==="custom_tool_call_output"){const s=typeof t.name=="string"?t.name:"custom_tool_call_output",m=t.output??t.result??t.content;x(e,r,n,u,o,i,s,c,f,a,t.call_id,m);return}if(u==="web_search_call"){const s=typeof t.action=="object"&&t.action!==null?t.action:null;x(e,r,n,u,o,i,"web_search",c,f,a,s??t.query);return}}}function st(e,r,n,t,o,i,c,f,a){const u=typeof t.type=="string"?t.type:"";if(u){if(u==="user_message"){const s=F(t);if(!s)return;C(e,r,n,u,o,i,s.payload,c,f,a,"user",!1,s.repairHint);return}if(u==="agent_message"){const s=typeof t.message=="string"?d(t.message):"";if(!s||j(s))return;C(e,r,n,u,o,i,s,c,f,a);return}if(u==="turn_aborted"||u==="error"){const s=typeof t.message=="string"?d(t.message):typeof t.error=="string"?d(t.error):"";if(!s)return;C(e,r,n,u,o,i,s,c,f,a,"agent",!0);return}if(et(u)){C(e,r,n,u,o,i,nt(t),c,f,a,"agent");return}if(u==="exec_command_end"){const s=Array.isArray(t.command)?t.command:t.parsed_cmd??t.command,m=t.exit_code??t.exitCode,p=[typeof t.aggregated_output=="string"?t.aggregated_output:"",m!=null?`
|
|
6
|
+
(exit ${String(m)})`:""].join("");x(e,r,n,u,o,i,"command_execution",c,f,a,{command:s},p);return}if(u==="patch_apply_end"){x(e,r,n,u,o,i,"apply_patch",c,f,a,t.changes,{success:t.success,stdout:t.stdout,stderr:t.stderr});return}if(u==="mcp_tool_call_end"){const s=typeof t.invocation=="object"&&t.invocation!==null?t.invocation:null,m=typeof s?.tool=="string"?s.tool:"mcp_tool_call",p={server:s?.server,...typeof s?.arguments=="object"&&s.arguments!==null?s.arguments:{}};x(e,r,n,u,o,i,m,c,f,a,p,t.result);return}if(u==="dynamic_tool_call_request"){const s=typeof t.tool=="string"?t.tool:"dynamic_tool_call";x(e,r,n,u,o,i,s,c,f,a,t.arguments);return}if(u==="dynamic_tool_call_response"){const s=typeof t.tool=="string"?t.tool:"dynamic_tool_call";x(e,r,n,u,o,i,s,c,f,a,t.callId??t.call_id,t.result??t.output);return}if(u==="web_search_end"){const s=typeof t.action=="object"&&t.action!==null?t.action:null;x(e,r,n,u,o,i,"web_search",c,f,a,s??t.query,t.query);return}if(u==="image_generation_end"){const s=typeof t.saved_path=="string"?t.saved_path.trim():"";if(!s)return;C(e,r,n,u,o,i,V(s),c,f,a);return}u==="view_image_tool_call"&&x(e,r,n,u,o,i,"view_image",c,f,a,t.path??t.image_path??t)}}function A(e=!1){return{sourceSessionKey:"",workDir:"",modelId:null,title:"",skipImport:e}}function H(e){if((typeof e.thread_source=="string"?e.thread_source.trim().toLowerCase():"")==="subagent")return!0;const n=typeof e.source=="object"&&e.source!==null?e.source:null;if(n&&typeof n.subagent=="object"&&n.subagent!==null)return!0;const t=typeof e.parent_thread_id=="string"&&e.parent_thread_id.trim().length>0,o=typeof e.agent_nickname=="string"||typeof e.agent_role=="string"||typeof e.multi_agent_version=="string";return t&&o}function ut(e){const r=T.openSync(e,"r");try{const n=[];let t=0,o="";for(;t<1024*1024;){const a=Buffer.alloc(16384),u=T.readSync(r,a,0,a.length,t);if(u<=0)break;const s=a.subarray(0,u);n.push(s),t+=u;const m=Buffer.concat(n),p=m.indexOf(10);if(p>=0){o=m.subarray(0,p).toString("utf8");break}}!o&&n.length>0&&(o=Buffer.concat(n).toString("utf8"));const i=I(o);if(!i||i.type!=="session_meta")return A();const c=typeof i.payload=="object"&&i.payload!==null?i.payload:null;if(!c)return A();if(H(c))return A(!0);const f=typeof c.id=="string"?c.id:"";return{sourceSessionKey:f,workDir:typeof c.cwd=="string"?c.cwd:"",modelId:L(c),title:J(f)??"",skipImport:!1}}finally{T.closeSync(r)}return A()}function L(e){return(typeof e.model=="string"?e.model.trim():"")||null}function ct(e,r){if((typeof e.id=="string"?e.id:typeof e.thread_id=="string"?e.thread_id:typeof e.session_id=="string"?e.session_id:"")!==r)return null;const o=(typeof e.thread_name=="string"?e.thread_name:typeof e.title=="string"?e.title:"").trim();return o?o.slice(0,120):null}function J(e){if(!e)return null;const r=b.join(v.homedir(),".codex","session_index.jsonl");if(!T.existsSync(r))return null;let n=null;return N(r,0,t=>{if(!t.trim())return;const o=I(t);if(!o)return;const i=ct(o,e);i&&(n=i)}),n}function gt(){const e=[b.join(v.homedir(),".codex","sessions"),b.join(v.homedir(),".codex","archived_sessions")],r=[],n=t=>{for(const o of T.readdirSync(t,{withFileTypes:!0})){const i=b.join(t,o.name);o.isDirectory()?n(i):o.isFile()&&o.name.endsWith(".jsonl")&&r.push(i)}};for(const t of e)T.existsSync(t)&&n(t);return[...new Set(r)].sort()}function _t(e,r){const n=[];let{sourceSessionKey:t,workDir:o,modelId:i,title:c,skipImport:f}=ut(e);if(f)return{nextOffset:T.statSync(e).size,events:[]};let a=!1;return{nextOffset:N(e,r,(s,m)=>{const p=I(s);if(!p)return;const S=typeof p.type=="string"?p.type:"",l=typeof p.payload=="object"&&p.payload!==null?p.payload:null,M=D(p.timestamp);if(!(!l||!M)){if(S==="session_meta"){if(H(l)){n.length=0,f=!0;return}t=typeof l.id=="string"?l.id:t,o=typeof l.cwd=="string"?l.cwd:o,i=L(l)??i,c||(c=J(t)??"");return}if(!f&&t){if(S==="response_item"){const y=z(l);y?.role==="user"&&y.titleText&&!c&&(c=y.titleText.slice(0,80)),it(n,e,m,l,t,M,c,i,o);return}if(S==="event_msg"){const y=typeof l.type=="string"?l.type:"";if(tt(y)){let g=!1;for(let _=n.length-1;_>=0;_-=1){const h=n[_];if(h?.agentType==="codex"&&h.role==="agent"&&!q(h.payload)){h.terminal=!0,g=!0,a=!1;break}}if(!g)for(let _=n.length-1;_>=0;_-=1){const h=n[_];if(!(h?.agentType!=="codex"||h.role!=="agent")){h.terminal=!0,g=!0,a=!1;break}}g||(a=!0);return}if(y==="user_message"){const g=F(l);if(g?.titleText&&!c&&(c=g.titleText.slice(0,80)),g){const _=W(n,"user",g.payload,M);_>=0&&n.splice(_,1)}}else if(y==="agent_message"){const g=typeof l.message=="string"?d(l.message):"";if(g&&!j(g)){const _=W(n,"agent",g,M);_>=0&&n.splice(_,1)}}const G=n.length;st(n,e,m,l,t,M,c,i,o),a&&n.length>G&&(n[n.length-1].terminal=!0,a=!1)}}}}),events:n}}export{gt as listCodexRolloutFiles,_t as parseCodexRolloutChunk};
|
|
@@ -2,9 +2,11 @@ import type { CliRelayClient } from '../relay/client.js';
|
|
|
2
2
|
export declare class NativeSessionFusionService {
|
|
3
3
|
private client;
|
|
4
4
|
private timer;
|
|
5
|
+
private activeTimer;
|
|
5
6
|
private scanPromise;
|
|
6
7
|
private pendingClaims;
|
|
7
8
|
private suppressionWindows;
|
|
9
|
+
private externalObservations;
|
|
8
10
|
constructor(client: CliRelayClient);
|
|
9
11
|
start(): void;
|
|
10
12
|
stop(): void;
|
|
@@ -17,11 +19,17 @@ export declare class NativeSessionFusionService {
|
|
|
17
19
|
sourceSessionKey?: string | null;
|
|
18
20
|
text: string;
|
|
19
21
|
managedEchoPolicy?: 'suppress_following' | 'import_following';
|
|
22
|
+
externalRunId?: string | null;
|
|
20
23
|
}): void;
|
|
21
24
|
noteManagedSourceSession(sessionId: string, agentType: string, sourceSessionKey: string | null): void;
|
|
25
|
+
private ensureActiveScanning;
|
|
26
|
+
private emitExternalActivity;
|
|
27
|
+
private emitExternalTerminal;
|
|
22
28
|
private pruneState;
|
|
23
29
|
scanNow(): Promise<void>;
|
|
24
30
|
private runScan;
|
|
31
|
+
private refreshExternalObservations;
|
|
25
32
|
private tryClaimManagedEcho;
|
|
33
|
+
private observeExternalLifecycle;
|
|
26
34
|
private isSuppressed;
|
|
27
35
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{randomUUID as
|
|
2
|
-
`).trim()}function N(
|
|
1
|
+
import{randomUUID as I}from"node:crypto";import S from"node:fs";import{listClaudeTranscriptFiles as v,listCodexRolloutFiles as A,listOpenCodeSessionFiles as T,parseClaudeTranscriptChunk as w,parseCodexRolloutChunk as M,parseOpenCodeSessionFile as E}from"./parsers.js";import{loadNativeScannerState as C,saveNativeScannerState as _}from"./state.js";const g=6e4,m=g*2,b=30*6e4,y=20,P=2e3,O=3e4,d=240*6e4;function x(a){return a.replace(/\r\n/g,`
|
|
2
|
+
`).trim()}function N(a){return a.split(/[\\/]+/).includes(".codex")}class L{client;timer=null;activeTimer=null;scanPromise=null;pendingClaims=new Map;suppressionWindows=new Map;externalObservations=new Map;constructor(e){this.client=e}start(){this.timer||(this.timer=setInterval(()=>{this.scanNow().catch(e=>{console.error("[native-fusion] periodic scan failed",e)})},g))}stop(){this.timer&&clearInterval(this.timer),this.timer=null,this.activeTimer&&clearInterval(this.activeTimer),this.activeTimer=null}handleConnected(){this.start(),this.scanNow().catch(e=>{console.error("[native-fusion] initial scan failed",e)})}registerManagedSend(e){if(!e.canonicalMessageId)return;const t=e.sourceAgentType??e.agentType;if(t!=="codex"&&t!=="claude"&&t!=="opencode")return;const n=e.canonicalMessageId;if(this.pendingClaims.set(n,{sessionId:e.sessionId,agentType:e.agentType,sourceAgentType:t,canonicalMessageId:e.canonicalMessageId,text:x(e.text),createdAt:Date.now(),expiresAt:Date.now()+(e.externalRunId?d:m),sourceSessionKey:e.sourceSessionKey??null,managedEchoPolicy:e.managedEchoPolicy??"suppress_following",externalRunId:e.externalRunId??null}),e.externalRunId&&e.sourceSessionKey){const s=`${t}:${e.sourceSessionKey}`,i=this.externalObservations.get(s)??[];i.push({sessionId:e.sessionId,sourceSessionKey:e.sourceSessionKey,runId:e.externalRunId,canonicalMessageId:e.canonicalMessageId,state:"queued",createdAt:Date.now(),lastPublishedAt:Date.now()}),this.externalObservations.set(s,i),this.emitExternalActivity(i[i.length-1],"queued"),this.ensureActiveScanning()}}noteManagedSourceSession(e,t,n){if(n)for(const s of this.pendingClaims.values())s.sessionId===e&&s.sourceAgentType===t&&(s.sourceSessionKey=n)}ensureActiveScanning(){this.activeTimer||(this.activeTimer=setInterval(()=>{if(this.externalObservations.size===0){this.activeTimer&&clearInterval(this.activeTimer),this.activeTimer=null;return}this.scanNow().catch(e=>{console.error("[native-fusion] active observation scan failed",e)})},P),this.scanNow().catch(()=>{}))}emitExternalActivity(e,t){this.client.sendAgentEvent({type:"event",event:"agent",id:`external-owner-${e.runId}-${t}-${e.lastPublishedAt}`,payload:{state:t==="queued"?"start":"heartbeat",sessionId:e.sessionId,runId:e.runId,seq:t==="queued"?0:1,runPhase:"thinking",canStop:!1,owner:"external",ownerLabel:"Codex Desktop",lifecycle:t}})}emitExternalTerminal(e){this.client.sendAgentEvent({type:"event",event:"agent",id:`external-owner-${e.runId}-final`,payload:{state:"final",sessionId:e.sessionId,runId:e.runId,seq:2,owner:"external",ownerLabel:"Codex Desktop"}})}pruneState(){const e=Date.now();for(const[t,n]of this.pendingClaims.entries())n.expiresAt<=e&&this.pendingClaims.delete(t);for(const[t,n]of this.suppressionWindows.entries())n.endsAt<=e&&this.suppressionWindows.delete(t)}async scanNow(){if(this.client.getState()==="connected")return this.scanPromise?this.scanPromise:(this.scanPromise=this.runScan().finally(()=>{this.scanPromise=null}),this.scanPromise)}async runScan(){this.pruneState(),this.refreshExternalObservations();const e=C(),t={...e.files},n=[],s=[...A(),...v(),...T()];for(const i of s){const o=S.statSync(i),r=e.files[i];if(!r){t[i]={offset:o.size,mtimeMs:o.mtimeMs};continue}const h=N(i),c=i.endsWith(".opencode-session.json"),l=c&&r?.mtimeMs!==o.mtimeMs?r.offset:r?.offset??0,f=c?E(i,l):h?M(i,l):w(i,l);t[i]={offset:f.nextOffset,mtimeMs:o.mtimeMs};for(const u of f.events){const p=this.tryClaimManagedEcho(u);if(p){n.push(p);continue}this.isSuppressed(u)||n.push(u)}}for(let i=0;i<n.length;i+=y){const o=n.slice(i,i+y);if(o.length===0)continue;const r=o.map(({repairHint:h,...c})=>c);await this.client.sendBufferedEvent({type:"event",event:"native.session.events",id:`native-fusion-${I()}`,payload:{events:r}},6e4)}for(const i of n)this.observeExternalLifecycle(i);e.files=t,_(e)}refreshExternalObservations(){const e=Date.now();for(const[t,n]of this.externalObservations.entries()){const s=n.filter(o=>e-o.createdAt<d);if(s.length===0){this.externalObservations.delete(t);continue}this.externalObservations.set(t,s);const i=s[0];e-i.lastPublishedAt<O||(i.lastPublishedAt=e,this.emitExternalActivity(i,i.state))}}tryClaimManagedEcho(e){const t=Date.now();for(const[n,s]of this.pendingClaims.entries()){if(s.sourceAgentType!==e.agentType||s.expiresAt<=t||e.role!=="user"||!s.sourceSessionKey||s.sourceSessionKey!==e.sourceSessionKey)continue;const i=s.externalRunId?d:m;if(!(Math.abs(e.ts-s.createdAt)>i)&&x(e.payload)===s.text)return this.pendingClaims.delete(n),s.managedEchoPolicy==="suppress_following"&&this.suppressionWindows.set(`${e.agentType}:${e.sourceSessionKey}`,{sessionId:s.sessionId,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,startedAt:e.ts,endsAt:Math.max(t,e.ts)+b}),{...e,sourceMode:"managed_echo_claim",claimCanonicalMessageId:s.canonicalMessageId,managedEchoPolicy:s.managedEchoPolicy}}return null}observeExternalLifecycle(e){const t=`${e.agentType}:${e.sourceSessionKey}`,n=this.externalObservations.get(t);if(!n?.length)return;const s=n[0];e.claimCanonicalMessageId===s.canonicalMessageId&&s.state==="queued"&&(s.state="running",s.lastPublishedAt=Date.now(),this.emitExternalActivity(s,"running")),!(!e.terminal||s.state!=="running")&&(this.emitExternalTerminal(s),n.shift(),n.length===0?this.externalObservations.delete(t):(this.externalObservations.set(t,n),n[0].lastPublishedAt=Date.now(),this.emitExternalActivity(n[0],"queued")))}isSuppressed(e){const t=`${e.agentType}:${e.sourceSessionKey}`,n=this.suppressionWindows.get(t);return!n||e.ts<n.startedAt?!1:e.ts>n.endsAt?(this.suppressionWindows.delete(t),!1):e.role==="user"?(this.suppressionWindows.delete(t),!1):!0}}export{L as NativeSessionFusionService};
|
|
@@ -21,6 +21,16 @@ export type PendingManagedClaim = {
|
|
|
21
21
|
expiresAt: number;
|
|
22
22
|
sourceSessionKey?: string | null;
|
|
23
23
|
managedEchoPolicy: 'suppress_following' | 'import_following';
|
|
24
|
+
externalRunId?: string | null;
|
|
25
|
+
};
|
|
26
|
+
export type ExternalOwnerObservation = {
|
|
27
|
+
sessionId: string;
|
|
28
|
+
sourceSessionKey: string;
|
|
29
|
+
runId: string;
|
|
30
|
+
canonicalMessageId: string;
|
|
31
|
+
state: 'queued' | 'running';
|
|
32
|
+
createdAt: number;
|
|
33
|
+
lastPublishedAt: number;
|
|
24
34
|
};
|
|
25
35
|
export type ManagedSuppressionWindow = {
|
|
26
36
|
sessionId: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&r.chmodSync(o,384),process.platform==="win32"&&r.existsSync(this.filePath)){const s=r.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw b();r.unlinkSync(this.filePath)}r.renameSync(o,this.filePath),process.platform!=="win32"&&r.chmodSync(this.filePath,384)}finally{try{r.unlinkSync(o)}catch{}}}}function f(e){return v(e.machineId),y(e.runtimeAdapter),A(e.sourceSessionKey),
|
|
1
|
+
import p from"node:crypto";import r from"node:fs";import u from"node:path";import{isRoomRuntimeAdapter as P}from"@shennian/wire";import{resolveShennianPath as H}from"../config/index.js";const h=/^[a-f0-9]{64}$/,I=/^[A-Za-z][A-Za-z0-9]{0,63}$/;class n extends Error{code;constructor(t,o){super(o),this.code=t,this.name="RoomHostSessionObservationError"}}class C{filePath;state;constructor(t=H("runtime","room-host-session-observations.json")){this.filePath=t,this.state=this.load()}observe(t){S(t);const o=f(t),s=this.state.records[o],a=(t.now??new Date).toISOString(),l=!!(s&&t.eventName==="SessionStart"&&(s.lifecycleState==="closed"||s.hostContextHash!==t.hostContextHash)),c=s?.lifecycleState==="closed"&&t.eventName!=="SessionStart"?"closed":t.lifecycleState,k=l?null:s?.lastActiveAt??null,_=l?null:s?.lastIdleAt??null,d={schemaVersion:1,observationKey:o,machineId:t.machineId,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:t.hostContextHash,workspaceHash:t.workspaceHash??s?.workspaceHash??null,workspacePath:t.workspacePath??s?.workspacePath??null,lifecycleState:c,registrationEpoch:s?s.registrationEpoch+(l?1:0):1,lastEventName:t.eventName,observedAt:s?.observedAt??a,lastSeenAt:a,lastActiveAt:c==="active"?a:k,lastIdleAt:c==="idle"?a:_,closedAt:c==="closed"?s?.closedAt??a:null};return this.state.records[o]=d,this.persist(),m(d)}getBySource(t){const o=this.state.records[f(t)];return o?m(o):null}list(){return Object.values(this.state.records).sort((t,o)=>t.observedAt.localeCompare(o.observedAt)).map(m)}load(){try{const t=r.lstatSync(this.filePath);if(!t.isFile()||t.isSymbolicLink())throw b();return process.platform!=="win32"&&(t.mode&63)!==0&&r.chmodSync(this.filePath,384),g(JSON.parse(r.readFileSync(this.filePath,"utf8")))}catch(t){if(t.code==="ENOENT")return{schemaVersion:1,records:{}};throw t instanceof n?t:new n("corrupt_observation_store","Room host Session observations are corrupt.")}}persist(){const t=u.dirname(this.filePath);r.mkdirSync(t,{recursive:!0,mode:448}),process.platform!=="win32"&&r.chmodSync(t,448);const o=`${this.filePath}.tmp-${process.pid}-${p.randomBytes(6).toString("hex")}`;try{if(r.writeFileSync(o,`${JSON.stringify(this.state,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&r.chmodSync(o,384),process.platform==="win32"&&r.existsSync(this.filePath)){const s=r.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw b();r.unlinkSync(this.filePath)}r.renameSync(o,this.filePath),process.platform!=="win32"&&r.chmodSync(this.filePath,384)}finally{try{r.unlinkSync(o)}catch{}}}}function f(e){return v(e.machineId),y(e.runtimeAdapter),A(e.sourceSessionKey),p.createHash("sha256").update(`${e.machineId}\0${e.runtimeAdapter}\0${e.sourceSessionKey}`,"utf8").digest("hex")}function S(e){v(e.machineId),y(e.runtimeAdapter),A(e.sourceSessionKey),h.test(e.hostContextHash)||i("host_context_hash"),e.workspaceHash!=null&&!h.test(e.workspaceHash)&&i("workspace_hash"),e.workspacePath!=null&&w(e.workspacePath),["active","idle","closed"].includes(e.lifecycleState)||i("lifecycle_state"),I.test(e.eventName)||i("event_name")}function v(e){/^[A-Za-z0-9_.:@/-]{1,512}$/.test(e)||i("machine_id")}function y(e){P(e)||i("runtime_adapter")}function A(e){(!e||e.length>1024||/[\0\r\n]/.test(e))&&i("source_session_key")}function g(e){if(!e||typeof e!="object"||e.schemaVersion!==1)throw new n("corrupt_observation_store","Unsupported observation store.");const t=e.records;if(!t||typeof t!="object"||Array.isArray(t))throw new n("corrupt_observation_store","Invalid observation records.");const o={};for(const[s,a]of Object.entries(t)){if(x(a),s!==a.observationKey||s!==f(a))throw new n("corrupt_observation_store","Observation key mismatch.");o[s]={...a,workspacePath:a.workspacePath??null,lastActiveAt:a.lastActiveAt??(a.lifecycleState==="active"?a.lastSeenAt:null),lastIdleAt:a.lastIdleAt??(a.lifecycleState==="idle"?a.lastSeenAt:null)}}return{schemaVersion:1,records:o}}function x(e){(!e||e.schemaVersion!==1)&&i("observation"),S({machineId:e.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,hostContextHash:e.hostContextHash,workspaceHash:e.workspaceHash,workspacePath:e.workspacePath??null,lifecycleState:e.lifecycleState,eventName:e.lastEventName}),h.test(e.observationKey)||i("observation_key"),(!Number.isInteger(e.registrationEpoch)||e.registrationEpoch<1)&&i("registration_epoch");for(const t of[e.observedAt,e.lastSeenAt])Number.isFinite(Date.parse(t))||i("observation_time");for(const t of[e.lastActiveAt,e.lastIdleAt])t!=null&&!Number.isFinite(Date.parse(t))&&i("transition_time");e.closedAt!=null&&!Number.isFinite(Date.parse(e.closedAt))&&i("closed_at"),e.workspacePath!=null&&w(e.workspacePath)}function w(e){(!u.isAbsolute(e)||e.length>4096||/[\0\r\n]/.test(e))&&i("workspace_path")}function i(e){throw new n(`invalid_${e}`,`Invalid ${e}.`)}function b(){return new n("unsafe_observation_store","Room host Session observation store is not a regular file.")}function m(e){return structuredClone(e)}export{n as RoomHostSessionObservationError,C as RoomHostSessionObservationStore,f as buildRoomHostSessionObservationKey};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"node:crypto";import t from"node:fs";import a from"node:path";import n from"qrcode";import{resolveShennianPath as p}from"../config/index.js";async function f(e,r=p("room-invites")){t.mkdirSync(r,{recursive:!0,mode:448});const m=i.createHash("sha256").update(e).digest("hex").slice(0,24),o=a.resolve(r,`room-invite-${m}.png`);return await n.toFile(o,e,{type:"png",width:640,margin:4,errorCorrectionLevel:"M"}),t.chmodSync(o,384),{imagePath:o,mimeType:"image/png",payload:e}}export{f as createRoomInviteQr};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AgentType } from '@shennian/wire';
|
|
2
2
|
import type { HttpRoomBindingClient } from './room-binding-client.js';
|
|
3
3
|
import type { RoomHostSessionObservationStore } from './host-session-observations.js';
|
|
4
4
|
import type { RoomSessionRegistry } from './session-registry.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import y from"node:crypto";class
|
|
1
|
+
import{roomRuntimeAdapterForAgent as y}from"@shennian/wire";import I from"node:crypto";class b{deps;constructor(e){this.deps=e}async bind(e){const{machineId:n,machineToken:o}=this.deps.getMachineCredentials();if(!n||!o)throw new Error("Machine is not paired with Shennian.");const t=c(e.agentType),d=this.deps.hostProcessIdentity(),s=this.deps.registry.register({machineId:n,runtimeAdapter:t,sourceSessionKey:e.agentSessionId,nianSessionId:e.nianSessionId,hostProcessIdentity:d,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}});this.deps.observations.observe({machineId:n,runtimeAdapter:t,sourceSessionKey:e.agentSessionId,hostContextHash:g(d),workspaceHash:g(e.workDir),workspacePath:e.workDir,lifecycleState:"active",eventName:"ManagedBind"});const i=await this.deps.bindingClient.create({machineToken:o,bindingIntentId:e.assignmentIntentId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,nianSessionId:e.nianSessionId,runtimeAdapter:t,sessionRegistryKey:s.registryKey,activationCapability:"managed",activationMode:"auto_follow"});return this.deps.registry.setBinding({registryKey:s.registryKey,bindingId:i.binding.id,roomId:i.binding.roomId,agentIdentityId:i.binding.agentIdentityId,agentMembershipId:i.binding.agentMembershipId,status:i.binding.status,activationMode:i.binding.activationMode}),i}async restart(e){const{machineId:n,machineToken:o}=this.deps.getMachineCredentials();if(!n||!o)throw new Error("Machine is not paired with Shennian.");const t=c(e.agentType),d=this.deps.hostProcessIdentity(),s=this.deps.registry.register({machineId:n,runtimeAdapter:t,sourceSessionKey:e.agentSessionId,nianSessionId:e.nianSessionId,hostProcessIdentity:d,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:!0,canActivateWhileHostOpen:!0}});this.deps.observations.observe({machineId:n,runtimeAdapter:t,sourceSessionKey:e.agentSessionId,hostContextHash:g(d),workspaceHash:g(e.workDir),workspacePath:e.workDir,lifecycleState:"active",eventName:"ManagedRestart"});const i=await this.deps.bindingClient.restart({machineToken:o,bindingId:e.bindingId,previousNianSessionId:e.previousNianSessionId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,nianSessionId:e.nianSessionId,runtimeAdapter:t,sessionRegistryKey:s.registryKey});this.deps.registry.setBinding({registryKey:s.registryKey,bindingId:i.binding.id,roomId:i.binding.roomId,agentIdentityId:i.binding.agentIdentityId,agentMembershipId:i.binding.agentMembershipId,status:i.binding.status,activationMode:i.binding.activationMode}),this.deps.registry.advanceCursor({registryKey:s.registryKey,cursor:"delivered",sequence:i.resumeSequence}),this.deps.registry.advanceCursor({registryKey:s.registryKey,cursor:"activated",sequence:i.resumeSequence});for(const a of this.deps.registry.list())a.registryKey!==s.registryKey&&a.nianSessionId===e.previousNianSessionId&&a.binding?.bindingId===e.bindingId&&this.deps.registry.detachBinding(a.registryKey);return i}}function g(r){return I.createHash("sha256").update(r,"utf8").digest("hex")}function c(r){const e=y(r);if(e)return e;throw new Error(`Agent ${r} does not support managed Room binding.`)}export{b as TrustedManagedRoomBinder};
|
|
@@ -7,7 +7,8 @@ export declare class PublicRoomClient {
|
|
|
7
7
|
private readonly fetchImpl;
|
|
8
8
|
constructor(fetchImpl?: typeof fetch);
|
|
9
9
|
create(name: string): Promise<Record<string, unknown>>;
|
|
10
|
-
join(inviteToken: string): Promise<Record<string, unknown>>;
|
|
10
|
+
join(roomId: string, inviteToken: string): Promise<Record<string, unknown>>;
|
|
11
|
+
open(roomId: string): Promise<Record<string, unknown>>;
|
|
11
12
|
status(roomId: string): Promise<Record<string, unknown>>;
|
|
12
13
|
read(roomId: string, input: {
|
|
13
14
|
before?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{loadConfig as w}from"../config/index.js";import{SERVERS as y}from"../region.js";class r extends Error{code;nextAction;constructor(e,t,n=null){super(t),this.code=e,this.nextAction=n}}class b{fetchImpl;constructor(e=fetch){this.fetchImpl=e}create(e){return this.request("POST","/api/room-cli/create",{name:e})}join(e){return this.request("POST","/api/room-cli/join",{inviteToken:e})}status(e){return this.request("GET",`/api/room-cli/${encodeURIComponent(e)}/status`)}read(e,t){const n=new URLSearchParams({limit:String(t.limit)});return t.before&&n.set("before",t.before),t.after&&n.set("after",t.after),this.request("GET",`/api/room-cli/${encodeURIComponent(e)}/messages?${n}`)}send(e,t){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/messages`,t,{retryNetworkOnce:!0})}initializeAttachment(e,t){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/attachments/init`,t)}completeAttachment(e,t){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/attachments/${encodeURIComponent(t)}/complete`,{})}getAttachmentDownload(e,t){return this.request("GET",`/api/room-cli/${encodeURIComponent(e)}/attachments/${encodeURIComponent(t)}/download`)}leave(e){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/leave`,{})}async upload(e,t,n){let s;try{s=await this.fetchImpl(e,{method:"PUT",headers:t,body:n,redirect:"error",signal:AbortSignal.timeout(12e4)})}catch{throw new r("attachment_upload_failed","Attachment upload failed.")}if(!s.ok)throw new r("attachment_upload_failed","Attachment upload failed.")}async download(e){try{const t=await this.fetchImpl(e,{redirect:"error",signal:AbortSignal.timeout(6e4)});if(!t.ok||!t.body)throw new r("attachment_download_failed","Attachment download failed.");return t}catch(t){throw t instanceof r?t:new r("attachment_download_failed","Attachment download failed.")}}async request(e,t,n,s={}){const l=w();if(!l.machineToken)throw new r("not_paired","Shennian CLI is not paired.",{type:"run_command",command:"shennian pair --browser"});const d=(l.serverUrl??y.cn.url).replace(/\/+$/,""),h=()=>this.fetchImpl(`${d}${t}`,{method:e,headers:{authorization:`Bearer ${l.machineToken}`,...n==null?{}:{"content-type":"application/json"}},...n==null?{}:{body:JSON.stringify(n)},signal:AbortSignal.timeout(2e4)});let i;try{i=await h()}catch{if(!s.retryNetworkOnce)throw new r("server_unavailable","Shennian service is unavailable.");try{i=await h()}catch{throw new r("server_unavailable","Shennian service is unavailable.")}}const c=await S(i);if(!i.ok){const a=m(c)?c:{},u=typeof a.error=="string"?a.error:`http_${i.status}`,p=typeof a.message=="string"?a.message:"Room request failed.",f=g(a.nextAction)?a.nextAction:null;throw new r(u,p,f)}if(!m(c))throw new r("invalid_server_response","Shennian returned an invalid Room response.");return c}}function g(o){return m(o)&&Object.values(o).every(e=>typeof e=="string")}async function S(o){try{return await o.json()}catch{return null}}function m(o){return!!o&&typeof o=="object"&&!Array.isArray(o)}export{b as PublicRoomClient,r as PublicRoomClientError};
|
|
1
|
+
import{loadConfig as w}from"../config/index.js";import{SERVERS as y}from"../region.js";class r extends Error{code;nextAction;constructor(e,t,n=null){super(t),this.code=e,this.nextAction=n}}class b{fetchImpl;constructor(e=fetch){this.fetchImpl=e}create(e){return this.request("POST","/api/room-cli/create",{name:e})}join(e,t){return this.request("POST","/api/room-cli/join",{roomId:e,inviteToken:t})}open(e){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/open`,{})}status(e){return this.request("GET",`/api/room-cli/${encodeURIComponent(e)}/status`)}read(e,t){const n=new URLSearchParams({limit:String(t.limit)});return t.before&&n.set("before",t.before),t.after&&n.set("after",t.after),this.request("GET",`/api/room-cli/${encodeURIComponent(e)}/messages?${n}`)}send(e,t){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/messages`,t,{retryNetworkOnce:!0})}initializeAttachment(e,t){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/attachments/init`,t)}completeAttachment(e,t){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/attachments/${encodeURIComponent(t)}/complete`,{})}getAttachmentDownload(e,t){return this.request("GET",`/api/room-cli/${encodeURIComponent(e)}/attachments/${encodeURIComponent(t)}/download`)}leave(e){return this.request("POST",`/api/room-cli/${encodeURIComponent(e)}/leave`,{})}async upload(e,t,n){let s;try{s=await this.fetchImpl(e,{method:"PUT",headers:t,body:n,redirect:"error",signal:AbortSignal.timeout(12e4)})}catch{throw new r("attachment_upload_failed","Attachment upload failed.")}if(!s.ok)throw new r("attachment_upload_failed","Attachment upload failed.")}async download(e){try{const t=await this.fetchImpl(e,{redirect:"error",signal:AbortSignal.timeout(6e4)});if(!t.ok||!t.body)throw new r("attachment_download_failed","Attachment download failed.");return t}catch(t){throw t instanceof r?t:new r("attachment_download_failed","Attachment download failed.")}}async request(e,t,n,s={}){const l=w();if(!l.machineToken)throw new r("not_paired","Shennian CLI is not paired.",{type:"run_command",command:"shennian pair --browser"});const d=(l.serverUrl??y.cn.url).replace(/\/+$/,""),h=()=>this.fetchImpl(`${d}${t}`,{method:e,headers:{authorization:`Bearer ${l.machineToken}`,...n==null?{}:{"content-type":"application/json"}},...n==null?{}:{body:JSON.stringify(n)},signal:AbortSignal.timeout(2e4)});let i;try{i=await h()}catch{if(!s.retryNetworkOnce)throw new r("server_unavailable","Shennian service is unavailable.");try{i=await h()}catch{throw new r("server_unavailable","Shennian service is unavailable.")}}const c=await S(i);if(!i.ok){const a=m(c)?c:{},u=typeof a.error=="string"?a.error:`http_${i.status}`,p=typeof a.message=="string"?a.message:"Room request failed.",f=g(a.nextAction)?a.nextAction:null;throw new r(u,p,f)}if(!m(c))throw new r("invalid_server_response","Shennian returned an invalid Room response.");return c}}function g(o){return m(o)&&Object.values(o).every(e=>typeof e=="string")}async function S(o){try{return await o.json()}catch{return null}}function m(o){return!!o&&typeof o=="object"&&!Array.isArray(o)}export{b as PublicRoomClient,r as PublicRoomClientError};
|
|
@@ -39,7 +39,7 @@ export type RemoteRoomJoinResult = {
|
|
|
39
39
|
room: {
|
|
40
40
|
id: string;
|
|
41
41
|
name: string;
|
|
42
|
-
|
|
42
|
+
canonicalUrl: string;
|
|
43
43
|
};
|
|
44
44
|
agentIdentity: {
|
|
45
45
|
id: string;
|
|
@@ -90,8 +90,8 @@ export type RemoteRoomCreateResult = {
|
|
|
90
90
|
id: string;
|
|
91
91
|
name: string;
|
|
92
92
|
visibility: 'private';
|
|
93
|
-
joinPolicy: '
|
|
94
|
-
|
|
93
|
+
joinPolicy: 'open';
|
|
94
|
+
canonicalUrl: string;
|
|
95
95
|
inviteUrl: string;
|
|
96
96
|
};
|
|
97
97
|
agentIdentity: {
|
|
@@ -114,8 +114,8 @@ export type RemoteOperatorRoomCreateResult = {
|
|
|
114
114
|
id: string;
|
|
115
115
|
name: string;
|
|
116
116
|
visibility: 'private';
|
|
117
|
-
joinPolicy: '
|
|
118
|
-
|
|
117
|
+
joinPolicy: 'open';
|
|
118
|
+
canonicalUrl: string;
|
|
119
119
|
};
|
|
120
120
|
};
|
|
121
121
|
export declare class RoomActionHttpError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import S from"node:path";class c extends Error{code;constructor(e,t){super(t),this.code=e,this.name="RoomActionHttpError"}}class R{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 o=await p(t);if(!t.ok)throw y(t.status,o);return _(o,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 o=await p(t);if(!t.ok)throw y(t.status,o);(!o||typeof o!="object")&&r();const n=o;return(typeof n.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(n.nianSessionId)||typeof n.created!="boolean")&&r(),n}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 o=await p(t);if(!t.ok)throw y(t.status,o);return j(o,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 o=await p(t);if(!t.ok)throw y(t.status,o);return A(o,e,this.baseUrl)}async status(e){const t=await this.request(e,"status");return w(t,e)}async getAttachmentCapability(e){const t=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!t||typeof t!="object")&&r();const o=t;return(o.attachmentId!==e.attachmentId||o.roomId!==e.roomId||typeof o.safeName!="string"||!b(o.safeName)||typeof o.mimeType!="string"||o.mimeType.length<1||o.mimeType.length>191||typeof o.byteSize!="string"||!d(o.byteSize)||typeof o.sha256!="string"||!/^[a-f0-9]{64}$/.test(o.sha256)||!g(o.downloadUrl)||typeof o.expiresAt!="string"||!Number.isFinite(Date.parse(o.expiresAt)))&&r(),o}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 o=t,n=o.attachment,a=o.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 o.idempotent!="boolean")&&r(),o}async completeAttachment(e){const t=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!t||typeof t!="object")&&r();const o=t;typeof o.idempotent!="boolean"&&r();const n=o.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:o.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 o=await this.request(e,`messages?${t}`);(!o||typeof o!="object")&&r();const n=o;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=>h(s,e.roomId)),nextSequence:n.nextSequence,hasMore:n.hasMore,historyFromSequence:n.historyFromSequence,roomName:n.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 o=t;return typeof o.idempotent!="boolean"&&r(),{message:h(o.message,e.roomId),idempotent:o.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 o=t;return(o.bindingId!==e.bindingId||o.roomId!==e.roomId||o.status!=="active"&&o.status!=="paused"||!I(o.activationMode)||o.activationMode!==e.activationMode||typeof o.idempotent!="boolean")&&r(),o}async request(e,t,o={}){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${t}`,{...o,headers:{authorization:`Bearer ${e.machineToken}`,...o.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 t=i,o=t.room,n=t.agent;(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"||!I(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||!S.isAbsolute(t.managedWorkspacePath))||typeof t.lastDeliveredSequence!="string"||!d(t.lastDeliveredSequence)||typeof t.lastActivatedSequence!="string"||!d(t.lastActivatedSequence)||!o||typeof o!="object"||n!==void 0&&(!n||typeof n!="object"))&&r(),(t.status==="pending_approval"&&t.agentMembershipId!==null||(t.status==="active"||t.status==="paused")&&typeof t.agentMembershipId!="string")&&r();const a=o,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(f=>{if(!f||typeof f!="object")return!0;const l=f;return typeof l.displayName!="string"||l.principalType!=="human"&&l.principalType!=="agent"})))&&r(),t}function h(i,e){(!i||typeof i!="object")&&r();const t=i;if((typeof t.id!="string"||t.roomId!==e||typeof t.sequence!="string"||!d(t.sequence)||!t.author||typeof t.author.membershipId!="string"||!Array.isArray(t.attachments)||typeof t.createdAt!="string")&&r(),t.activationCause!=null){const o=t.activationCause;(typeof o.causeMessageId!="string"||typeof o.rootCauseMessageId!="string"||!Number.isInteger(o.hopCount)||o.hopCount<1||o.hopCount>32)&&r()}return t}function j(i,e,t){(!i||typeof i!="object")&&r();const o=i,n=o.room,a=o.agentIdentity,s=o.binding;return(o.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!u(n.webUrl,t,`/spaces/${n.id}`)||!q(n.inviteUrl,t)||!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 o.idempotent!="boolean")&&r(),o}function _(i,e,t){(!i||typeof i!="object")&&r();const o=i,n=o.room;return(o.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!u(n.webUrl,t,`/spaces/${n.id}`))&&r(),o}function A(i,e,t){(!i||typeof i!="object")&&r();const o=i,n=o.room,a=o.agentIdentity,s=o.binding;return(o.outcome!=="joined"&&o.outcome!=="pending_approval"||!n||typeof n.id!="string"||typeof n.name!="string"||!u(n.webUrl,t,`/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 o.idempotent!="boolean")&&r(),(o.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof o.joinRequestId!="string")||o.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||o.joinRequestId!==void 0))&&r(),o}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 t=`room_action_http_${i}`;if(e&&typeof e=="object"){const o=e.error;typeof o=="string"&&/^[a-z0-9_-]{1,80}$/.test(o)&&(t=o)}return new c(t,"Shennian Room action failed.")}function u(i,e,t){return v(i,e,o=>o.pathname===t)}function q(i,e){return v(i,e,t=>/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(t.pathname))}function v(i,e,t){if(typeof i!="string")return!1;try{const o=new URL(i),n=new URL(e),a=o.origin===n.origin,s=n.protocol==="http:"&&o.protocol==="http:"&&o.hostname===n.hostname,m=o.protocol==="https:"&&o.port===""&&(o.hostname==="shennian.net"||o.hostname==="app.shennian.net");return(a||s||m)&&!o.username&&!o.password&&!o.search&&!o.hash&&t(o)}catch{return!1}}export{R as HttpRoomActionClient,c as RoomActionHttpError};
|
|
1
|
+
import v from"node:path";class c extends Error{code;constructor(e,n){super(n),this.code=e,this.name="RoomActionHttpError"}}class R{fetchImpl;baseUrl;constructor(e,n=fetch){this.fetchImpl=n,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let n;try{n=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(n);if(!n.ok)throw y(n.status,t);return _(t,e,this.baseUrl)}async resolveNativeSession(e){let n;try{n=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(n);if(!n.ok)throw y(n.status,t);(!t||typeof t!="object")&&r();const o=t;return(typeof o.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(o.nianSessionId)||typeof o.created!="boolean")&&r(),o}async createRoom(e){let n;try{n=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(n);if(!n.ok)throw y(n.status,t);return j(t,e,this.baseUrl)}async joinRoom(e){let n;try{n=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(n);if(!n.ok)throw y(n.status,t);return A(t,e,this.baseUrl)}async status(e){const n=await this.request(e,"status");return w(n,e)}async getAttachmentCapability(e){const n=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!n||typeof n!="object")&&r();const t=n;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 n=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})});(!n||typeof n!="object")&&r();const t=n,o=t.attachment,a=t.upload;return(!o||typeof o.id!="string"||o.roomId!==e.roomId||typeof o.name!="string"||!b(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"||!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 n=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!n||typeof n!="object")&&r();const t=n;typeof t.idempotent!="boolean"&&r();const o=t.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"||!d(o.byteSize)||typeof o.sha256!="string"||!/^[a-f0-9]{64}$/.test(o.sha256)||o.status!=="ready")&&r(),{attachment:o,idempotent:t.idempotent}}async read(e){if(e.beforeSequence&&e.afterSequence)throw new c("ambiguous_sequence_cursor","beforeSequence and afterSequence are mutually exclusive.");const n=new URLSearchParams({limit:String(e.limit)});e.beforeSequence&&n.set("before",e.beforeSequence),e.afterSequence&&n.set("after",e.afterSequence);const t=await this.request(e,`messages?${n}`);(!t||typeof t!="object")&&r();const o=t;return(!Array.isArray(o.messages)||typeof o.nextSequence!="string"||!d(o.nextSequence)||typeof o.hasMore!="boolean"||typeof o.historyFromSequence!="string"||!d(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 n=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})});(!n||typeof n!="object")&&r();const t=n;return typeof t.idempotent!="boolean"&&r(),{message:h(t.message,e.roomId),idempotent:t.idempotent}}async mode(e){const n=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!n||typeof n!="object")&&r();const t=n;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,n,t={}){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${n}`,{...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(!o.ok){let a=`room_action_http_${o.status}`;try{const s=await o.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 o.json()}catch{r()}}}function w(i,e){(!i||typeof i!="object")&&r();const n=i,t=n.room,o=n.agent;(n.bindingId!==e.bindingId||n.roomId!==e.roomId||typeof n.agentIdentityId!="string"||n.agentMembershipId!==null&&typeof n.agentMembershipId!="string"||n.status!=="pending_approval"&&n.status!=="active"&&n.status!=="paused"&&n.status!=="detached"||!I(n.activationMode)||n.activationCapability!==void 0&&n.activationCapability!=="managed"&&n.activationCapability!=="idle_resume"&&n.activationCapability!=="notify_only"||n.managedWorkspacePath!==void 0&&n.managedWorkspacePath!==null&&(typeof n.managedWorkspacePath!="string"||n.managedWorkspacePath.length>4096||!v.isAbsolute(n.managedWorkspacePath))||typeof n.lastDeliveredSequence!="string"||!d(n.lastDeliveredSequence)||typeof n.lastActivatedSequence!="string"||!d(n.lastActivatedSequence)||!t||typeof t!="object"||o!==void 0&&(!o||typeof o!="object"))&&r(),(n.status==="pending_approval"&&n.agentMembershipId!==null||(n.status==="active"||n.status==="paused")&&typeof n.agentMembershipId!="string")&&r();const a=t,s=o,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(f=>{if(!f||typeof f!="object")return!0;const l=f;return typeof l.displayName!="string"||l.principalType!=="human"&&l.principalType!=="agent"})))&&r(),n}function h(i,e){(!i||typeof i!="object")&&r();const n=i;if((typeof n.id!="string"||n.roomId!==e||typeof n.sequence!="string"||!d(n.sequence)||!n.author||typeof n.author.membershipId!="string"||!Array.isArray(n.attachments)||typeof n.createdAt!="string")&&r(),n.activationCause!=null){const t=n.activationCause;(typeof t.causeMessageId!="string"||typeof t.rootCauseMessageId!="string"||!Number.isInteger(t.hopCount)||t.hopCount<1||t.hopCount>32)&&r()}return n}function j(i,e,n){(!i||typeof i!="object")&&r();const t=i,o=t.room,a=t.agentIdentity,s=t.binding;return(t.outcome!=="created"||!o||typeof o.id!="string"||o.name!==e.name||o.visibility!=="private"||o.joinPolicy!=="open"||!u(o.canonicalUrl,n,`/spaces/${o.id}`)||!q(o.inviteUrl,n,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 t.idempotent!="boolean")&&r(),t}function _(i,e,n){(!i||typeof i!="object")&&r();const t=i,o=t.room;return(t.outcome!=="created"||!o||typeof o.id!="string"||o.name!==e.name||o.visibility!=="private"||o.joinPolicy!=="open"||!u(o.canonicalUrl,n,`/spaces/${o.id}`))&&r(),t}function A(i,e,n){(!i||typeof i!="object")&&r();const t=i,o=t.room,a=t.agentIdentity,s=t.binding;return(t.outcome!=="joined"&&t.outcome!=="pending_approval"||!o||typeof o.id!="string"||typeof o.name!="string"||!u(o.canonicalUrl,n,`/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 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 n=`room_action_http_${i}`;if(e&&typeof e=="object"){const t=e.error;typeof t=="string"&&/^[a-z0-9_-]{1,80}$/.test(t)&&(n=t)}return new c(n,"Shennian Room action failed.")}function u(i,e,n){return S(i,e,t=>t.pathname===n)}function q(i,e,n){if(typeof i!="string")return!1;try{const t=new URL(i);return S(`${t.origin}${t.pathname}`,e,o=>o.pathname===`/spaces/${n}`)?t.searchParams.size===1&&/^[A-Za-z0-9_-]{32,256}$/.test(t.searchParams.get("invite")??"")&&!t.hash&&!t.username&&!t.password:!1}catch{return!1}}function S(i,e,n){if(typeof i!="string")return!1;try{const t=new URL(i),o=new URL(e),a=t.origin===o.origin,s=o.protocol==="http:"&&t.protocol==="http:"&&t.hostname===o.hostname,m=t.protocol==="https:"&&t.port===""&&t.hostname==="app.shennian.net";return(a||s||m)&&!t.username&&!t.password&&!t.search&&!t.hash&&n(t)}catch{return!1}}export{R as HttpRoomActionClient,c as RoomActionHttpError};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type RoomActivationMode, type RoomBindingStatus, type RoomRuntimeAdapter } from '@shennian/wire';
|
|
2
2
|
export type RoomSessionCapabilities = {
|
|
3
3
|
canIdentifySession: boolean;
|
|
4
4
|
canBindAutomatically: boolean;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&u.chmodSync(i,384),process.platform==="win32"&&u.existsSync(this.filePath)){const s=u.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw new o("unsafe_session_registry","Refusing to replace an unsafe Room session registry.");u.unlinkSync(this.filePath)}u.renameSync(i,this.filePath),process.platform!=="win32"&&u.chmodSync(this.filePath,384)}finally{try{u.unlinkSync(i)}catch{}}}}function
|
|
1
|
+
import w from"node:crypto";import u from"node:fs";import q from"node:path";import{isRoomRuntimeAdapter as C}from"@shennian/wire";import{resolveShennianPath as M}from"../config/index.js";const y=500,A=20,T=/^[A-Za-z0-9_.:@/-]{1,512}$/,I=new Set(["pending_approval","active","paused","detached"]),S=new Set(["auto_follow","mention_only","paused"]);class o extends Error{code;constructor(e,i){super(i),this.code=e,this.name="RoomSessionRegistryError"}}class j{filePath;state;mutexTails=new Map;constructor(e=M("runtime","room-session-registry.json"),i=new Date){this.filePath=e,this.state=this.load(),this.markPersistedActivationsInterrupted(i)&&this.persist()}register(e){c(e.machineId,"machine_id"),f(e.runtimeAdapter),_(e.sourceSessionKey),c(e.nianSessionId,"nian_session_id"),c(e.hostProcessIdentity,"host_process_identity"),P(e.capabilities);const i=v(e),s=(e.now??new Date).toISOString(),n=this.state.records[i];if(Object.values(this.state.records).find(h=>h.registryKey!==i&&h.nianSessionId===e.nianSessionId&&h.lifecycleState!=="closed"))throw new o("nian_session_already_registered","This Shennian session is already registered to another native Agent session.");const r=!!(n&&n.hostProcessIdentity!==e.hostProcessIdentity),l={schemaVersion:1,registryKey:i,machineId:e.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,nianSessionId:e.nianSessionId,panelContextKey:e.nianSessionId,hostProcessIdentity:e.hostProcessIdentity,registrationEpoch:n?n.registrationEpoch+(r?1:0):1,lifecycleState:"active",capabilities:{...e.capabilities},binding:n?.binding??null,registeredAt:n?.registeredAt??s,lastSeenAt:s,updatedAt:s};return this.state.records[i]=l,this.persist(),d(l)}getBySource(e){const i=this.state.records[v(e)];return i?d(i):null}getByNianSessionId(e){c(e,"nian_session_id");const i=Object.values(this.state.records).find(s=>s.nianSessionId===e&&s.lifecycleState!=="closed");return i?d(i):null}getByRegistryKey(e){if(!/^[a-f0-9]{64}$/.test(e))throw new o("invalid_registry_key","Invalid Room session registry key.");const i=this.state.records[e];return i?d(i):null}list(){return Object.values(this.state.records).sort((e,i)=>e.registeredAt.localeCompare(i.registeredAt)).map(d)}setBinding(e){const i=this.requireRecord(e.registryKey);for(const[a,r]of Object.entries({binding_id:e.bindingId,room_id:e.roomId,agent_identity_id:e.agentIdentityId}))c(r,a);if(!I.has(e.status))throw new o("invalid_binding_status","Invalid binding status.");if(e.status==="pending_approval"&&e.agentMembershipId!==null||(e.status==="active"||e.status==="paused")&&e.agentMembershipId===null)throw new o("invalid_agent_membership_id","Pending Room bindings cannot claim an Agent membership.");e.agentMembershipId!==null&&c(e.agentMembershipId,"agent_membership_id");const s=e.activationMode??"auto_follow";if(!S.has(s))throw new o("invalid_activation_mode","Invalid activation mode.");if(i.binding&&i.binding.status!=="detached"&&(i.binding.bindingId!==e.bindingId||i.binding.roomId!==e.roomId))throw new o("session_already_bound","An Agent session can have only one active Room binding.");const n=i.binding?.activationMode;return i.binding={bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:s,lastDeliveredSequence:i.binding?.lastDeliveredSequence??"0",lastActivatedSequence:i.binding?.lastActivatedSequence??"0",activationQueue:i.binding?.activationQueue??[],activeActivation:i.binding?.activeActivation??null,mentionContext:n==="mention_only"&&s==="mention_only"?i.binding?.mentionContext??[]:[],loopBreaker:i.binding?.loopBreaker??{windowStartedAt:null,activationCount:0,pausedUntil:null}},this.touchAndPersist(i,e.now),d(i)}detachBinding(e,i=new Date){const s=this.requireRecord(e);return s.binding&&(s.binding.status="detached",s.binding.activationMode="paused",s.binding.activationQueue=[],s.binding.mentionContext=[]),this.touchAndPersist(s,i),d(s)}advanceCursor(e){const i=this.requireBoundRecord(e.registryKey);p(e.sequence);const s=e.cursor==="delivered"?"lastDeliveredSequence":"lastActivatedSequence";if(BigInt(e.sequence)<BigInt(i.binding[s]))throw new o("cursor_regression","Room cursor cannot move backwards.");return i.binding[s]=e.sequence,this.touchAndPersist(i,e.now),d(i)}enqueueActivation(e,i,s=new Date,n=!1){const a=this.requireBoundRecord(e);m(i);const r=a.binding.activationQueue;if(!r.some(l=>l.causeMessageId===i.causeMessageId)){if(r.length>=y)throw new o("activation_queue_full","Agent activation queue is full.");r.push(d(i))}return n&&(a.binding.mentionContext=[]),this.touchAndPersist(a,s),d(a)}appendMentionContext(e,i,s=new Date){const n=this.requireBoundRecord(e);b(i);const a=n.binding.mentionContext??[];return a.some(r=>r.sequence===i.sequence)||(a.push(d(i)),n.binding.mentionContext=a.slice(-A)),this.touchAndPersist(n,s),d(n)}dequeueActivation(e,i,s=new Date){const n=this.requireBoundRecord(e);return n.binding.activationQueue=n.binding.activationQueue.filter(a=>a.causeMessageId!==i),this.touchAndPersist(n,s),d(n)}markActivationDispatching(e,i,s=new Date){const n=this.requireBoundRecord(e),a=n.binding.activationQueue.find(r=>r.causeMessageId===i);if(!a)throw new o("activation_not_found","Agent activation is unavailable.");return n.binding.activeActivation={...d(a),dispatchState:"dispatching",dispatchedAt:s.toISOString(),interruptedAt:void 0},n.binding.activationQueue=n.binding.activationQueue.filter(r=>r.causeMessageId!==i),this.touchAndPersist(n,s),d(n)}clearActiveActivation(e,i=new Date){const s=this.requireBoundRecord(e);return s.binding.activeActivation=null,this.touchAndPersist(s,i),d(s)}retryActiveActivation(e){const i=this.requireBoundRecord(e.registryKey),s=i.binding.activeActivation;if(!s)return{queued:!1,attempts:0};if(x(s))throw new o("activation_interrupted_requires_explicit_retry","Interrupted Agent activation requires an explicit retry.");return this.requeueActiveActivation(i,e)}requeueActiveActivation(e,i){const s=e.binding.activeActivation,n=i.now??new Date,{dispatchState:a,dispatchedAt:r,interruptedAt:l,...h}=s,g={...h,attempts:s.attempts+1};return e.binding.activeActivation=null,g.attempts<i.maxAttempts?(e.binding.activationQueue.some(R=>R.causeMessageId===g.causeMessageId)||e.binding.activationQueue.unshift(g),this.touchAndPersist(e,n),{queued:!0,attempts:g.attempts}):(e.binding.loopBreaker.pausedUntil=new Date(n.getTime()+i.pauseMs).toISOString(),this.touchAndPersist(e,n),{queued:!1,attempts:g.attempts})}markPersistedActivationsInterrupted(e){const i=e.toISOString();let s=!1;for(const n of Object.values(this.state.records)){const a=n.binding?.activeActivation;!a||x(a)||(a.dispatchState="interrupted",a.interruptedAt=i,n.updatedAt=i,s=!0)}return s}claimActivation(e){const i=this.requireBoundRecord(e.registryKey),s=e.now??new Date,n=i.binding.loopBreaker;return n.pausedUntil&&Date.parse(n.pausedUntil)>s.getTime()?{allowed:!1,pausedUntil:n.pausedUntil,newlyPaused:!1}:((!n.windowStartedAt||s.getTime()-Date.parse(n.windowStartedAt)>=e.windowMs)&&(n.windowStartedAt=s.toISOString(),n.activationCount=0,n.pausedUntil=null),n.activationCount>=e.maxActivations?(n.pausedUntil=new Date(s.getTime()+e.pauseMs).toISOString(),this.touchAndPersist(i,s),{allowed:!1,pausedUntil:n.pausedUntil,newlyPaused:!0}):(n.activationCount+=1,this.touchAndPersist(i,s),{allowed:!0,pausedUntil:null}))}updateLifecycle(e,i,s=new Date){const n=this.requireRecord(e);return n.lifecycleState=i,i==="closed"&&n.binding&&(n.binding.status="detached",n.binding.activationMode="paused",n.binding.activationQueue=[],n.binding.mentionContext=[]),this.touchAndPersist(n,s),d(n)}async runExclusive(e,i){this.requireRecord(e);const s=this.mutexTails.get(e)??Promise.resolve();let n;const a=new Promise(l=>{n=l}),r=s.then(()=>a);this.mutexTails.set(e,r),await s;try{return await i()}finally{n(),this.mutexTails.get(e)===r&&this.mutexTails.delete(e)}}requireRecord(e){const i=this.state.records[e];if(!i)throw new o("session_registration_not_found","Agent session is not registered.");return i}requireBoundRecord(e){const i=this.requireRecord(e);if(!i.binding||i.binding.status!=="active"&&i.binding.status!=="paused")throw new o("session_not_bound","Agent session has no active Room binding.");return i}touchAndPersist(e,i=new Date){e.updatedAt=i.toISOString(),this.persist()}load(){try{const e=u.lstatSync(this.filePath);if(!e.isFile()||e.isSymbolicLink())throw new o("unsafe_session_registry","Room session registry is not a regular file.");process.platform!=="win32"&&(e.mode&63)!==0&&u.chmodSync(this.filePath,384);const i=JSON.parse(u.readFileSync(this.filePath,"utf8"));return D(i)}catch(e){if(k(e))return{schemaVersion:1,records:{}};throw e instanceof o?e:new o("corrupt_session_registry","Room Agent session registry is corrupt.")}}persist(){const e=q.dirname(this.filePath);u.mkdirSync(e,{recursive:!0,mode:448}),process.platform!=="win32"&&u.chmodSync(e,448);const i=`${this.filePath}.tmp-${process.pid}-${w.randomBytes(6).toString("hex")}`;try{if(u.writeFileSync(i,`${JSON.stringify(this.state,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&u.chmodSync(i,384),process.platform==="win32"&&u.existsSync(this.filePath)){const s=u.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw new o("unsafe_session_registry","Refusing to replace an unsafe Room session registry.");u.unlinkSync(this.filePath)}u.renameSync(i,this.filePath),process.platform!=="win32"&&u.chmodSync(this.filePath,384)}finally{try{u.unlinkSync(i)}catch{}}}}function v(t){return c(t.machineId,"machine_id"),f(t.runtimeAdapter),_(t.sourceSessionKey),w.createHash("sha256").update(`${t.machineId}\0${t.runtimeAdapter}\0${t.sourceSessionKey}`,"utf8").digest("hex")}function D(t){if(!t||typeof t!="object"||t.schemaVersion!==1)throw new o("corrupt_session_registry","Unsupported Room session registry.");const e=t.records;if(!e||typeof e!="object"||Array.isArray(e))throw new o("corrupt_session_registry","Room session records are invalid.");const i={};for(const[s,n]of Object.entries(e)){if(B(n),s!==n.registryKey||s!==v(n))throw new o("corrupt_session_registry","Room session registry key mismatch.");i[s]={...n,binding:n.binding?{...n.binding,activeActivation:n.binding.activeActivation??null,mentionContext:n.binding.mentionContext??[]}:null}}return{schemaVersion:1,records:i}}function B(t){if(!t||t.schemaVersion!==1)throw new o("corrupt_session_registry","Invalid registration.");if(c(t.machineId,"machine_id"),f(t.runtimeAdapter),_(t.sourceSessionKey),c(t.nianSessionId,"nian_session_id"),c(t.hostProcessIdentity,"host_process_identity"),t.panelContextKey!==t.nianSessionId)throw new o("corrupt_session_registry","Invalid panel context.");if(!Number.isInteger(t.registrationEpoch)||t.registrationEpoch<1)throw new o("corrupt_session_registry","Invalid registration epoch.");P(t.capabilities),t.binding&&O(t.binding)}function O(t){if(c(t.bindingId,"binding_id"),c(t.roomId,"room_id"),c(t.agentIdentityId,"agent_identity_id"),!I.has(t.status))throw new o("corrupt_session_registry","Invalid binding status.");if(t.status==="pending_approval"&&t.agentMembershipId!==null||(t.status==="active"||t.status==="paused")&&t.agentMembershipId===null)throw new o("corrupt_session_registry","Invalid Agent membership.");if(t.agentMembershipId!==null&&c(t.agentMembershipId,"agent_membership_id"),!S.has(t.activationMode))throw new o("corrupt_session_registry","Invalid activation mode.");if(p(t.lastDeliveredSequence),p(t.lastActivatedSequence),!Array.isArray(t.activationQueue)||t.activationQueue.length>y)throw new o("corrupt_session_registry","Invalid activation queue.");if(t.activationQueue.forEach(m),t.activeActivation!=null&&m(t.activeActivation),t.mentionContext!==void 0&&(!Array.isArray(t.mentionContext)||t.mentionContext.length>A))throw new o("corrupt_session_registry","Invalid mention context.");t.mentionContext?.forEach(b)}function b(t){if(p(t.sequence),typeof t.deliveryText!="string"||!t.deliveryText.trim()||t.deliveryText.length>24e3)throw new o("invalid_mention_context","Invalid mention context.")}function m(t){if(c(t.causeMessageId,"cause_message_id"),c(t.rootCauseMessageId,"root_cause_message_id"),p(t.sequence),!Number.isInteger(t.hopCount)||t.hopCount<0||t.hopCount>32)throw new o("invalid_hop_count","Invalid Agent activation hop count.");if(!Number.isInteger(t.attempts)||t.attempts<0||t.attempts>100)throw new o("invalid_activation_attempts","Invalid Agent activation attempts.");if(!Number.isFinite(new Date(t.enqueuedAt).getTime()))throw new o("invalid_activation_time","Invalid Agent activation time.");if(t.deliveryText!==void 0&&(typeof t.deliveryText!="string"||!t.deliveryText.trim()||t.deliveryText.length>24e3))throw new o("invalid_activation_text","Invalid Agent activation text.");if(t.systemPrompt!==void 0&&(typeof t.systemPrompt!="string"||!t.systemPrompt.trim()||t.systemPrompt.length>24e3))throw new o("invalid_activation_prompt","Invalid Agent activation prompt.");if(t.promptSnapshot!==void 0&&!E(t.promptSnapshot))throw new o("invalid_activation_prompt_snapshot","Invalid Agent activation prompt snapshot.");if(t.dispatchState!==void 0&&t.dispatchState!=="dispatching"&&t.dispatchState!=="interrupted")throw new o("invalid_activation_state","Invalid Agent activation state.");if(t.dispatchedAt!==void 0&&!Number.isFinite(new Date(t.dispatchedAt).getTime()))throw new o("invalid_activation_dispatch_time","Invalid Agent activation dispatch time.");if(t.dispatchState===void 0&&t.dispatchedAt!==void 0)throw new o("invalid_activation_dispatch_time","Queued Agent activation cannot have a dispatch time.");if(t.dispatchState==="interrupted"&&(t.interruptedAt===void 0||!Number.isFinite(new Date(t.interruptedAt).getTime())))throw new o("invalid_activation_interrupted_time","Invalid Agent activation interruption time.");if(t.dispatchState!=="interrupted"&&t.interruptedAt!==void 0)throw new o("invalid_activation_interrupted_time","Unexpected Agent activation interruption time.")}function E(t){if(!t||typeof t!="object"||Array.isArray(t))return!1;const e=t,i=s=>s===null||typeof s=="string"&&/^[a-f0-9]{64}$/.test(s);return typeof e.managedRoomPolicyVersion=="string"&&e.managedRoomPolicyVersion.length<=64&&i(e.sessionModeInstructionsHash)&&i(e.agentSystemPromptHash)&&typeof e.trustedRoomContextHash=="string"&&/^[a-f0-9]{64}$/.test(e.trustedRoomContextHash)}function x(t){return t.dispatchState==="interrupted"}function P(t){if(!t||["canIdentifySession","canBindAutomatically","canOpenEmbeddedRoomPage","canResumeIdleSession","canActivateWhileHostOpen"].some(i=>typeof t[i]!="boolean"))throw new o("invalid_session_capabilities","Invalid Agent session capabilities.")}function f(t){if(!C(t))throw new o("invalid_runtime_adapter","Unsupported Room runtime adapter.")}function _(t){if(!t||t.length>1024||/[\0\r\n]/.test(t))throw new o("invalid_source_session_key","Invalid native Agent session key.")}function c(t,e){if(!T.test(t))throw new o(`invalid_${e}`,`Invalid ${e}.`)}function p(t){if(!/^(0|[1-9]\d{0,19})$/.test(t))throw new o("invalid_room_sequence","Invalid Room sequence.")}function d(t){return structuredClone(t)}function k(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}export{j as RoomSessionRegistry,o as RoomSessionRegistryError,v as buildRoomSessionRegistryKey,x as isInterruptedRoomActivation};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ie(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const r={runId:e.runId,sourceSeq:e.seq},o=se(e.args);return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:r,...e.name?{name:e.name}:{},...o!==void 0?{argsSummary:o}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const le=3e4;function ue(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function ce(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function L(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function H(e,t,a){e.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:t.sessionId,message:t,session:{id:t.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function ge(e,t){F({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function U(e){return ae(e)}function j(e,t,a){return{}}function B(e,t,a,r,o,l){e.configure?.({sessionId:t,externalChannel:r??null,env:{...U(a),...j(t,r,o),...l?{SHENNIAN_MANAGED_ROOM_CONTEXT:l}:{}}})}function fe(e,t,a){if(e!=="claude"||!a)return t;const r=t.trim();return!!r&&r.startsWith("-")&&!r.includes("/")?te(a)??t:t}function pe(e,t,a,r){let o=null;function l(n,s={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ie(n,t,s),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function f(n){if(!n.currentRunId||!n.currentRunPhase)return;const s=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:s,runPhase:n.currentRunPhase},seq:s,id:`agent-heartbeat-${n.currentRunId}-${s}-${Date.now()}`})}function y(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{f(n)},le),n.heartbeatTimer.unref?.())}function i(n){const s=n?.pendingTextEvent;!n||!s||!s.text||(l({state:"delta",runId:s.runId,seq:s.seq,text:s.text,thinking:s.thinking||void 0}),n.pendingTextEvent=null)}r.on("agentEvent",n=>{const s=e.sessions.get(t),T=ue(n),I=n.state==="final"||n.state==="error"||n.state==="aborted";if(s&&(s.nextEventSeq=n.seq+1,I||(s.currentRunId=n.runId),!I&&T&&(s.currentRunPhase=T,y(s)),n.agentSessionId&&(s.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&w({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?E(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?E(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?E(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:Y(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&E(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message});const h=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(h,(e.runTextAcc.get(h)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,L(a,n.source),n.agentSessionId),s&&F({sessionId:t,agentType:a,workDir:s.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const S=n.text??"";if(!S)return;s?.pendingTextEvent&&s.pendingTextEvent.runId!==n.runId&&i(s),s&&!s.pendingTextEvent&&(s.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),s?.pendingTextEvent&&(s.pendingTextEvent.text+=S,s.pendingTextEvent.seq=n.seq);return}let x={};if(n.state==="final"){i(s);const S=e.runTextAcc.get(h)??"";S&&(x={messageSummary:de(S)}),e.runTextAcc.delete(h)}else n.state==="error"||n.state==="aborted"?(i(s),e.runTextAcc.delete(h)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&i(s);I&&s?.currentRunId===n.runId&&(s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,g(s),e.chatQueue?.noteTerminal(t)),l(n,x)}),r.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function me(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function C(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function J(e,t,a,r,o,l,g){return g?e.send(t,a,r,o,l,g):l?.trim()?e.send(t,a,r,o,l):o?.length?e.send(t,a,r,o):r?e.send(t,a,r):e.send(t,a)}async function he(e,t,a,r,o,l,g,f){e.evictIdleSessions();const y=X(a);if(!y)throw new Error(`Unsupported agent: ${a}`);const i=r==="manager"&&a!=="manager"?new ee(y,a==="claude"?"claude":"codex"):y;B(i,t,a,g,f),await i.start(t,o,l);const n={adapter:i,workDir:o,agentType:a,sessionMode:r,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:g??null,externalReplyTarget:f??null,externalChannelEnv:{...U(a),...j(t,g,f)}};return e.sessions.set(t,n),pe(e,t,a,i),n}function ye(e,t){const a=e.sessions.get(t),r=a?.currentRunId;if(!a||!r)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${r}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:r,seq:o},seq:o,id:`agent-evt-${r}-${o}`}),e.chatQueue?.noteTerminal(t)}async function qe(e,t,a){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}me(e,t.id);const{sessionId:r,text:o,agentType:l,sessionMode:g,managerConfig:f,workDir:y,agentSessionId:i,modelId:n,systemPrompt:s,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:I,reasoningEffort:h,clientMessageId:x,sessionListProjection:S,waitForDispatch:Q,responseId:K,managedRoomContextToken:$,suppressExternalOwnerEcho:V}=t.params,k=K||t.id;ne(S);const b=null,P="";if(!r||!o){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:"sessionId and text are required"});return}const c=l,D=c==="manager"||g==="manager"?"manager":"standard";D==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,T??null,I??null);const q=(c==="claude"||c==="codex"||c==="workbuddy")&&typeof h=="string"&&h.trim()?h.trim():void 0,A=e.resolvePath(fe(c,y||v.homedir(),i)||v.homedir()),M=A,_=oe(t.params.attachments),p=_?.length?await re({text:o,attachments:_,workDir:A}):{text:o,attachments:_,localized:!1};let d=e.sessions.get(r);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==c||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==D||d.workDir!==A||JSON.stringify(d.externalChannel??null)!==JSON.stringify(b??null)){e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}else if(i&&d.agentSessionId!==i)try{await d.adapter.resume(i),d.agentSessionId=i}catch{e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}}if(!d)try{d=await he(e,r,c,D,A,i,b,P)}catch(u){const m=u instanceof Error&&u.message.startsWith("Unsupported agent:")?u.message:`Failed to start ${l}: ${u instanceof Error?u.message:String(u)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${r} agentType=${l} workDir=${A} agentSessionId=${i??""}: ${m}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:m,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:m});return}$&&/^mrc_[A-Za-z0-9_-]{43}$/.test($)&&B(d.adapter,r,c,b,P,$);const W={id:x??`user-${t.id}`,sessionId:r,role:"user",ts:Date.now(),payload:Z(p.text,p.attachments)};w({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:c,sessionMode:D,managerConfig:f??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null,reasoningEffort:q}});const z=u=>{ge(e,{sessionId:r,agentType:c,sessionMode:D,managerConfig:f??null,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null}),d.currentRunId||(d.nextEventSeq=0),e.nativeFusion?.registerManagedSend({sessionId:r,agentType:c,sourceAgentType:L(c,n),canonicalMessageId:x??null,sourceSessionKey:d.agentSessionId??i??null,text:p.text,managedEchoPolicy:u?.deliveryMode==="external_owner_queue"&&!V?"import_following":"suppress_following"}),E(r,W),H(e,W,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})},N=async(u,m)=>{const R=ce(c,u);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${r} agentType=${l} workDir=${A} agentSessionId=${d.agentSessionId??i??""}: ${R}`),e.sessions.delete(r);try{await C(d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:R,runId:"",seq:0}}),!m){const O={id:`agent-error-${t.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:R};E(r,O),H(e,O,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})}m&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:R}))};if(Q){let u;try{const m=p.attachments;u=await J(d.adapter,p.text,n,q,m,s,a),w({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(m){await N(m,!0);return}z(u??void 0),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized||u?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...u?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:u.deliveryMode,queuedSubmissionId:u.queuedSubmissionId}:{}}}:{}}),w({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}z(),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),w({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const G=p.attachments;J(d.adapter,p.text,n,q,G,s,a).then(()=>{w({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(u=>{N(u,!1)})}async function Me(e,t){const{sessionId:a}=t.params,r=e.sessions.get(a);if(r){try{await r.adapter.stop()}catch{}ye(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{Me as handleChatAbort,qe as handleChatSend,ge as sendSessionUpdateEvent};
|
|
1
|
+
import O from"node:os";import{createAgent as X}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Y,buildUserMessagePayload as Z}from"@shennian/wire";import{ManagerModeAdapter as ee}from"../../agents/manager.js";import{reportLog as E}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as te}from"../../native-fusion/parsers.js";import{appendMessage as x,recordSession as F}from"../store.js";import{mergeProjectedSessions as ne}from"../projection.js";import{buildManagedAgentEnv as ae}from"../../agents/config-status.js";import{materializeRemoteChatAttachments as re}from"../remote-attachments.js";import{buildToolArgsSummary as se}from"../tool-summary.js";function oe(e){if(!Array.isArray(e))return;const t=e.map(a=>{if(!a||typeof a!="object")return null;const r=a,o=typeof r.path=="string"?r.path:"",u=typeof r.name=="string"?r.name:"",g=typeof r.mimeType=="string"?r.mimeType:"";if(!o||!u||!g)return null;const f=typeof r.previewData=="string"&&r.previewData.trim()?r.previewData.trim():void 0;return{path:o,name:u,mimeType:g,kind:g.startsWith("image/")?"image":"file",...f?{previewData:f}:{}}}).filter(a=>a!=null);return t.length?t:void 0}function de(e){const t=e.indexOf(`
|
|
2
|
+
`),a=t>0?Math.min(t,80):Math.min(e.length,80);return e.slice(0,a)}function ie(e,t,a={}){if(e.state==="tool-call"||e.state==="tool-result"){const r={runId:e.runId,sourceSeq:e.seq},o=se(e.args);return{state:e.state,runId:e.runId,seq:e.seq,sessionId:t,detailRef:r,...e.name?{name:e.name}:{},...o!==void 0?{argsSummary:o}:{},...e.source?{source:e.source}:{},...e.agentSessionId?{agentSessionId:e.agentSessionId}:{},...a}}return{...e,sessionId:t,...a}}const le=3e4;function ue(e){return e.state==="heartbeat"?e.runPhase??null:e.state==="tool-call"||e.state==="tool-result"?"tool_running":e.state==="approval-pending"?"waiting_approval":e.state==="delta"?e.thinking?"thinking":"streaming_text":e.state==="init"||e.state==="start"?"thinking":null}function ce(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function L(e,t){return e!=="manager"?e:t==="claude"?"claude":"codex"}function H(e,t,a){e.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:t.sessionId,message:t,session:{id:t.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function ge(e,t){F({sessionId:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,workDir:t.workDir,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,managerDefaultWorkerAgentType:t.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:t.managerDefaultWorkerModelId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t.sessionId,agentType:t.agentType,sessionMode:t.sessionMode,managerConfig:t.managerConfig,agentSessionId:t.agentSessionId??null,modelId:t.modelId??null,workDir:t.workDir,status:"active",externalChannel:null}}})}function U(e){return ae(e)}function j(e,t,a){return{}}function B(e,t,a,r,o,u){e.configure?.({sessionId:t,externalChannel:r??null,env:{...U(a),...j(t,r,o),...u?{SHENNIAN_MANAGED_ROOM_CONTEXT:u}:{}}})}function fe(e,t,a){if(e!=="claude"||!a)return t;const r=t.trim();return!!r&&r.startsWith("-")&&!r.includes("/")?te(a)??t:t}function pe(e,t,a,r){let o=null;function u(n,s={}){e.client.sendAgentEvent({type:"event",event:"agent",payload:ie(n,t,s),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function f(n){if(!n.currentRunId||!n.currentRunPhase)return;const s=n.heartbeatSeq++;e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:t,runId:n.currentRunId,seq:s,runPhase:n.currentRunPhase},seq:s,id:`agent-heartbeat-${n.currentRunId}-${s}-${Date.now()}`})}function y(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{f(n)},le),n.heartbeatTimer.unref?.())}function i(n){const s=n?.pendingTextEvent;!n||!s||!s.text||(u({state:"delta",runId:s.runId,seq:s.seq,text:s.text,thinking:s.thinking||void 0}),n.pendingTextEvent=null)}r.on("agentEvent",n=>{const s=e.sessions.get(t),T=ue(n),I=n.state==="final"||n.state==="error"||n.state==="aborted";if(s&&(s.nextEventSeq=n.seq+1,I||(s.currentRunId=n.runId),!I&&T&&(s.currentRunPhase=T,y(s)),n.agentSessionId&&(s.agentSessionId=n.agentSessionId)),e.managerRuntime?.noteAgentEvent(t,n),n.state==="delta"&&n.thinking)return;n.state!=="delta"&&E({level:"info",sessionId:t,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?x(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?x(t,{id:`agent-${n.runId}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:n.state==="tool-call"?"tool_use":"tool_result",name:n.name,status:n.state==="tool-call"?"running":"completed",detailRef:{runId:n.runId,sourceSeq:n.seq},args:n.args,result:n.result})}):n.state==="approval-pending"?x(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:Y(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&x(t,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:t,role:"agent",ts:Date.now(),payload:n.message});const h=`${t}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&e.runTextAcc.set(h,(e.runTextAcc.get(h)??"")+n.text),n.agentSessionId&&n.agentSessionId!==o&&(o=n.agentSessionId,e.nativeFusion?.noteManagedSourceSession(t,L(a,n.source),n.agentSessionId),s&&F({sessionId:t,agentType:a,workDir:s.workDir,agentSessionId:n.agentSessionId}),e.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:t,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const S=n.text??"";if(!S)return;s?.pendingTextEvent&&s.pendingTextEvent.runId!==n.runId&&i(s),s&&!s.pendingTextEvent&&(s.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:!1}),s?.pendingTextEvent&&(s.pendingTextEvent.text+=S,s.pendingTextEvent.seq=n.seq);return}let w={};if(n.state==="final"){i(s);const S=e.runTextAcc.get(h)??"";S&&(w={messageSummary:de(S)}),e.runTextAcc.delete(h)}else n.state==="error"||n.state==="aborted"?(i(s),e.runTextAcc.delete(h)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&i(s);I&&s?.currentRunId===n.runId&&(s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,g(s),e.chatQueue?.noteTerminal(t)),u(n,w)}),r.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${t} agentType=${a}: ${n.message}`),e.sessions.delete(t),e.chatQueue?.noteTerminal(t),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:n.message,runId:"",seq:0}})})}function me(e,t){if(e.processedReqIds.add(t),e.processedReqIds.size>1e3){const a=e.processedReqIds.values().next().value;e.processedReqIds.delete(a)}}async function C(e){e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{})}function J(e,t,a,r,o,u,g){return g?e.send(t,a,r,o,u,g):u?.trim()?e.send(t,a,r,o,u):o?.length?e.send(t,a,r,o):r?e.send(t,a,r):e.send(t,a)}async function he(e,t,a,r,o,u,g,f){e.evictIdleSessions();const y=X(a);if(!y)throw new Error(`Unsupported agent: ${a}`);const i=r==="manager"&&a!=="manager"?new ee(y,a==="claude"?"claude":"codex"):y;B(i,t,a,g,f),await i.start(t,o,u);const n={adapter:i,workDir:o,agentType:a,sessionMode:r,agentSessionId:u??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:g??null,externalReplyTarget:f??null,externalChannelEnv:{...U(a),...j(t,g,f)}};return e.sessions.set(t,n),pe(e,t,a,i),n}function ye(e,t){const a=e.sessions.get(t),r=a?.currentRunId;if(!a||!r)return;const o=a.nextEventSeq;e.runTextAcc.delete(`${t}:${r}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:r,seq:o},seq:o,id:`agent-evt-${r}-${o}`}),e.chatQueue?.noteTerminal(t)}async function qe(e,t,a){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}me(e,t.id);const{sessionId:r,text:o,agentType:u,sessionMode:g,managerConfig:f,workDir:y,agentSessionId:i,modelId:n,systemPrompt:s,managerDefaultWorkerAgentType:T,managerDefaultWorkerModelId:I,reasoningEffort:h,clientMessageId:w,sessionListProjection:S,waitForDispatch:Q,responseId:K,managedRoomContextToken:$,suppressExternalOwnerEcho:V}=t.params,k=K||t.id;ne(S);const b=null,P="";if(!r||!o){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:"sessionId and text are required"});return}const c=u,D=c==="manager"||g==="manager"?"manager":"standard";D==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(r,T??null,I??null);const q=(c==="claude"||c==="codex"||c==="workbuddy")&&typeof h=="string"&&h.trim()?h.trim():void 0,A=e.resolvePath(fe(c,y||O.homedir(),i)||O.homedir()),M=A,_=oe(t.params.attachments),p=_?.length?await re({text:o,attachments:_,workDir:A}):{text:o,attachments:_,localized:!1};let d=e.sessions.get(r);if(d){if(d.lastActiveAt=Date.now(),d.agentType!==c||(d.sessionMode??(d.agentType==="manager"?"manager":"standard"))!==D||d.workDir!==A||JSON.stringify(d.externalChannel??null)!==JSON.stringify(b??null)){e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}else if(i&&d.agentSessionId!==i)try{await d.adapter.resume(i),d.agentSessionId=i}catch{e.sessions.delete(r);try{await C(d)}catch{e.processedReqIds.delete(t.id)}d=void 0}}if(!d)try{d=await he(e,r,c,D,A,i,b,P)}catch(l){const m=l instanceof Error&&l.message.startsWith("Unsupported agent:")?l.message:`Failed to start ${u}: ${l instanceof Error?l.message:String(l)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${r} agentType=${u} workDir=${A} agentSessionId=${i??""}: ${m}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:m,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:m});return}$&&/^mrc_[A-Za-z0-9_-]{43}$/.test($)&&B(d.adapter,r,c,b,P,$);const W={id:w??`user-${t.id}`,sessionId:r,role:"user",ts:Date.now(),payload:Z(p.text,p.attachments)};E({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:c,sessionMode:D,managerConfig:f??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null,reasoningEffort:q}});const z=l=>{ge(e,{sessionId:r,agentType:c,sessionMode:D,managerConfig:f??null,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n,managerDefaultWorkerAgentType:T??null,managerDefaultWorkerModelId:I??null}),d.currentRunId||(d.nextEventSeq=0),e.nativeFusion?.registerManagedSend({sessionId:r,agentType:c,sourceAgentType:L(c,n),canonicalMessageId:w??null,sourceSessionKey:d.agentSessionId??i??null,text:p.text,managedEchoPolicy:l?.deliveryMode==="external_owner_queue"&&!V?"import_following":"suppress_following",externalRunId:l?.deliveryMode==="external_owner_queue"?`codex-external-${l.queuedSubmissionId??w??t.id}`:null}),x(r,W),H(e,W,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})},N=async(l,m)=>{const R=ce(c,l);console.error(`[chat.send] send failed reqId=${t.id} sessionId=${r} agentType=${u} workDir=${A} agentSessionId=${d.agentSessionId??i??""}: ${R}`),e.sessions.delete(r);try{await C(d)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:R,runId:"",seq:0}}),!m){const v={id:`agent-error-${t.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:R};x(r,v),H(e,v,{agentType:c,workDir:M,agentSessionId:d.agentSessionId??i??null,modelId:n})}m&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:k,ok:!1,error:R}))};if(Q){let l;try{const m=p.attachments;l=await J(d.adapter,p.text,n,q,m,s,a),E({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(m){await N(m,!0);return}z(l??void 0),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized||l?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...l?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:l.deliveryMode,queuedSubmissionId:l.queuedSubmissionId}:{}}}:{}}),E({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}z(),e.client.sendRes({type:"res",id:k,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),E({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const G=p.attachments;J(d.adapter,p.text,n,q,G,s,a).then(()=>{E({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(l=>{N(l,!1)})}async function Me(e,t){const{sessionId:a}=t.params,r=e.sessions.get(a);if(r){try{await r.adapter.stop()}catch{}ye(e,a),e.activityPublisher?.publish(a,null)}e.client.sendRes({type:"res",id:t.id,ok:!0})}export{Me as handleChatAbort,qe as handleChatSend,ge as sendSessionUpdateEvent};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentType } from './session.ts';
|
|
1
|
+
import type { AgentType, ManagerConfig, SessionMode } from './session.ts';
|
|
2
2
|
import type { RoomActivationMode } from './room.ts';
|
|
3
3
|
export type AgentWorkspaceRoomLink = {
|
|
4
4
|
bindingId: string;
|
|
@@ -37,6 +37,8 @@ export type AgentWorkspace = {
|
|
|
37
37
|
runtimeAdapter: AgentType | null;
|
|
38
38
|
workDir: string;
|
|
39
39
|
modelId: string | null;
|
|
40
|
+
sessionMode: SessionMode;
|
|
41
|
+
managerConfig: ManagerConfig | null;
|
|
40
42
|
createdAt: string;
|
|
41
43
|
updatedAt: string;
|
|
42
44
|
sessions: AgentWorkspaceSession[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @arch docs/features/
|
|
1
|
+
// @arch docs/features/free-growth-phase.md
|
|
2
2
|
// @test packages/wire/src/__tests__/external-message-format.test.ts
|
|
3
3
|
function cleanText(value) {
|
|
4
4
|
return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|