shennian 0.3.13 → 0.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +10 -10
- package/dist/src/agents/adapter.d.ts +4 -1
- package/dist/src/agents/workbuddy.d.ts +2 -2
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/integrations/room-session-hook.js +2 -2
- package/dist/src/integrations/workbuddy-plugin-bundle.d.ts +1 -0
- package/dist/src/integrations/workbuddy-plugin-bundle.js +1 -1
- package/dist/src/session/handlers/chat.d.ts +2 -1
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/manager.js +1 -1
- package/package.json +1 -1
|
@@ -136,8 +136,8 @@
|
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
"file": "src/agents/workbuddy.js",
|
|
139
|
-
"beforeBytes":
|
|
140
|
-
"afterBytes":
|
|
139
|
+
"beforeBytes": 11645,
|
|
140
|
+
"afterBytes": 6365
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"file": "src/commands/agent.js",
|
|
@@ -296,13 +296,13 @@
|
|
|
296
296
|
},
|
|
297
297
|
{
|
|
298
298
|
"file": "src/integrations/room-session-hook.js",
|
|
299
|
-
"beforeBytes":
|
|
300
|
-
"afterBytes":
|
|
299
|
+
"beforeBytes": 12586,
|
|
300
|
+
"afterBytes": 6308
|
|
301
301
|
},
|
|
302
302
|
{
|
|
303
303
|
"file": "src/integrations/workbuddy-plugin-bundle.js",
|
|
304
|
-
"beforeBytes":
|
|
305
|
-
"afterBytes":
|
|
304
|
+
"beforeBytes": 2256,
|
|
305
|
+
"afterBytes": 1258
|
|
306
306
|
},
|
|
307
307
|
{
|
|
308
308
|
"file": "src/integrations/workbuddy-plugin-lifecycle.js",
|
|
@@ -566,8 +566,8 @@
|
|
|
566
566
|
},
|
|
567
567
|
{
|
|
568
568
|
"file": "src/session/handlers/chat.js",
|
|
569
|
-
"beforeBytes":
|
|
570
|
-
"afterBytes":
|
|
569
|
+
"beforeBytes": 31227,
|
|
570
|
+
"afterBytes": 13586
|
|
571
571
|
},
|
|
572
572
|
{
|
|
573
573
|
"file": "src/session/handlers/control.js",
|
|
@@ -601,8 +601,8 @@
|
|
|
601
601
|
},
|
|
602
602
|
{
|
|
603
603
|
"file": "src/session/manager.js",
|
|
604
|
-
"beforeBytes":
|
|
605
|
-
"afterBytes":
|
|
604
|
+
"beforeBytes": 20531,
|
|
605
|
+
"afterBytes": 10697
|
|
606
606
|
},
|
|
607
607
|
{
|
|
608
608
|
"file": "src/session/projection.js",
|
|
@@ -29,6 +29,9 @@ export type AgentSendResult = {
|
|
|
29
29
|
deliveryMode?: 'direct' | 'external_owner_queue';
|
|
30
30
|
queuedSubmissionId?: string;
|
|
31
31
|
};
|
|
32
|
+
export type AgentSendContext = {
|
|
33
|
+
source: 'room_activation';
|
|
34
|
+
};
|
|
32
35
|
export interface AgentAdapterEvents {
|
|
33
36
|
agentEvent: [event: AgentEvent];
|
|
34
37
|
error: [error: Error];
|
|
@@ -48,7 +51,7 @@ export declare abstract class AgentAdapter extends EventEmitter<AgentAdapterEven
|
|
|
48
51
|
}>;
|
|
49
52
|
setTitle?(agentSessionId: string, title: string, workDir?: string): Promise<void>;
|
|
50
53
|
abstract start(sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
51
|
-
abstract send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], systemPrompt?: string | null): Promise<void | AgentSendResult>;
|
|
54
|
+
abstract send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], systemPrompt?: string | null, context?: AgentSendContext): Promise<void | AgentSendResult>;
|
|
52
55
|
abstract resume(agentSessionId: string): Promise<void>;
|
|
53
56
|
abstract stop(): Promise<void>;
|
|
54
57
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChatAttachmentMeta, ExternalChannelSessionStatus, SessionRunPhase } from '@shennian/wire';
|
|
2
|
-
import { AgentAdapter } from './adapter.js';
|
|
2
|
+
import { AgentAdapter, type AgentSendContext } from './adapter.js';
|
|
3
3
|
export declare function normalizeWorkBuddyModelId(modelId?: string | null): string;
|
|
4
4
|
export declare function normalizeWorkBuddyReasoningEffort(value?: string | null): string | undefined;
|
|
5
5
|
export declare class WorkBuddyAdapter extends AgentAdapter {
|
|
@@ -21,7 +21,7 @@ export declare class WorkBuddyAdapter extends AgentAdapter {
|
|
|
21
21
|
env?: NodeJS.ProcessEnv;
|
|
22
22
|
}): void;
|
|
23
23
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
24
|
-
send(text: string, modelId?: string, reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null): Promise<void>;
|
|
24
|
+
send(text: string, modelId?: string, reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
|
|
25
25
|
resume(agentSessionId: string): Promise<void>;
|
|
26
26
|
stop(): Promise<void>;
|
|
27
27
|
getStatus(): Promise<{
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createInterface as
|
|
1
|
+
import{createInterface as c}from"node:readline";import{randomUUID as g}from"node:crypto";import{AgentAdapter as y,registerAgent as I}from"./adapter.js";import{resolveBuiltinCommand as k,spawnAgentCommand as S}from"./command-spec.js";import{buildAgentProcessEnv as _}from"../agent-env.js";import{buildPlatformInstructions as x}from"./platform-instructions.js";import{ensureWorkBuddyRoomPluginBundle as E}from"../integrations/workbuddy-plugin-bundle.js";import{roomMcpHostToolName as w}from"../integrations/room-plugin-shared.js";const P=new Set(["minimal","low","medium","high","xhigh","max"]);function B(i){return i?.trim()||"auto"}function T(i){const t=i?.trim();if(t){if(P.has(t))return t;throw new Error(`Unsupported WorkBuddy reasoning effort "${t}". Supported values: minimal, low, medium, high, xhigh, max.`)}}class W extends y{type="workbuddy";process=null;agentSessionId=null;workDir=null;seq=0;runId="";terminalState="open";hasEmittedText=!1;runPhase=null;externalChannel=null;shennianSessionId=null;extraEnv={};configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{}}async start(t,s,e){this.workDir=s,this.seq=0,e&&(this.agentSessionId=e)}async send(t,s,e,r,a,n){await this.killProcess(),this.runId=g(),this.resetRunState();const u=n?.source==="room_activation",o=["-p",t,"--output-format","stream-json","--verbose","--permission-mode",u?"dontAsk":"auto","--setting-sources","user,project,local","--model",B(s)],h=E();if(u){const f=w("workbuddy");o.push("--allowedTools",f,"--tools","","--mcp-config",h.mcpFile,"--strict-mcp-config","--disallowedTools","mcp__shennian_room__shennian_agent","--max-turns","2")}o.push("--plugin-dir",h.pluginRoot);const d=x(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a);d&&o.push("--append-system-prompt",d);const m=T(e);m&&o.push("--effort",m),this.agentSessionId&&o.push("--resume",this.agentSessionId),this.spawnAndParse(o)}async resume(t){await this.killProcess(),this.agentSessionId=t,this.resetRunState()}async stop(){await this.killProcess()}async getStatus(){return{active:this.process!=null&&this.terminalState==="open",runId:this.runId||null,runPhase:this.runPhase,canStop:this.process!=null&&this.terminalState==="open"}}spawnAndParse(t){const s=k("workbuddy");if(!s){this.emit("error",new Error("WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry."));return}const e=S(s,t,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:_(this.extraEnv)});this.process=e,c({input:e.stdout}).on("line",n=>{if(n.trim())try{this.handleStreamEvent(JSON.parse(n))}catch{}});let a="";e.stderr?.on("data",n=>{a=`${a}${n.toString()}`.slice(-8e3)}),e.on("close",n=>{this.process===e&&(this.process=null,this.runPhase=null,this.handleProcessClose(n,a))}),e.on("error",n=>{if(this.process!==e)return;this.process=null,this.runPhase=null;const u=n.code==="ENOENT";this.emitErrorIfOpen({state:"error",message:u?"WorkBuddy was not found. Install WorkBuddy Desktop or the CodeBuddy CLI, then retry.":l(n.message)})})}handleStreamEvent(t){if(t.type==="system"&&t.subtype==="init"&&t.session_id){this.agentSessionId=t.session_id,this.runPhase="thinking",this.emitEvent({state:"start",agentSessionId:t.session_id});return}if(t.type==="control_request"&&t.request?.subtype==="can_use_tool"){this.runPhase="waiting_approval",this.emitEvent({state:"approval-pending",name:t.request.tool_name,args:t.request.input,approval:{title:`WorkBuddy \u8BF7\u6C42\u4F7F\u7528 ${t.request.tool_name||"\u5DE5\u5177"}`,source:"workbuddy",actionHint:"\u8BF7\u5728 WorkBuddy \u6216\u795E\u5FF5\u4E2D\u786E\u8BA4\u540E\u7EE7\u7EED\u3002"}});return}if(t.type==="assistant"){const e=t.message?.content;if(e?.length)for(const r of e)this.handleContentBlock(r);else t.text&&this.emitText(t.text,!!t.thinking);return}if(t.type!=="result")return;if(t.subtype==="tool_result"){this.runPhase="tool_running",this.emitEvent({state:"tool-result",name:t.name,result:p(t.content)});return}if(t.subtype==="error"||t.error){this.emitErrorIfOpen({state:"error",message:l(t.error||t.result||"WorkBuddy failed.")});return}!this.hasEmittedText&&t.result&&this.emitText(t.result,!1),t.session_id&&(this.agentSessionId=t.session_id);const s=t.usage??t.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:s?{inputTokens:s.input_tokens??0,outputTokens:s.output_tokens??0}:void 0})}handleContentBlock(t){t.type==="text"&&t.text?this.emitText(t.text,!1):t.type==="thinking"&&t.thinking?this.emitText(t.thinking,!0):t.type==="tool_use"?(this.runPhase="tool_running",this.emitEvent({state:"tool-call",name:t.name,args:t.input})):t.type==="tool_result"&&this.emitEvent({state:"tool-result",name:t.name,result:p(t.content)})}emitText(t,s){this.runPhase=s?"thinking":"streaming_text";const e=!s&&this.hasEmittedText?`
|
|
2
2
|
|
|
3
|
-
`:"";this.emitEvent({state:"delta",text:e+t,thinking:s||void 0}),s||(this.hasEmittedText=!0)}handleProcessClose(t,s){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:
|
|
3
|
+
`:"";this.emitEvent({state:"delta",text:e+t,thinking:s||void 0}),s||(this.hasEmittedText=!0)}handleProcessClose(t,s){if(t!==0&&t!==null){this.emitErrorIfOpen({state:"error",message:l(s)||`WorkBuddy exited with code ${t}.`});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(t){this.terminalState==="open"&&(this.terminalState="final",this.runPhase=null,this.emitEvent(t))}emitErrorIfOpen(t){this.terminalState==="open"&&(this.terminalState="error",this.runPhase=null,this.emitEvent(t))}emitEvent(t){this.emit("agentEvent",{...t,runId:this.runId,seq:this.seq++})}resetRunState(){this.seq=0,this.terminalState="open",this.hasEmittedText=!1,this.runPhase=null}async killProcess(){const t=this.process;t&&(this.process=null,this.runPhase=null,t.kill("SIGTERM"),await new Promise(s=>{let e=!1;const r=()=>{e||(e=!0,s())};t.once("close",r),setTimeout(()=>{t.kill("SIGKILL"),r()},3e3).unref?.()}))}}function p(i){if(typeof i=="string")return i;try{return JSON.stringify(i)}catch{return"[unserializable WorkBuddy tool result]"}}function l(i){return i.replace(/(Authorization\s*:\s*)Bearer\s+[A-Za-z0-9._~+\/-]+/gi,"$1Bearer [redacted]").replace(/(api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|connector[-_ ]?token)(\s*[:=]\s*)[^\s,;]+/gi,"$1$2[redacted]").trim().slice(0,2e3)}I("workbuddy",()=>new W);export{W as WorkBuddyAdapter,B as normalizeWorkBuddyModelId,T as normalizeWorkBuddyReasoningEffort,l as sanitizeWorkBuddyError};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}function h(t){return
|
|
1
|
+
import E from"node:crypto";import{callDaemonIpc as T}from"../daemon-ipc/client.js";import{DaemonIpcError as f}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as z}from"../room/room-action-schema.js";import{SHENNIAN_ROOM_TOOL_NAME as N}from"../room/tool-schema.js";import{roomMcpHostToolName as O}from"./room-plugin-shared.js";const H=256*1024,_={SessionStart:"active",UserPromptSubmit:"active",PreCompact:"active",PostCompact:"active",PreToolUse:"active",Stop:"idle",SessionEnd:"idle"};async function Z(t,o=process.stdin,n=process.stdout,r=T){let s="";for await(const a of o)if(s+=Buffer.isBuffer(a)?a.toString("utf8"):String(a),Buffer.byteLength(s,"utf8")>H)return"ignored";const e=j(t,s);if(!e)return"ignored";let d=!1;try{await r({method:"room-host-session.observe",params:{runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,hostContextHash:e.hostContextHash,workspaceHash:e.workspaceHash,workspacePath:e.workspacePath,lifecycleState:e.lifecycleState,eventName:e.eventName},timeoutMs:2e3})}catch(a){if(a instanceof f&&a.code==="machine_not_authorized")d=!0;else return"unavailable"}if(e.eventName!=="PreToolUse")return"observed";if(e.toolName!==O(e.runtimeAdapter)||!e.toolInput)return"ignored";let i=null,c="";try{if(i=K(e.toolInput),c=x(e,i),i.action==="room.create"||i.action==="room.join")try{const u=await r({method:"authorized-action.status",params:{idempotencyKey:c},timeoutMs:2e3}),b=A(u);return S(n,e.runtimeAdapter,e.toolInput,b),"authorized"}catch(u){if(!(u instanceof f)||u.code!=="pending_action_not_found")throw u}if(d)return await w(e,i,c,n,r);const a=await r({method:"room-session.resolve",params:{runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey},timeoutMs:2e3}),l=v(a),P=await r({method:"room-invocation.issue",params:{registryKey:l,action:i.action,targetTool:N,actionParameters:i.actionParameters},timeoutMs:2e3}),k=R(P),I={...e.toolInput,_shennianRegistryKey:l,_shennianInvocationToken:k};return y(n,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Authorized by the local Shennian daemon for this Agent conversation.",...g(e.runtimeAdapter,I)}}),"authorized"}catch(a){if(a instanceof f&&a.code==="machine_not_authorized"&&i&&(i.action==="room.create"||i.action==="room.join"))try{return await w(e,i,c,n,r)}catch{}return y(n,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"This Agent conversation is not connected to Shennian yet. Open it from Shennian or use the repair connection action."}}),"denied"}}async function w(t,o,n,r,s){if(o.action!=="room.create"&&o.action!=="room.join")throw new Error("authorization_requires_onboarding_action");const e=await s({method:"authorized-action.create",params:{actionType:o.action,actionSummary:C(o.action,o.actionParameters),actionPayload:U(t,o),idempotencyKey:n,returnTo:"/spaces"},timeoutMs:12e3});return S(r,t.runtimeAdapter,t.toolInput,A(e)),"authorized"}function j(t,o){if(t!=="codex"&&t!=="claude_code"&&t!=="workbuddy")return null;let n;try{n=JSON.parse(o)}catch{return null}if(!n||typeof n!="object"||Array.isArray(n))return null;const r=n,s=m(r.session_id,1024),e=M(r.hook_event_name);if(!s||!e)return null;const d=m(r.transcript_path,4096)??"",i=m(r.cwd,4096)??"",c=e==="PreToolUse"?m(r.tool_name,256):null,a=e==="PreToolUse"?D(r.tool_input):null,l=e==="PreToolUse"?m(r.tool_use_id??r.tool_call_id,512):null;return{runtimeAdapter:t,sourceSessionKey:s,hostContextHash:h(`${s}\0${d}`),workspaceHash:i?h(i):null,workspacePath:i||null,lifecycleState:_[e],eventName:e,toolName:c,toolInput:a,toolRequestId:l}}function K(t){const o=$(t.action);if(Object.keys(t).some(r=>r.startsWith("_shennian")))throw new Error("hidden_input");const n=Object.fromEntries(Object.entries(t).filter(([r])=>r!=="action"));return{action:o,actionParameters:z(o,n)}}function $(t){const o={create:"room.create",join:"room.join",read:"room.read",send:"room.send",mode:"room.mode",status:"room.status"};if(typeof t!="string"||!(t in o))throw new Error("invalid_action");return o[t]}function v(t){const o=p(t,"registryKey");if(!/^[a-f0-9]{64}$/.test(o))throw new Error("invalid_registry");return o}function R(t){const o=p(t,"invocationToken");if(!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(o))throw new Error("invalid_token");return o}function A(t){const o=p(t,"id");if(!/^[A-Za-z0-9_-]{8,128}$/.test(o))throw new Error("invalid_pending_action");return o}function x(t,o){const n=t.toolRequestId??h(JSON.stringify(o.actionParameters));return`${o.action.replace(".","-")}-auth:${h(`${t.runtimeAdapter}\0${t.sourceSessionKey}\0${n}`)}`}function U(t,o){return{schemaVersion:1,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:t.hostContextHash,workspaceHash:t.workspaceHash,workspacePath:t.workspacePath,lifecycleState:t.lifecycleState,action:o.action,actionParameters:o.actionParameters}}function C(t,o){if(!o||Array.isArray(o)||typeof o!="object")throw new Error("invalid_authorized_action");const n=typeof o.agentAlias=="string"?o.agentAlias:"";return t==="room.create"?`\u521B\u5EFA\u7FA4\u804A\u300C${typeof o.name=="string"?o.name:""}\u300D\uFF0C\u5E76\u4EE5 Agent\u300C${n}\u300D\u52A0\u5165`:`\u4F7F\u7528\u5F53\u524D\u9080\u8BF7\u52A0\u5165\u7FA4\u804A\uFF0C\u5E76\u4EE5 Agent\u300C${n}\u300D\u53C2\u4E0E`}function S(t,o,n,r){const s={...n,_shennianAuthorizedActionId:r};y(t,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Shennian will open a secure browser authorization and continue this action automatically.",...g(o,s)}})}function g(t,o){return t==="workbuddy"?{updatedInput:o,modifiedInput:o}:{updatedInput:o}}function p(t,o){if(!t||Array.isArray(t)||typeof t!="object")throw new Error("invalid_response");const n=t[o];if(typeof n!="string")throw new Error("invalid_response");return n}function M(t){return typeof t=="string"&&t in _?t:null}function m(t,o){if(typeof t!="string")return null;const n=t.trim();return!n||n.length>o||/[\0\r\n]/.test(n)?null:n}function D(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function y(t,o){t.write(`${JSON.stringify(o)}
|
|
2
|
+
`)}function h(t){return E.createHash("sha256").update(t,"utf8").digest("hex")}export{j as parseRoomSessionHookInput,Z as runRoomSessionHook};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"node:path";import{resolveShennianPath as d}from"../config/index.js";import{ensurePrivateDirectory as r,ensureSharedRoomPluginFiles as l,normalizePluginVersion as c,writeJsonAtomic as
|
|
1
|
+
import o from"node:path";import{resolveShennianPath as d}from"../config/index.js";import{ensurePrivateDirectory as r,ensureSharedRoomPluginFiles as l,normalizePluginVersion as c,writeJsonAtomic as u}from"./room-plugin-shared.js";const p="shennian",s="shennian-room";function k(t={}){const n=t.marketplaceRoot??d("integrations","workbuddy","marketplace"),e=o.join(n,"plugins",s);r(n),r(o.join(n,".codebuddy-plugin")),r(o.join(n,"plugins"));const a=l("workbuddy",e,t),i=c(a.pluginVersion);return r(o.join(e,".codebuddy-plugin")),u(o.join(n,".codebuddy-plugin","marketplace.json"),{name:p,owner:{name:"Shennian"},description:"Official Shennian integrations for WorkBuddy.",version:i,plugins:[{name:s,source:`./plugins/${s}`,description:"Securely links the current WorkBuddy Session to Shennian Rooms.",version:i}]}),u(o.join(e,".codebuddy-plugin","plugin.json"),{name:s,description:"Official Shennian Room Session integration for WorkBuddy.",version:i,author:{name:"Shennian"},license:"MIT",hooks:"./hooks/hooks.json"}),{marketplaceRoot:n,marketplaceManifest:o.join(n,".codebuddy-plugin","marketplace.json"),pluginRoot:e,pluginVersion:i,mcpFile:a.mcpFile}}export{p as WORKBUDDY_MARKETPLACE_NAME,s as WORKBUDDY_PLUGIN_NAME,k as ensureWorkBuddyRoomPluginBundle};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentSendContext } from '../../agents/adapter.js';
|
|
1
2
|
import { type ReqFrame, type AgentType } from '@shennian/wire';
|
|
2
3
|
import type { SessionManagerRuntime } from '../types.js';
|
|
3
4
|
export declare function sendSessionUpdateEvent(runtime: SessionManagerRuntime, input: {
|
|
@@ -7,5 +8,5 @@ export declare function sendSessionUpdateEvent(runtime: SessionManagerRuntime, i
|
|
|
7
8
|
agentSessionId?: string | null;
|
|
8
9
|
modelId?: string;
|
|
9
10
|
}): void;
|
|
10
|
-
export declare function handleChatSend(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
11
|
+
export declare function handleChatSend(runtime: SessionManagerRuntime, req: ReqFrame, context?: AgentSendContext): Promise<void>;
|
|
11
12
|
export declare function handleChatAbort(runtime: SessionManagerRuntime, req: ReqFrame): Promise<void>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import z from"node:os";import{createAgent as
|
|
2
|
-
`),a=e>0?Math.min(e,80):Math.min(t.length,80);return t.slice(0,a)}function ne(t,e,a={}){if(t.state==="tool-call"||t.state==="tool-result"){const s={runId:t.runId,sourceSeq:t.seq};return{state:t.state,runId:t.runId,seq:t.seq,sessionId:e,detailRef:s,...t.name?{name:t.name}:{},...t.source?{source:t.source}:{},...t.agentSessionId?{agentSessionId:t.agentSessionId}:{},...a}}return{...t,sessionId:e,...a}}const ae=3e4;function re(t){return t.state==="heartbeat"?t.runPhase??null:t.state==="tool-call"||t.state==="tool-result"?"tool_running":t.state==="approval-pending"?"waiting_approval":t.state==="delta"?t.thinking?"thinking":"streaming_text":t.state==="init"||t.state==="start"?"thinking":null}function se(t,e){return`Agent send failed: ${e instanceof Error?e.message:String(e)}`}function L(t,e){return t!=="manager"?t:e==="claude"?"claude":"codex"}function O(t,e,a){t.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:e.sessionId,message:e,session:{id:e.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function oe(t,e){B({sessionId:e.sessionId,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e.sessionId,agentType:e.agentType,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,workDir:e.workDir,status:"active",externalChannel:null}}})}function Ae(t){if(!t||typeof t!="object")return null;const e=t;return{configured:e.configured===void 0?void 0:!!e.configured,connected:!!e.connected,type:typeof e.type=="string"?e.type:null,channelId:typeof e.channelId=="string"?e.channelId:null,name:typeof e.name=="string"?e.name:null,canReply:e.canReply===void 0||e.canReply===null?null:!!e.canReply,systemPrompt:typeof e.systemPrompt=="string"?e.systemPrompt:null,wechatRpaSource:typeof e.wechatRpaSource=="string"?e.wechatRpaSource:null,wechatRpaGroups:Array.isArray(e.wechatRpaGroups)?e.wechatRpaGroups.map(a=>({name:String(a?.name||"").trim()})).filter(a=>a.name):null,pollIntervalMs:Number.isFinite(e.pollIntervalMs)?Number(e.pollIntervalMs):null,recentLimit:Number.isFinite(e.recentLimit)?Number(e.recentLimit):null,idleSeconds:Number.isFinite(e.idleSeconds)?Number(e.idleSeconds):null,forceForeground:e.forceForeground===void 0||e.forceForeground===null?null:!!e.forceForeground,noRestore:e.noRestore===void 0||e.noRestore===null?null:!!e.noRestore,downloadAttachments:e.downloadAttachments===void 0||e.downloadAttachments===null?null:!!e.downloadAttachments,downloadAttachmentsDir:typeof e.downloadAttachmentsDir=="string"?e.downloadAttachmentsDir:null}}function Re(t){return!!(t?.configured??t?.connected)}function W(t){return X(t)}function H(t,e,a){return{}}function j(t,e,a,s,l,u){t.configure?.({sessionId:e,externalChannel:s??null,env:{...W(a),...H(e,s,l),...u?{SHENNIAN_ROOM_REGISTRY_KEY:u}:{}}})}function Te(t,e,a){return null}function le(t,e,a){if(t!=="claude"||!a)return e;const s=e.trim();return!!s&&s.startsWith("-")&&!s.includes("/")?Y(a)??e:e}function de(t,e,a,s){let l=null;function u(n,r={}){t.client.sendAgentEvent({type:"event",event:"agent",payload:ne(n,e,r),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function d(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function c(n){if(!n.currentRunId||!n.currentRunPhase)return;const r=n.heartbeatSeq++;t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:n.currentRunId,seq:r,runPhase:n.currentRunPhase},seq:r,id:`agent-heartbeat-${n.currentRunId}-${r}-${Date.now()}`})}function m(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{c(n)},ae),n.heartbeatTimer.unref?.())}function I(n){const r=n?.pendingTextEvent;!n||!r||!r.text||(u({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),n.pendingTextEvent=null)}s.on("agentEvent",n=>{const r=t.sessions.get(e),A=re(n),R=n.state==="final"||n.state==="error"||n.state==="aborted";r&&(r.nextEventSeq=n.seq+1,R||(r.currentRunId=n.runId),!R&&A&&(r.currentRunPhase=A,m(r)),n.agentSessionId&&(r.agentSessionId=n.agentSessionId)),t.managerRuntime?.noteAgentEvent(e,n),n.state!=="delta"&&S({level:"info",sessionId:e,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?E(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?E(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,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(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:K(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&E(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.message});const y=`${e}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&t.runTextAcc.set(y,(t.runTextAcc.get(y)??"")+n.text),n.agentSessionId&&n.agentSessionId!==l&&(l=n.agentSessionId,t.nativeFusion?.noteManagedSourceSession(e,L(a,n.source),n.agentSessionId),r&&B({sessionId:e,agentType:a,workDir:r.workDir,agentSessionId:n.agentSessionId}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const h=n.text??"";if(!h)return;const b=!!n.thinking;r?.pendingTextEvent&&(r.pendingTextEvent.runId!==n.runId||r.pendingTextEvent.thinking!==b)&&I(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:b}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=h,r.pendingTextEvent.seq=n.seq);return}let x={};if(n.state==="final"){I(r);const h=t.runTextAcc.get(y)??"";h&&(x={messageSummary:te(h)}),t.runTextAcc.delete(y)}else n.state==="error"||n.state==="aborted"?(I(r),t.runTextAcc.delete(y)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&I(r);R&&r?.currentRunId===n.runId&&(r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,d(r),t.chatQueue?.noteTerminal(e)),u(n,x)}),s.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${a}: ${n.message}`),t.sessions.delete(e),t.chatQueue?.noteTerminal(e),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:n.message,runId:"",seq:0}})})}function ie(t,e){if(t.processedReqIds.add(e),t.processedReqIds.size>1e3){const a=t.processedReqIds.values().next().value;t.processedReqIds.delete(a)}}async function M(t){t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function G(t,e,a,s,l,u){return u?.trim()?t.send(e,a,s,l,u):l?.length?t.send(e,a,s,l):s?t.send(e,a,s):t.send(e,a)}async function ce(t,e,a,s,l,u,d){t.evictIdleSessions();const c=J(a);if(!c)throw new Error(`Unsupported agent: ${a}`);j(c,e,a,u,d),await c.start(e,s,l);const m={adapter:c,workDir:s,agentType:a,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:u??null,externalReplyTarget:d??null,externalChannelEnv:{...W(a),...H(e,u,d)}};return t.sessions.set(e,m),de(t,e,a,c),m}function ue(t,e){const a=t.sessions.get(e),s=a?.currentRunId;if(!a||!s)return;const l=a.nextEventSeq;t.runTextAcc.delete(`${e}:${s}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:e,runId:s,seq:l},seq:l,id:`agent-evt-${s}-${l}`}),t.chatQueue?.noteTerminal(e)}async function xe(t,e){if(t.processedReqIds.has(e.id)){t.client.sendRes({type:"res",id:e.id,ok:!0});return}ie(t,e.id);const{sessionId:a,text:s,agentType:l,workDir:u,agentSessionId:d,modelId:c,systemPrompt:m,managerDefaultWorkerAgentType:I,managerDefaultWorkerModelId:n,reasoningEffort:r,clientMessageId:A,sessionListProjection:R,waitForDispatch:y,responseId:x,roomContextRegistryKey:h,suppressExternalOwnerEcho:b}=e.params,T=x||e.id;V(R);const $=null,_="";if(!a||!s){t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:T,ok:!1,error:"sessionId and text are required"});return}const g=l;g==="manager"&&t.managerRuntime?.setManagerWorkerDefaults(a,I??null,n??null);const q=(g==="claude"||g==="codex"||g==="workbuddy")&&typeof r=="string"&&r.trim()?r.trim():void 0,w=t.resolvePath(le(g,u||z.homedir(),d)||z.homedir()),D=w,P=ee(e.params.attachments),f=P?.length?await Z({text:s,attachments:P,workDir:w}):{text:s,attachments:P,localized:!1};let o=t.sessions.get(a);if(o){if(o.lastActiveAt=Date.now(),o.agentType!==g||o.workDir!==w||JSON.stringify(o.externalChannel??null)!==JSON.stringify($??null)){t.sessions.delete(a);try{await M(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}else if(d&&o.agentSessionId!==d)try{await o.adapter.resume(d),o.agentSessionId=d}catch{t.sessions.delete(a);try{await M(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}}if(!o)try{o=await ce(t,a,g,w,d,$,_)}catch(i){const p=i instanceof Error&&i.message.startsWith("Unsupported agent:")?i.message:`Failed to start ${l}: ${i instanceof Error?i.message:String(i)}`;console.error(`[chat.send] start failed reqId=${e.id} sessionId=${a} agentType=${l} workDir=${w} agentSessionId=${d??""}: ${p}`),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:p,runId:"",seq:0}}),t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:T,ok:!1,error:p});return}h&&/^[a-f0-9]{64}$/.test(h)&&j(o.adapter,a,g,$,_,h);const C={id:A??`user-${e.id}`,sessionId:a,role:"user",ts:Date.now(),payload:Q(f.text,f.attachments)};S({level:"info",sessionId:a,wsEvent:"chat.send.start",metadata:{reqId:e.id,agentType:g,modelId:c,reasoningEffort:q}});const F=i=>{oe(t,{sessionId:a,agentType:g,workDir:D,agentSessionId:o.agentSessionId??d??null,modelId:c}),o.currentRunId||(o.nextEventSeq=0),t.nativeFusion?.registerManagedSend({sessionId:a,agentType:g,sourceAgentType:L(g,c),canonicalMessageId:A??null,sourceSessionKey:o.agentSessionId??d??null,text:f.text,managedEchoPolicy:i?.deliveryMode==="external_owner_queue"&&!b?"import_following":"suppress_following"}),E(a,C),O(t,C,{agentType:g,workDir:D,agentSessionId:o.agentSessionId??d??null,modelId:c})},N=async(i,p)=>{const k=se(g,i);console.error(`[chat.send] send failed reqId=${e.id} sessionId=${a} agentType=${l} workDir=${w} agentSessionId=${o.agentSessionId??d??""}: ${k}`),t.sessions.delete(a);try{await M(o)}catch{}if(t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:a,message:k,runId:"",seq:0}}),!p){const v={id:`agent-error-${e.id}-${Date.now()}`,sessionId:a,role:"agent",ts:Date.now(),payload:k};E(a,v),O(t,v,{agentType:g,workDir:D,agentSessionId:o.agentSessionId??d??null,modelId:c})}p&&(t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:T,ok:!1,error:k}))};if(y){let i;try{const p=f.attachments;i=await G(o.adapter,f.text,c,q,p,m),S({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}catch(p){await N(p,!0);return}F(i??void 0),t.client.sendRes({type:"res",id:T,ok:!0,...f.localized||i?.deliveryMode==="external_owner_queue"?{payload:{...f.localized?{localizedAttachments:!0}:{},...i?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:i.deliveryMode,queuedSubmissionId:i.queuedSubmissionId}:{}}}:{}}),S({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});return}F(),t.client.sendRes({type:"res",id:T,ok:!0,...f.localized?{payload:{localizedAttachments:!0}}:{}}),S({level:"info",sessionId:a,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});const U=f.attachments;G(o.adapter,f.text,c,q,U,m).then(()=>{S({level:"info",sessionId:a,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}).catch(i=>{N(i,!1)})}async function be(t,e){const{sessionId:a}=e.params,s=t.sessions.get(a);if(s){try{await s.adapter.stop()}catch{}ue(t,a),t.activityPublisher?.publish(a,null)}t.client.sendRes({type:"res",id:e.id,ok:!0})}export{be as handleChatAbort,xe as handleChatSend,oe as sendSessionUpdateEvent};
|
|
1
|
+
import z from"node:os";import{createAgent as K}from"../../agents/adapter.js";import{buildApprovalPendingPayload as Q,buildUserMessagePayload as Y}from"@shennian/wire";import{reportLog as R}from"../../log-reporter.js";import{lookupClaudeTranscriptCwd as V}from"../../native-fusion/parsers.js";import{appendMessage as T,recordSession as B}from"../store.js";import{mergeProjectedSessions as X}from"../projection.js";import{buildManagedAgentEnv as Z}from"../../agents/config-status.js";import{materializeRemoteChatAttachments as ee}from"../remote-attachments.js";function te(t){if(!Array.isArray(t))return;const e=t.map(a=>{if(!a||typeof a!="object")return null;const r=a,l=typeof r.path=="string"?r.path:"",d=typeof r.name=="string"?r.name:"",g=typeof r.mimeType=="string"?r.mimeType:"";if(!l||!d||!g)return null;const i=typeof r.previewData=="string"&&r.previewData.trim()?r.previewData.trim():void 0;return{path:l,name:d,mimeType:g,kind:g.startsWith("image/")?"image":"file",...i?{previewData:i}:{}}}).filter(a=>a!=null);return e.length?e:void 0}function ne(t){const e=t.indexOf(`
|
|
2
|
+
`),a=e>0?Math.min(e,80):Math.min(t.length,80);return t.slice(0,a)}function ae(t,e,a={}){if(t.state==="tool-call"||t.state==="tool-result"){const r={runId:t.runId,sourceSeq:t.seq};return{state:t.state,runId:t.runId,seq:t.seq,sessionId:e,detailRef:r,...t.name?{name:t.name}:{},...t.source?{source:t.source}:{},...t.agentSessionId?{agentSessionId:t.agentSessionId}:{},...a}}return{...t,sessionId:e,...a}}const re=3e4;function se(t){return t.state==="heartbeat"?t.runPhase??null:t.state==="tool-call"||t.state==="tool-result"?"tool_running":t.state==="approval-pending"?"waiting_approval":t.state==="delta"?t.thinking?"thinking":"streaming_text":t.state==="init"||t.state==="start"?"thinking":null}function oe(t,e){return`Agent send failed: ${e instanceof Error?e.message:String(e)}`}function L(t,e){return t!=="manager"?t:e==="claude"?"claude":"codex"}function O(t,e,a){t.client.sendEvent({type:"event",event:"session.message",payload:{sessionId:e.sessionId,message:e,session:{id:e.sessionId,agentType:a.agentType,agentSessionId:a.agentSessionId??null,modelId:a.modelId??null,workDir:a.workDir,status:"active",externalChannel:null}}})}function le(t,e){B({sessionId:e.sessionId,agentType:e.agentType,workDir:e.workDir,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e.sessionId,agentType:e.agentType,agentSessionId:e.agentSessionId??null,modelId:e.modelId??null,workDir:e.workDir,status:"active",externalChannel:null}}})}function Re(t){if(!t||typeof t!="object")return null;const e=t;return{configured:e.configured===void 0?void 0:!!e.configured,connected:!!e.connected,type:typeof e.type=="string"?e.type:null,channelId:typeof e.channelId=="string"?e.channelId:null,name:typeof e.name=="string"?e.name:null,canReply:e.canReply===void 0||e.canReply===null?null:!!e.canReply,systemPrompt:typeof e.systemPrompt=="string"?e.systemPrompt:null,wechatRpaSource:typeof e.wechatRpaSource=="string"?e.wechatRpaSource:null,wechatRpaGroups:Array.isArray(e.wechatRpaGroups)?e.wechatRpaGroups.map(a=>({name:String(a?.name||"").trim()})).filter(a=>a.name):null,pollIntervalMs:Number.isFinite(e.pollIntervalMs)?Number(e.pollIntervalMs):null,recentLimit:Number.isFinite(e.recentLimit)?Number(e.recentLimit):null,idleSeconds:Number.isFinite(e.idleSeconds)?Number(e.idleSeconds):null,forceForeground:e.forceForeground===void 0||e.forceForeground===null?null:!!e.forceForeground,noRestore:e.noRestore===void 0||e.noRestore===null?null:!!e.noRestore,downloadAttachments:e.downloadAttachments===void 0||e.downloadAttachments===null?null:!!e.downloadAttachments,downloadAttachmentsDir:typeof e.downloadAttachmentsDir=="string"?e.downloadAttachmentsDir:null}}function Te(t){return!!(t?.configured??t?.connected)}function W(t){return Z(t)}function H(t,e,a){return{}}function j(t,e,a,r,l,d){t.configure?.({sessionId:e,externalChannel:r??null,env:{...W(a),...H(e,r,l),...d?{SHENNIAN_ROOM_REGISTRY_KEY:d}:{}}})}function xe(t,e,a){return null}function ie(t,e,a){if(t!=="claude"||!a)return e;const r=e.trim();return!!r&&r.startsWith("-")&&!r.includes("/")?V(a)??e:e}function de(t,e,a,r){let l=null;function d(n,s={}){t.client.sendAgentEvent({type:"event",event:"agent",payload:ae(n,e,s),seq:n.seq,id:`agent-evt-${n.runId}-${n.seq}`})}function g(n){n?.heartbeatTimer&&(clearInterval(n.heartbeatTimer),n.heartbeatTimer=null)}function i(n){if(!n.currentRunId||!n.currentRunPhase)return;const s=n.heartbeatSeq++;t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:n.currentRunId,seq:s,runPhase:n.currentRunPhase},seq:s,id:`agent-heartbeat-${n.currentRunId}-${s}-${Date.now()}`})}function f(n){!n||n.heartbeatTimer||(n.heartbeatTimer=setInterval(()=>{i(n)},re),n.heartbeatTimer.unref?.())}function m(n){const s=n?.pendingTextEvent;!n||!s||!s.text||(d({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=t.sessions.get(e),w=se(n),S=n.state==="final"||n.state==="error"||n.state==="aborted";s&&(s.nextEventSeq=n.seq+1,S||(s.currentRunId=n.runId),!S&&w&&(s.currentRunPhase=w,f(s)),n.agentSessionId&&(s.agentSessionId=n.agentSessionId)),t.managerRuntime?.noteAgentEvent(e,n),n.state!=="delta"&&R({level:"info",sessionId:e,wsEvent:`agent.${n.state}`,wsDirection:"out",metadata:{runId:n.runId,seq:n.seq,agentType:a}}),n.state==="delta"&&n.text&&!n.thinking?T(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.text}):n.state==="tool-call"||n.state==="tool-result"?T(e,{id:`agent-${n.runId}-${n.seq}`,sessionId:e,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"?T(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:Q(n.approval)}):(n.state==="error"||n.state==="aborted")&&n.message&&T(e,{id:`agent-${n.runId||"run"}-${n.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:n.message});const y=`${e}:${n.runId}`;if(n.state==="delta"&&!n.thinking&&n.text&&t.runTextAcc.set(y,(t.runTextAcc.get(y)??"")+n.text),n.agentSessionId&&n.agentSessionId!==l&&(l=n.agentSessionId,t.nativeFusion?.noteManagedSourceSession(e,L(a,n.source),n.agentSessionId),s&&B({sessionId:e,agentType:a,workDir:s.workDir,agentSessionId:n.agentSessionId}),t.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:a,agentSessionId:n.agentSessionId}}})),n.state==="delta"){const I=n.text??"";if(!I)return;const E=!!n.thinking;s?.pendingTextEvent&&(s.pendingTextEvent.runId!==n.runId||s.pendingTextEvent.thinking!==E)&&m(s),s&&!s.pendingTextEvent&&(s.pendingTextEvent={runId:n.runId,seq:n.seq,text:"",thinking:E}),s?.pendingTextEvent&&(s.pendingTextEvent.text+=I,s.pendingTextEvent.seq=n.seq);return}let b={};if(n.state==="final"){m(s);const I=t.runTextAcc.get(y)??"";I&&(b={messageSummary:ne(I)}),t.runTextAcc.delete(y)}else n.state==="error"||n.state==="aborted"?(m(s),t.runTextAcc.delete(y)):(n.state==="tool-call"||n.state==="tool-result"||n.state==="approval-pending")&&m(s);S&&s?.currentRunId===n.runId&&(s.currentRunId=null,s.currentRunPhase=null,s.nextEventSeq=0,g(s),t.chatQueue?.noteTerminal(e)),d(n,b)}),r.on("error",n=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${a}: ${n.message}`),t.sessions.delete(e),t.chatQueue?.noteTerminal(e),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:n.message,runId:"",seq:0}})})}function ce(t,e){if(t.processedReqIds.add(e),t.processedReqIds.size>1e3){const a=t.processedReqIds.values().next().value;t.processedReqIds.delete(a)}}async function M(t){t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),t.adapter.removeAllListeners(),await t.adapter.stop().catch(()=>{})}function G(t,e,a,r,l,d,g){return g?t.send(e,a,r,l,d,g):d?.trim()?t.send(e,a,r,l,d):l?.length?t.send(e,a,r,l):r?t.send(e,a,r):t.send(e,a)}async function ue(t,e,a,r,l,d,g){t.evictIdleSessions();const i=K(a);if(!i)throw new Error(`Unsupported agent: ${a}`);j(i,e,a,d,g),await i.start(e,r,l);const f={adapter:i,workDir:r,agentType:a,agentSessionId:l??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:d??null,externalReplyTarget:g??null,externalChannelEnv:{...W(a),...H(e,d,g)}};return t.sessions.set(e,f),de(t,e,a,i),f}function ge(t,e){const a=t.sessions.get(e),r=a?.currentRunId;if(!a||!r)return;const l=a.nextEventSeq;t.runTextAcc.delete(`${e}:${r}`),a.pendingTextEvent=null,a.currentRunId=null,a.currentRunPhase=null,a.nextEventSeq=0,a.heartbeatTimer&&(clearInterval(a.heartbeatTimer),a.heartbeatTimer=null),t.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:e,runId:r,seq:l},seq:l,id:`agent-evt-${r}-${l}`}),t.chatQueue?.noteTerminal(e)}async function be(t,e,a){if(t.processedReqIds.has(e.id)){t.client.sendRes({type:"res",id:e.id,ok:!0});return}ce(t,e.id);const{sessionId:r,text:l,agentType:d,workDir:g,agentSessionId:i,modelId:f,systemPrompt:m,managerDefaultWorkerAgentType:n,managerDefaultWorkerModelId:s,reasoningEffort:w,clientMessageId:S,sessionListProjection:y,waitForDispatch:b,responseId:I,roomContextRegistryKey:E,suppressExternalOwnerEcho:U}=e.params,x=I||e.id;X(y);const $=null,_="";if(!r||!l){t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:x,ok:!1,error:"sessionId and text are required"});return}const u=d;u==="manager"&&t.managerRuntime?.setManagerWorkerDefaults(r,n??null,s??null);const q=(u==="claude"||u==="codex"||u==="workbuddy")&&typeof w=="string"&&w.trim()?w.trim():void 0,A=t.resolvePath(ie(u,g||z.homedir(),i)||z.homedir()),D=A,P=te(e.params.attachments),p=P?.length?await ee({text:l,attachments:P,workDir:A}):{text:l,attachments:P,localized:!1};let o=t.sessions.get(r);if(o){if(o.lastActiveAt=Date.now(),o.agentType!==u||o.workDir!==A||JSON.stringify(o.externalChannel??null)!==JSON.stringify($??null)){t.sessions.delete(r);try{await M(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}else if(i&&o.agentSessionId!==i)try{await o.adapter.resume(i),o.agentSessionId=i}catch{t.sessions.delete(r);try{await M(o)}catch{t.processedReqIds.delete(e.id)}o=void 0}}if(!o)try{o=await ue(t,r,u,A,i,$,_)}catch(c){const h=c instanceof Error&&c.message.startsWith("Unsupported agent:")?c.message:`Failed to start ${d}: ${c instanceof Error?c.message:String(c)}`;console.error(`[chat.send] start failed reqId=${e.id} sessionId=${r} agentType=${d} workDir=${A} agentSessionId=${i??""}: ${h}`),t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:h,runId:"",seq:0}}),t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:x,ok:!1,error:h});return}E&&/^[a-f0-9]{64}$/.test(E)&&j(o.adapter,r,u,$,_,E);const C={id:S??`user-${e.id}`,sessionId:r,role:"user",ts:Date.now(),payload:Y(p.text,p.attachments)};R({level:"info",sessionId:r,wsEvent:"chat.send.start",metadata:{reqId:e.id,agentType:u,modelId:f,reasoningEffort:q}});const F=c=>{le(t,{sessionId:r,agentType:u,workDir:D,agentSessionId:o.agentSessionId??i??null,modelId:f}),o.currentRunId||(o.nextEventSeq=0),t.nativeFusion?.registerManagedSend({sessionId:r,agentType:u,sourceAgentType:L(u,f),canonicalMessageId:S??null,sourceSessionKey:o.agentSessionId??i??null,text:p.text,managedEchoPolicy:c?.deliveryMode==="external_owner_queue"&&!U?"import_following":"suppress_following"}),T(r,C),O(t,C,{agentType:u,workDir:D,agentSessionId:o.agentSessionId??i??null,modelId:f})},N=async(c,h)=>{const k=oe(u,c);console.error(`[chat.send] send failed reqId=${e.id} sessionId=${r} agentType=${d} workDir=${A} agentSessionId=${o.agentSessionId??i??""}: ${k}`),t.sessions.delete(r);try{await M(o)}catch{}if(t.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:k,runId:"",seq:0}}),!h){const v={id:`agent-error-${e.id}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:k};T(r,v),O(t,v,{agentType:u,workDir:D,agentSessionId:o.agentSessionId??i??null,modelId:f})}h&&(t.processedReqIds.delete(e.id),t.client.sendRes({type:"res",id:x,ok:!1,error:k}))};if(b){let c;try{const h=p.attachments;c=await G(o.adapter,p.text,f,q,h,m,a),R({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}catch(h){await N(h,!0);return}F(c??void 0),t.client.sendRes({type:"res",id:x,ok:!0,...p.localized||c?.deliveryMode==="external_owner_queue"?{payload:{...p.localized?{localizedAttachments:!0}:{},...c?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:c.deliveryMode,queuedSubmissionId:c.queuedSubmissionId}:{}}}:{}}),R({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});return}F(),t.client.sendRes({type:"res",id:x,ok:!0,...p.localized?{payload:{localizedAttachments:!0}}:{}}),R({level:"info",sessionId:r,wsEvent:"chat.send.res",metadata:{reqId:e.id,ok:!0}});const J=p.attachments;G(o.adapter,p.text,f,q,J,m,a).then(()=>{R({level:"info",sessionId:r,wsEvent:"chat.send.done",metadata:{reqId:e.id}})}).catch(c=>{N(c,!1)})}async function ke(t,e){const{sessionId:a}=e.params,r=t.sessions.get(a);if(r){try{await r.adapter.stop()}catch{}ge(t,a),t.activityPublisher?.publish(a,null)}t.client.sendRes({type:"res",id:e.id,ok:!0})}export{ke as handleChatAbort,be as handleChatSend,le as sendSessionUpdateEvent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import g from"node:crypto";import{getRegisteredAgents as m,unregisterAgent as f}from"../agents/adapter.js";import{loadConfig as p}from"../config/index.js";import{handleUpgradeStart as w,handleUpgradeStatus as b}from"../commands/upgrade.js";import{handleAgentsRefresh as v,handleModelsRefresh as R}from"./handlers/agents.js";import{handleAgentCapabilitiesList as k}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as y,handleAgentConfigGet as S,handleAgentConfigTest as I,handleAgentConfigUpsert as A}from"./handlers/agent-config.js";import{handleChatAbort as T,handleChatSend as c,sendSessionUpdateEvent as P}from"./handlers/chat.js";import{handleSessionRefresh as M}from"./handlers/session-refresh.js";import{handleSessionToolDetail as _}from"./handlers/tool-detail.js";import{handleSessionTitleSet as C}from"./handlers/title.js";import{cleanupPendingTransfers as F,handleFsLs as x,handleFsRead as D,handleFsRename as E,handleFsWrite as O,handleFsTransfer as Q,handleFsTransferAbort as j,handleFsTransferChunk as z,handleFsTransferFinish as U,handleFsTransferStart as V,handleFsExportMarkdownPdf as $,handleFsExportMarkdownPdfSetup as B,handleFsArchiveZip as W}from"./handlers/fs.js";import{handleRegionProbe as L,handleRegionSwitch as G,handleUpgradeSetPolicy as K}from"./handlers/control.js";import{ManagerRuntimeService as N,setManagerRuntimeService as h}from"../manager/runtime.js";import{ChatQueueManager as H}from"./queue.js";import{createAuthorizedFsRoot as X,resolveAuthorizedPath as Z,resolveSessionWorkDir as J}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/manager.js";import{registerCustomAgent as Y}from"../agents/custom.js";import{handleManagedRoomBind as q,handleManagedRoomRestart as ee}from"./handlers/room-managed.js";import{listSessionRecords as d}from"./store.js";const te=50;import{resolveSessionWorkDir as xe}from"../fs/boundary.js";class _e{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;activityProbeTimer=null;activeRequests=0;constructor(e,t=null,s,n=null,i=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=n,this.managedRoomBinder=i,this.managerRuntime=new N({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a)}),this.chatQueue=new H({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a)}),h(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}async createManagedRoomSession(e){const t=g.createHash("sha256").update(e.creationKey,"utf8").digest("hex"),s=`room-managed-${t.slice(0,48)}`,n=d().find(r=>r.sessionId===s);if(n&&(n.agentType!==e.agentType||n.workDir!==e.workDir))throw Object.assign(new Error("Managed Room Session identity conflicts with local state."),{code:"managed_room_session_conflict"});if(n?.agentSessionId)return{nianSessionId:s};const i={value:null},a=new Proxy(this.client,{get:(r,l)=>{if(l==="sendRes")return u=>{i.value=u};const o=Reflect.get(r,l,r);return typeof o=="function"?o.bind(r):o}});if(await c({...this.getRuntime(),client:a},{type:"req",id:`managed-create-${t.slice(0,32)}`,method:"chat.send",params:{sessionId:s,text:e.initialMessage,agentType:e.agentType,workDir:e.workDir,modelId:e.modelId??void 0,clientMessageId:`managed-bootstrap-${t.slice(0,32)}`,waitForDispatch:!0}}),i.value?.ok!==!0)throw Object.assign(new Error(i.value?.error||"Managed Agent failed to start."),{code:"managed_agent_start_failed"});return{nianSessionId:s}}async waitForManagedNativeSession(e){const t=Date.now();for(;Date.now()-t<e.timeoutMs;){const s=this.sessions.get(e.nianSessionId);if(s&&s.agentType!==e.agentType)throw Object.assign(new Error("Managed Agent runtime does not match the created session."),{code:"managed_room_session_conflict"});const n=d().find(a=>a.sessionId===e.nianSessionId),i=s?.agentSessionId??n?.agentSessionId;if(i)return{sourceSessionKey:i};await new Promise(a=>setTimeout(a,100))}throw Object.assign(new Error("Timed out waiting for the exact native Agent Session."),{code:"native_session_timeout"})}publishManagedRoomSession(e){const t=d().find(s=>s.sessionId===e.nianSessionId);if(!t?.agentSessionId||t.agentType!==e.agentType)throw Object.assign(new Error("Managed Agent Session is unavailable for publication."),{code:"managed_room_session_unavailable"});if(this.client.getState()!=="connected")throw Object.assign(new Error("Shennian Relay is disconnected."),{code:"managed_room_relay_disconnected"});P(this.getRuntime(),{sessionId:t.sessionId,agentType:t.agentType,workDir:t.workDir,agentSessionId:t.agentSessionId,modelId:t.modelId??void 0})}async activateRoomSession(e){const t={value:null},s=new Proxy(this.client,{get:(n,i)=>{if(i==="sendRes")return r=>{t.value=r};const a=Reflect.get(n,i,n);return typeof a=="function"?a.bind(n):a}});return await c({...this.getRuntime(),client:s},{type:"req",id:e.clientMessageId,method:"chat.send",params:{...e,waitForDispatch:!0,suppressExternalOwnerEcho:!0}}),{externalOwnerQueued:t.value?.ok===!0&&t.value.payload?.deliveryMode==="external_owner_queue"}}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const n=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:n,canStop:s.canStop??!0});const i=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:i,runPhase:s.runPhase,canStop:s.canStop??!0},seq:i,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of m())t.startsWith("custom:")&&f(t);const e=p();for(const[t,s]of Object.entries(e.customAgents??{}))Y(t,s)}async handleReq(e){const t=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":await c(t,e);break;case"chat.enqueue":await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":await this.chatQueue.handleGet(e);break;case"chat.queue.edit":await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":await this.chatQueue.handleDelete(e);break;case"chat.abort":await T(t,e);break;case"session.refresh":await M(t,e);break;case"session.tool.detail":await _(t,e);break;case"session.title.set":await C(t,e);break;case"room.agent.bind-managed":await q(t,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await ee(t,this.managedRoomBinder,e);break;case"fs.ls":await x(t,e);break;case"fs.read":await D(t,e);break;case"fs.write":await O(t,e);break;case"fs.export.markdown-pdf":await $(t,e);break;case"fs.export.markdown-pdf.setup":await B(t,e);break;case"fs.archive.zip":await W(t,e);break;case"fs.rename":await E(t,e);break;case"fs.transfer":await Q(t,e);break;case"fs.transfer.start":await V(t,e);break;case"fs.transfer.chunk":await z(t,e);break;case"fs.transfer.finish":await U(t,e);break;case"fs.transfer.abort":await j(t,e);break;case"region.probe":await L(t,e);break;case"region.switch":await G(t,e);break;case"upgrade.start":await w(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await b(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await K(t,e);break;case"agents.refresh":await v(t,e);break;case"models.refresh":await R(t,e);break;case"agent.config.get":await S(t,e);break;case"agent.config.upsert":await A(t,e);break;case"agent.config.clear":await y(t,e);break;case"agent.config.test":await I(t,e);break;case"agent.capabilities.list":await k(t,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(s){this.client.sendRes({type:"res",id:e.id,ok:!1,error:s instanceof Error?s.message:String(s)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<te)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return J(e)}resolveAuthorizedPath(e,t){return Z(e,X(t))}async cleanup(){this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null);for(const[,e]of this.sessions)e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),F(this.getRuntime()),await this.managerRuntime.stop(),h(null)}}export{_e as SessionManager,xe as resolveSessionWorkDir};
|
|
1
|
+
import m from"node:crypto";import{getRegisteredAgents as g,unregisterAgent as f}from"../agents/adapter.js";import{loadConfig as p}from"../config/index.js";import{handleUpgradeStart as w,handleUpgradeStatus as b}from"../commands/upgrade.js";import{handleAgentsRefresh as v,handleModelsRefresh as R}from"./handlers/agents.js";import{handleAgentCapabilitiesList as k}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as y,handleAgentConfigGet as S,handleAgentConfigTest as I,handleAgentConfigUpsert as A}from"./handlers/agent-config.js";import{handleChatAbort as T,handleChatSend as c,sendSessionUpdateEvent as P}from"./handlers/chat.js";import{handleSessionRefresh as M}from"./handlers/session-refresh.js";import{handleSessionToolDetail as _}from"./handlers/tool-detail.js";import{handleSessionTitleSet as C}from"./handlers/title.js";import{cleanupPendingTransfers as F,handleFsLs as x,handleFsRead as D,handleFsRename as E,handleFsWrite as O,handleFsTransfer as Q,handleFsTransferAbort as j,handleFsTransferChunk as z,handleFsTransferFinish as U,handleFsTransferStart as V,handleFsExportMarkdownPdf as $,handleFsExportMarkdownPdfSetup as B,handleFsArchiveZip as W}from"./handlers/fs.js";import{handleRegionProbe as L,handleRegionSwitch as G,handleUpgradeSetPolicy as K}from"./handlers/control.js";import{ManagerRuntimeService as N,setManagerRuntimeService as h}from"../manager/runtime.js";import{ChatQueueManager as H}from"./queue.js";import{createAuthorizedFsRoot as X,resolveAuthorizedPath as Z,resolveSessionWorkDir as J}from"../fs/boundary.js";import"../agents/claude.js";import"../agents/codex.js";import"../agents/workbuddy.js";import"../agents/gemini.js";import"../agents/cursor.js";import"../agents/opencode.js";import"../agents/manager.js";import{registerCustomAgent as Y}from"../agents/custom.js";import{handleManagedRoomBind as q,handleManagedRoomRestart as ee}from"./handlers/room-managed.js";import{listSessionRecords as d}from"./store.js";const te=50;import{resolveSessionWorkDir as xe}from"../fs/boundary.js";class _e{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;activityProbeTimer=null;activeRequests=0;constructor(e,t=null,s,n=null,i=null){this.client=e,this.nativeFusion=t,this.cliVersion=s,this.upgradePolicyController=n,this.managedRoomBinder=i,this.managerRuntime=new N({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a)}),this.chatQueue=new H({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a)}),h(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents(),this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.()}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,t)=>this.resolveAuthorizedPath(e,t),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,t)=>this.publishSessionActivity(e,t)},upgradePolicyController:this.upgradePolicyController}}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(t=>t.currentRunId!==null)&&e.push("active-agent-turn"),this.pendingTransfers.size>0&&e.push("file-transfer"),this.activeRequests>0&&e.push("active-daemon-request"),{idle:e.length===0,reasons:e}}async createManagedRoomSession(e){const t=m.createHash("sha256").update(e.creationKey,"utf8").digest("hex"),s=`room-managed-${t.slice(0,48)}`,n=d().find(r=>r.sessionId===s);if(n&&(n.agentType!==e.agentType||n.workDir!==e.workDir))throw Object.assign(new Error("Managed Room Session identity conflicts with local state."),{code:"managed_room_session_conflict"});if(n?.agentSessionId)return{nianSessionId:s};const i={value:null},a=new Proxy(this.client,{get:(r,l)=>{if(l==="sendRes")return u=>{i.value=u};const o=Reflect.get(r,l,r);return typeof o=="function"?o.bind(r):o}});if(await c({...this.getRuntime(),client:a},{type:"req",id:`managed-create-${t.slice(0,32)}`,method:"chat.send",params:{sessionId:s,text:e.initialMessage,agentType:e.agentType,workDir:e.workDir,modelId:e.modelId??void 0,clientMessageId:`managed-bootstrap-${t.slice(0,32)}`,waitForDispatch:!0}}),i.value?.ok!==!0)throw Object.assign(new Error(i.value?.error||"Managed Agent failed to start."),{code:"managed_agent_start_failed"});return{nianSessionId:s}}async waitForManagedNativeSession(e){const t=Date.now();for(;Date.now()-t<e.timeoutMs;){const s=this.sessions.get(e.nianSessionId);if(s&&s.agentType!==e.agentType)throw Object.assign(new Error("Managed Agent runtime does not match the created session."),{code:"managed_room_session_conflict"});const n=d().find(a=>a.sessionId===e.nianSessionId),i=s?.agentSessionId??n?.agentSessionId;if(i)return{sourceSessionKey:i};await new Promise(a=>setTimeout(a,100))}throw Object.assign(new Error("Timed out waiting for the exact native Agent Session."),{code:"native_session_timeout"})}publishManagedRoomSession(e){const t=d().find(s=>s.sessionId===e.nianSessionId);if(!t?.agentSessionId||t.agentType!==e.agentType)throw Object.assign(new Error("Managed Agent Session is unavailable for publication."),{code:"managed_room_session_unavailable"});if(this.client.getState()!=="connected")throw Object.assign(new Error("Shennian Relay is disconnected."),{code:"managed_room_relay_disconnected"});P(this.getRuntime(),{sessionId:t.sessionId,agentType:t.agentType,workDir:t.workDir,agentSessionId:t.agentSessionId,modelId:t.modelId??void 0})}async activateRoomSession(e){const t={value:null},s=new Proxy(this.client,{get:(n,i)=>{if(i==="sendRes")return r=>{t.value=r};const a=Reflect.get(n,i,n);return typeof a=="function"?a.bind(n):a}});return await c({...this.getRuntime(),client:s},{type:"req",id:e.clientMessageId,method:"chat.send",params:{...e,waitForDispatch:!0,suppressExternalOwnerEcho:!0}},{source:"room_activation"}),{externalOwnerQueued:t.value?.ok===!0&&t.value.payload?.deliveryMode==="external_owner_queue"}}publishSessionActivity(e,t){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:t}})}async publishManagedActivitySnapshots(){for(const[e,t]of this.sessions.entries()){if(!t.currentRunId||!t.adapter.getStatus)continue;const s=await t.adapter.getStatus().catch(()=>null);if(!s?.active||!s.runPhase)continue;const n=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:s.runId||t.currentRunId,runPhase:s.runPhase,startedAt:new Date(t.lastActiveAt).toISOString(),updatedAt:n,canStop:s.canStop??!0});const i=t.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:s.runId||t.currentRunId,seq:i,runPhase:s.runPhase,canStop:s.canStop??!0},seq:i,id:`agent-status-${s.runId||t.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const t of g())t.startsWith("custom:")&&f(t);const e=p();for(const[t,s]of Object.entries(e.customAgents??{}))Y(t,s)}async handleReq(e){const t=this.getRuntime();this.activeRequests++;try{switch(e.method){case"chat.send":await c(t,e);break;case"chat.enqueue":await this.chatQueue.handleEnqueue(e);break;case"chat.queue.get":await this.chatQueue.handleGet(e);break;case"chat.queue.edit":await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":await this.chatQueue.handleDelete(e);break;case"chat.abort":await T(t,e);break;case"session.refresh":await M(t,e);break;case"session.tool.detail":await _(t,e);break;case"session.title.set":await C(t,e);break;case"room.agent.bind-managed":await q(t,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await ee(t,this.managedRoomBinder,e);break;case"fs.ls":await x(t,e);break;case"fs.read":await D(t,e);break;case"fs.write":await O(t,e);break;case"fs.export.markdown-pdf":await $(t,e);break;case"fs.export.markdown-pdf.setup":await B(t,e);break;case"fs.archive.zip":await W(t,e);break;case"fs.rename":await E(t,e);break;case"fs.transfer":await Q(t,e);break;case"fs.transfer.start":await V(t,e);break;case"fs.transfer.chunk":await z(t,e);break;case"fs.transfer.finish":await U(t,e);break;case"fs.transfer.abort":await j(t,e);break;case"region.probe":await L(t,e);break;case"region.switch":await G(t,e);break;case"upgrade.start":await w(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0});break;case"upgrade.status":await b(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await K(t,e);break;case"agents.refresh":await v(t,e);break;case"models.refresh":await R(t,e);break;case"agent.config.get":await S(t,e);break;case"agent.config.upsert":await A(t,e);break;case"agent.config.clear":await y(t,e);break;case"agent.config.test":await I(t,e);break;case"agent.capabilities.list":await k(t,e);break;case"skill.list":case"skill.install":case"skill.doctor":case"skill.setup":case"skill.use":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;case"session.wechat-rpa.channel.get":case"session.wechat-rpa.channel.upsert":case"session.wechat-rpa.channel.sync":case"manager.wechat-rpa.channel.get":case"manager.wechat-rpa.channel.upsert":case"manager.wechat-rpa.channel.sync":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"feature_retired"});break;default:this.client.sendRes({type:"res",id:e.id,ok:!1,error:`Unknown method: ${e.method}`})}}catch(s){this.client.sendRes({type:"res",id:e.id,ok:!1,error:s instanceof Error?s.message:String(s)})}finally{this.activeRequests--}}evictIdleSessions(){if(this.sessions.size<te)return;let e=null;for(const[t,s]of this.sessions)(!e||s.lastActiveAt<e.session.lastActiveAt)&&(e={key:t,session:s});e&&(e.session.heartbeatTimer&&(clearInterval(e.session.heartbeatTimer),e.session.heartbeatTimer=null),e.session.adapter.removeAllListeners(),e.session.adapter.stop().catch(()=>{}),this.sessions.delete(e.key))}resolvePath(e){return J(e)}resolveAuthorizedPath(e,t){return Z(e,X(t))}async cleanup(){this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null);for(const[,e]of this.sessions)e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),F(this.getRuntime()),await this.managerRuntime.stop(),h(null)}}export{_e as SessionManager,xe as resolveSessionWorkDir};
|