shennian 0.4.77 → 0.4.79
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 +18 -13
- package/dist/src/index.js +3 -3
- package/dist/src/room/activation-service.js +6 -6
- package/dist/src/room/live-delivery.d.ts +5 -0
- package/dist/src/room/live-delivery.js +1 -0
- package/dist/src/room/mirror-sync-service.d.ts +11 -0
- package/dist/src/room/mirror-sync-service.js +1 -1
- package/dist/src/session/handlers/chat.js +1 -1
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue-types.d.ts +4 -1
- package/dist/src/session/queue.js +1 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts +3 -1
- package/node_modules/@shennian/wire/dist/room.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -2
- package/node_modules/@shennian/wire/dist/session-sync.js +1 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"jsMinified": true,
|
|
4
4
|
"minifier": "esbuild",
|
|
5
|
-
"fileCount":
|
|
5
|
+
"fileCount": 222,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -496,8 +496,8 @@
|
|
|
496
496
|
},
|
|
497
497
|
{
|
|
498
498
|
"file": "src/index.js",
|
|
499
|
-
"beforeBytes":
|
|
500
|
-
"afterBytes":
|
|
499
|
+
"beforeBytes": 42085,
|
|
500
|
+
"afterBytes": 20878
|
|
501
501
|
},
|
|
502
502
|
{
|
|
503
503
|
"file": "src/log-redaction.js",
|
|
@@ -671,8 +671,8 @@
|
|
|
671
671
|
},
|
|
672
672
|
{
|
|
673
673
|
"file": "src/room/activation-service.js",
|
|
674
|
-
"beforeBytes":
|
|
675
|
-
"afterBytes":
|
|
674
|
+
"beforeBytes": 27631,
|
|
675
|
+
"afterBytes": 14835
|
|
676
676
|
},
|
|
677
677
|
{
|
|
678
678
|
"file": "src/room/attachment-cache.js",
|
|
@@ -704,6 +704,11 @@
|
|
|
704
704
|
"beforeBytes": 888,
|
|
705
705
|
"afterBytes": 519
|
|
706
706
|
},
|
|
707
|
+
{
|
|
708
|
+
"file": "src/room/live-delivery.js",
|
|
709
|
+
"beforeBytes": 1945,
|
|
710
|
+
"afterBytes": 1123
|
|
711
|
+
},
|
|
707
712
|
{
|
|
708
713
|
"file": "src/room/managed-context.js",
|
|
709
714
|
"beforeBytes": 4743,
|
|
@@ -726,8 +731,8 @@
|
|
|
726
731
|
},
|
|
727
732
|
{
|
|
728
733
|
"file": "src/room/mirror-sync-service.js",
|
|
729
|
-
"beforeBytes":
|
|
730
|
-
"afterBytes":
|
|
734
|
+
"beforeBytes": 11100,
|
|
735
|
+
"afterBytes": 5019
|
|
731
736
|
},
|
|
732
737
|
{
|
|
733
738
|
"file": "src/room/public-attachment-cache.js",
|
|
@@ -881,8 +886,8 @@
|
|
|
881
886
|
},
|
|
882
887
|
{
|
|
883
888
|
"file": "src/session/handlers/chat.js",
|
|
884
|
-
"beforeBytes":
|
|
885
|
-
"afterBytes":
|
|
889
|
+
"beforeBytes": 33158,
|
|
890
|
+
"afterBytes": 13967
|
|
886
891
|
},
|
|
887
892
|
{
|
|
888
893
|
"file": "src/session/handlers/control.js",
|
|
@@ -956,8 +961,8 @@
|
|
|
956
961
|
},
|
|
957
962
|
{
|
|
958
963
|
"file": "src/session/manager.js",
|
|
959
|
-
"beforeBytes":
|
|
960
|
-
"afterBytes":
|
|
964
|
+
"beforeBytes": 42678,
|
|
965
|
+
"afterBytes": 22322
|
|
961
966
|
},
|
|
962
967
|
{
|
|
963
968
|
"file": "src/session/native-cleanup-outbox.js",
|
|
@@ -1006,8 +1011,8 @@
|
|
|
1006
1011
|
},
|
|
1007
1012
|
{
|
|
1008
1013
|
"file": "src/session/queue.js",
|
|
1009
|
-
"beforeBytes":
|
|
1010
|
-
"afterBytes":
|
|
1014
|
+
"beforeBytes": 49784,
|
|
1015
|
+
"afterBytes": 21900
|
|
1011
1016
|
},
|
|
1012
1017
|
{
|
|
1013
1018
|
"file": "src/session/queued-chat-dispatch.js",
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as Se}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ye}from"./schedules/personal-context.js";import{executeServerSchedule as oe,deliverServerSchedule as we}from"./schedules/server-client.js";import{Command as Ce}from"commander";import
|
|
2
|
-
`),process.exitCode=1}function zo(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Ce;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(q),i.enablePositionalOptions(),ze(i),Je(i),Se(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",be.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Me(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=G("env.json");try{const e=JSON.parse(F.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);le()}catch{}Te(ne(process.env,p()));const g=fo();ae()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=xe(),y=co({lockPath:Go,instanceId:c});y.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(y.ownerPid?` (PID ${y.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=Wo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),y.release(),process.exit(0)}catch{}}catch{}const J=Ve(process.env,fe)?[]:$e(process.pid);J.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${J[0]}), skipping duplicate start`),y.release(),process.exit(0)),se(process.pid,c,{version:q}),ce(process.pid,void 0,c),process.on("exit",()=>{te(process.pid,c),re(c)}),process.on("exit",y.release);let O=null;try{ro()||(O=to())}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const T=await eo();if(T){O||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{ao({fromVersion:q,targetVersion:T.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:T.jobId},{readiness:O}),console.error(`[upgrade] New version crashed ${T.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{jo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??W.cn.url,h=de(),d=await so({serverUrl:u,authToken:m.machineToken??m.accessToken}),w=d.map(e=>e.type),V=N(d),f=new bo,D=new Po,C=new No(f),I=new ko(u),ve=new xo({actionClient:I}),Ie=new $o({actionClient:I});let S=null,r;const K=Oo({authority:C,actionClient:I,attachmentCache:ve,attachmentUploader:Ie,observations:D,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),scheduleExecutor:(e,n,o)=>oe({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let L=null,j=null,H=null;const b=await me({paths:ho(),handlers:{...Io({daemonVersion:h,protocolVersion:Co,getStatus:()=>ie(),getAgents:()=>d,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!L)throw new Error("upgrade_unavailable");return L(e)},prepareExternalUpgrade:e=>{if(!j)throw new Error("external_upgrade_unavailable");return j(e)},cancelExternalUpgrade:()=>{if(!H)throw new Error("external_upgrade_unavailable");return H()}}),...So({getStatus:()=>ie(),getSnapshot:()=>N(d).map(e=>{const n=d.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await yo(!0,e);if(e){const t=d.filter(P=>P.type!==e);d.splice(0,d.length,...t,...n.detectedAgents)}else d.splice(0,d.length,...n.detectedAgents);const o=N(d);return l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:d.map(t=>t.type),agents:o}}),n.agents.map(t=>{const P=n.detectedAgents.find(B=>B.type===t.type);return{...t,runtimePlatform:P?.runtimePlatform,runtimeIsWsl:P?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return wo(e,{ignoreNativeSession:(n,o)=>R?.ignoreNativeSource(n,o)})}})}});let k;try{k=await me({paths:vo(),handlers:{"managed-room.execute":e=>K(e),"schedule.personal":async e=>{const n=e,o=ye(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await oe({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let Y=!1;const U=()=>{Y||(Y=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",U),process.once("SIGTERM",U),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",U),process.removeListener("SIGTERM",U);const Q=`${qo(u)}/relay/machine`,M=m.machineToken;if(!M)return;try{await Lo({serverUrl:u,machineToken:M,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}m.machineId&&mo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${Q}... (v${h}) agents: ${w.join(",")}`),E({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:w}});let R=null,_=null,x=0,v=oo();const Z=Ae(m.autoUpgrade),l=new Ee({serverUrl:Q,machineToken:M,cliVersion:h,agentList:w,autoUpgradePolicy:Z,sessionSyncProtocolVersion:pe,sessionSyncCapabilities:ge,daemonInstallationId:Vo(),dataEpoch:ue,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),E({level:"info",wsEvent:"daemon.connected"}),V.some(o=>o.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:V}}),lo(d,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(V)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),v&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),$.start(),_?.start();const e=++x,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&io(o.jobId,o.to),Xe(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),po({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),l.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===x&&l.getState()==="connected"&&R?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===x&&l.getState()==="connected"){if(o instanceof Bo)return;const t=o instanceof Ho?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{x+=1,r?.revokeProjectFsHandles(),_?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${zo(e)}`),E({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),E({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),r.handleReq(e)}});R=go()?new uo(l):null,_=new Ao({registry:f,observations:D,actionClient:I,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Eo().hasSession(e),onMessages:async(e,n,o,t)=>{await S?.processMessages(e,n,o,t)},onBindingPaused:e=>{C.revokeSession(e.registryKey),e.lifecycleState==="closed"?r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new no({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:pe,dataEpoch:ue,advertisedCapabilities:ge,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(l,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});$.setPolicy(Z),r=new Ke(l,R,h,$,new Ro({registry:f,observations:D,bindingClient:I,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Ne(),isDisabled:()=>ae(),shutdownAfterResponse:()=>{setTimeout(()=>{Oe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),K,null,Ye(M),()=>N(d)),L=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await Ge(l,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:B=>r.prepareForUpgrade(B),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},j=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},H=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),S=new Uo({registry:f,observations:D,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await I.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!Do(f,e)||!To(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o)=>{const t=p().machineToken;if(!t)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:t,activationCauseMessageId:n,allowedActions:o})},audit:new _o,resolveManagedAgentPolicy:e=>Mo({...e,runtime:d.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>we({serverUrl:u,machineToken:p().machineToken,req:e,client:l,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await I.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>S.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[]}):e.source==="manager_event"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:S.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),F.mkdirSync(fe,{recursive:!0}),se(process.pid,c,{version:h}),ce(process.pid,void 0,c),l.connect(),process.stdin.resume();let X=!1;const ee=async()=>{if(!X){X=!0,console.log(A.gray(`
|
|
3
|
-
Disconnecting...`)),
|
|
1
|
+
import{registerScheduleCommand as Se}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ye}from"./schedules/personal-context.js";import{executeServerSchedule as oe,deliverServerSchedule as we}from"./schedules/server-client.js";import{Command as Ce}from"commander";import E from"chalk";import be from"node:os";import F from"node:fs";import{loadConfig as p,saveConfig as ke,getConfigPath as Re,getShennianDir as Pe,resolveShennianPath as G,normalizeAutoUpgradePolicy as Ae}from"./config/index.js";import{CliRelayClient as Ee}from"./relay/client.js";import{collectKnownLogSecrets as ne,installCliLogRedaction as De,sanitizeCliLogValue as Te}from"./log-redaction.js";import{registerPairCommand as Ue,runSmartStart as Me}from"./commands/pair.js";import{registerLogoutCommand as _e}from"./commands/logout.js";import{clearDaemonPidIfOwner as te,createDaemonInstanceId as xe,clearDaemonLauncher as re,findRunningDaemonProcessIds as $e,isRemoteAccessDisabled as ae,prepareRemoteAccessPause as Ne,stopPreparedRemoteAccessService as Oe,isIsolatedDaemonE2eInstance as Ve,getDaemonStatus as ie,registerDaemonCommand as Le,writeDaemonPid as se,writeDaemonLauncher as ce}from"./commands/daemon.js";import{registerRuntimeCommand as je}from"./commands/runtime.js";import{registerAgentCommand as He}from"./commands/agent.js";import{registerManagerCommand as Be}from"./commands/manager.js";import{registerWorkerCommand as Fe}from"./commands/worker.js";import{handleUpgradeStart as Ge,registerUpgradeCommand as We}from"./commands/upgrade.js";import{registerToolsCommand as ze}from"./commands/tools.js";import{registerIntegrationCommand as Je}from"./commands/integration.js";import{registerRoomCommand as Ke}from"./commands/room.js";import{SessionManager as qe}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Ye}from"./session/handlers/project-fs.js";import{SERVERS as W,regionToUrl as Qe,urlToRegion as Ze}from"./region.js";import{clearUpgradeAttempt as Xe,getCurrentVersion as de,handleStartupCrashCheck as eo,readUpgradeAttempt as oo}from"./upgrade/engine.js";import{AutoUpgradeScheduler as no}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as to}from"./upgrade/updater-bootstrap.js";import{isDesktopDistribution as ro}from"./runtime-distribution.js";import{launchUpdater as ao,markUpdaterHealthy as io}from"./upgrade/updater-launcher.js";import{detectAgents as so}from"./agents/detect.js";import{augmentProcessPath as le}from"./env-path.js";import{acquireDaemonInstanceLock as co}from"./commands/daemon-instance-lock.js";le();const z=de();import{getCachedAgentInfos as N,resolveAgentInfos as lo}from"./agents/model-registry.js";import{initCliLogReporter as mo,reportLog as D,reportUpgradeAggregateMetric as po}from"./log-reporter.js";import{isNativeFusionEnabled as go}from"./native-fusion/config.js";import{NativeSessionFusionService as uo}from"./native-fusion/service.js";import{startDaemonLogRetention as fo}from"./daemon-log.js";import{startDaemonIpcServer as me}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as ho,resolveManagedRoomIpcPaths as vo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as Io}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as So}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as yo}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as wo}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as Co}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as bo}from"./room/session-registry.js";import{HttpRoomBindingClient as ko}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Ro}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Po}from"./room/host-session-observations.js";import{RoomMirrorSyncService as Ao}from"./room/mirror-sync-service.js";import{handleLiveRoomDelivery as Eo}from"./room/live-delivery.js";import{NativeSourceDeletionStore as Do}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as To,isCurrentRegistration as Uo}from"./room/registration-authority.js";import{RoomActivationService as Mo,resolveManagedAgentPolicy as _o}from"./room/activation-service.js";import{RoomActivationAudit as xo}from"./room/activation-audit.js";import{RoomAttachmentCache as $o}from"./room/attachment-cache.js";import{RoomAttachmentUploader as No}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Oo}from"./room/managed-context.js";import{createManagedRoomExecutor as Vo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as pe,SESSION_SYNC_ADVERTISED_CAPABILITIES as ge}from"@shennian/wire";import{getDaemonInstallationId as Lo}from"./session/store.js";import{synchronizeMachinePersonalAuthority as jo}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as ue,ensurePersonalDataEpoch as Ho}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as Bo,DeletionSyncTerminalError as Fo}from"./session/deletion-sync-state.js";const fe=Pe(),Go=G("daemon.pid"),Wo=G("daemon.instance.lock");function zo(){try{const a=F.readFileSync(Go,"utf-8").trim();if(a.startsWith("{")){const g=JSON.parse(a),c=Number(g.pid);return Number.isInteger(c)&&c>0?c:null}const s=parseInt(a,10);return Number.isInteger(s)&&s>0?s:null}catch{return null}}function Jo(a){return a.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function J(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
|
|
2
|
+
`),process.exitCode=1}function Ko(a){const s=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&s.push(`reason=${a.reason}`),a.error&&s.push(`error=${a.error}`),s.join(" ")}const i=new Ce;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(z),i.enablePositionalOptions(),Je(i),Ke(i),Se(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",be.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Me(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const s=G("env.json");try{const e=JSON.parse(F.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);le()}catch{}De(ne(process.env,p()));const g=fo();ae()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=xe(),y=co({lockPath:Wo,instanceId:c});y.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(y.ownerPid?` (PID ${y.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=zo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),y.release(),process.exit(0)}catch{}}catch{}const K=Ve(process.env,fe)?[]:$e(process.pid);K.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${K[0]}), skipping duplicate start`),y.release(),process.exit(0)),se(process.pid,c,{version:z}),ce(process.pid,void 0,c),process.on("exit",()=>{te(process.pid,c),re(c)}),process.on("exit",y.release);let O=null;try{ro()||(O=to())}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const T=await eo();if(T){O||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{ao({fromVersion:z,targetVersion:T.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:T.jobId},{readiness:O}),console.error(`[upgrade] New version crashed ${T.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Ho()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??W.cn.url,h=de(),l=await so({serverUrl:u,authToken:m.machineToken??m.accessToken}),w=l.map(e=>e.type),V=N(l),f=new bo,U=new Po,C=new Oo(f),I=new ko(u),ve=new $o({actionClient:I}),Ie=new No({actionClient:I});let S=null,r;const q=Vo({authority:C,actionClient:I,attachmentCache:ve,attachmentUploader:Ie,observations:U,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),scheduleExecutor:(e,n,o)=>oe({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let L=null,j=null,H=null;const b=await me({paths:ho(),handlers:{...Io({daemonVersion:h,protocolVersion:Co,getStatus:()=>ie(),getAgents:()=>l,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!L)throw new Error("upgrade_unavailable");return L(e)},prepareExternalUpgrade:e=>{if(!j)throw new Error("external_upgrade_unavailable");return j(e)},cancelExternalUpgrade:()=>{if(!H)throw new Error("external_upgrade_unavailable");return H()}}),...So({getStatus:()=>ie(),getSnapshot:()=>N(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await yo(!0,e);if(e){const t=l.filter(A=>A.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=N(l);return d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const A=n.detectedAgents.find(B=>B.type===t.type);return{...t,runtimePlatform:A?.runtimePlatform,runtimeIsWsl:A?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return wo(e,{ignoreNativeSession:(n,o)=>R?.ignoreNativeSource(n,o)})}})}});let k;try{k=await me({paths:vo(),handlers:{"managed-room.execute":e=>q(e),"schedule.personal":async e=>{const n=e,o=ye(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await oe({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let Y=!1;const M=()=>{Y||(Y=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",M),process.once("SIGTERM",M),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",M),process.removeListener("SIGTERM",M);const Q=`${Jo(u)}/relay/machine`,_=m.machineToken;if(!_)return;try{await jo({serverUrl:u,machineToken:_,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),k.close()]),process.exit(1)}m.machineId&&mo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${Q}... (v${h}) agents: ${w.join(",")}`),D({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:w}});let R=null,P=null,x=0,v=oo();const Z=Ae(m.autoUpgrade),d=new Ee({serverUrl:Q,machineToken:_,cliVersion:h,agentList:w,autoUpgradePolicy:Z,sessionSyncProtocolVersion:pe,sessionSyncCapabilities:ge,daemonInstallationId:Lo(),dataEpoch:ue,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),D({level:"info",wsEvent:"daemon.connected"}),V.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:V}}),lo(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(V)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),v&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),$.start(),P?.start();const e=++x,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&io(o.jobId,o.to),Xe(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),po({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),d.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===x&&d.getState()==="connected"&&R?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===x&&d.getState()==="connected"){if(o instanceof Fo)return;const t=o instanceof Bo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{x+=1,r?.revokeProjectFsHandles(),P?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Ko(e)}`),D({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),D({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){Eo(d,P,e);return}r.handleReq(e)}});R=go()?new uo(d):null,P=new Ao({registry:f,observations:U,actionClient:I,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Do().hasSession(e),onMessages:async(e,n,o,t)=>{await S?.processMessages(e,n,o,t)},onBindingPaused:e=>{C.revokeSession(e.registryKey),e.lifecycleState==="closed"?r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new no({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:pe,dataEpoch:ue,advertisedCapabilities:ge,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(d,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});$.setPolicy(Z),r=new qe(d,R,h,$,new Ro({registry:f,observations:U,bindingClient:I,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Ne(),isDisabled:()=>ae(),shutdownAfterResponse:()=>{setTimeout(()=>{Oe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),q,null,Ye(_),()=>N(l)),L=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await Ge(d,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:B=>r.prepareForUpgrade(B),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},j=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},H=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),S=new Mo({registry:f,observations:U,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await I.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!Uo(f,e)||!To(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o)=>{const t=p().machineToken;if(!t)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:t,activationCauseMessageId:n,allowedActions:o})},audit:new xo,resolveManagedAgentPolicy:e=>_o({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>we({serverUrl:u,machineToken:p().machineToken,req:e,client:d,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await I.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>S.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>S.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[]}):e.source==="manager_event"?S.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:S.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),F.mkdirSync(fe,{recursive:!0}),se(process.pid,c,{version:h}),ce(process.pid,void 0,c),d.connect(),process.stdin.resume();let X=!1;const ee=async()=>{if(!X){X=!0,console.log(E.gray(`
|
|
3
|
+
Disconnecting...`)),D({level:"info",wsEvent:"daemon.stop"}),clearInterval(g);try{await r.cleanup(),$.stop(),R?.stop(),P?.stop(),d.disconnect(),await Promise.allSettled([b.close(),k.close()]),te(process.pid,c),re(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{ee()}),process.on("SIGTERM",()=>{ee()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>J()),i.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>J()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>J());const he=i.command("config").description("View or modify configuration");he.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${Re()}`);const a=p(),s=Te(a,ne(process.env,a));if(a.serverUrl){const g=Ze(a.serverUrl);console.log(`Region: ${g} (${W[g].label})`)}console.log(JSON.stringify(s,null,2))}),he.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((a,s)=>{const g=p();if(a==="server"){s!=="cn"&&s!=="global"&&(console.error(E.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;g.serverUrl=Qe(c),ke(g),console.log(E.green(`\u2713 Server set to ${W[c].label}`)),console.log(E.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(E.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Ue(i),_e(i),Le(i),je(i),He(i),Be(i),Fe(i),We(i),ze(i),i.parse();
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import{isAvailableBuiltinAgentType as k,managedAgentExecutionMode as P}from"@shennian/wire";import{getBuiltinAgentVersion as S,resolveBuiltinCommand as B}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as K,resolveCustomRuntimeExecutableFingerprint as
|
|
2
|
-
${
|
|
3
|
-
`)}`:"";return{speaker:
|
|
1
|
+
import{isAvailableBuiltinAgentType as k,managedAgentExecutionMode as P}from"@shennian/wire";import{getBuiltinAgentVersion as S,resolveBuiltinCommand as B}from"../agents/command-spec.js";import{registerCustomRuntimeSecurity as K,resolveCustomRuntimeExecutableFingerprint as $}from"../agents/custom-managed-runtime.js";import{loadConfig as x}from"../config/index.js";import{resolveManagedAgentExecutableIdentity as W}from"../security/managed-agent-executable.js";import{compileManagedAgentPolicy as T}from"../security/managed-agent-policy.js";import{resolveManagedAgentRuntimeProtection as q}from"../security/managed-agent-runtime.js";import{selectRoomActivationTargets as D}from"./activation-policy.js";import{buildManagedAgentPermissionPrompt as E,composeManagedRoomPrompt as R}from"./managed-prompt-composer.js";import{isCurrentRegistration as f}from"./registration-authority.js";const N=4,F=6e4,O=6,V=5*6e4;class ce{deps;latestContexts=new Map;constructor(e){this.deps=e}async processMessages(e,r,t,a={roomName:"\u5F53\u524D\u7FA4\u804A"}){const d=[],g=[],u=new Map,m=new Set,c=[...r].sort((i,s)=>J(i.sequence,s.sequence));for(const i of t){const s=this.deps.registry.getByRegistryKey(i.registryKey);!s?.binding||!f(this.deps.registry,i)||s.binding.roomId!==e||s.binding.status!=="active"&&s.binding.status!=="paused"&&s.binding.activationQueue.length}for(const i of t){const s=this.deps.registry.getByRegistryKey(i.registryKey);if(!s?.binding||!f(this.deps.registry,i)||s.binding.roomId!==e||s.binding.activationQueue.length===0&&!s.binding.activeActivation||(m.add(s.registryKey),s.binding.status!=="active"||s.binding.activationMode==="paused")||s.binding.activationQueue.length===0)continue;const y=this.deps.observations.getBySource(s);if(!y?.workspacePath||y.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");this.latestContexts.set(s.registryKey,a);for(const o of s.binding.activationQueue)d.push({registration:s,input:{params:{sessionId:s.nianSessionId,text:o.deliveryText??"\u7FA4\u804A\u4E2D\u6709\u4E00\u6761\u5F85\u5904\u7406\u6D88\u606F\uFF0C\u8BF7\u7ED3\u5408\u5F53\u524D\u7FA4\u804A\u4E0A\u4E0B\u6587\u5904\u7406\u3002",agentType:l(s.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[s.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:y.workspacePath,agentSessionId:s.sourceSessionKey,queueMessageId:`room-activation-${s.binding.bindingId}-${o.causeMessageId}`,clientMessageId:`room-activation-${s.binding.bindingId}-${o.causeMessageId}`},context:{registryKey:s.registryKey,causeMessageId:o.causeMessageId,rootCauseMessageId:o.rootCauseMessageId,sequence:o.sequence,hopCount:o.hopCount}}})}for(const i of c){const s=new Set(D(i,t).map(y=>y.registration.registryKey));for(const y of t){const o=this.deps.registry.getByRegistryKey(y.registryKey);if(!o?.binding||!f(this.deps.registry,y)||o.binding.roomId!==e||o.binding.status!=="active"&&o.binding.status!=="paused"||a.agentsByIdentityId?.[o.binding.agentIdentityId]?.configurationPending||BigInt(i.sequence)<=BigInt(o.binding.lastActivatedSequence))continue;const I=u.get(o.registryKey)??[...o.binding.mentionContext??[]];if(u.set(o.registryKey,I),s.has(o.registryKey)){const p=(i.activationCause?.hopCount??0)+(i.author.principalType==="agent"?1:0),h=i.activationCause?.rootCauseMessageId??i.id;if(p>N)this.audit(o,i.id,h,p,"suppressed","max_hops");else{const M=this.deps.registry.claimActivation({registryKey:o.registryKey,causeMessageId:i.id,windowMs:F,maxActivations:O,pauseMs:V,now:this.deps.now?.()});if(!M.allowed){M.newlyPaused&&this.audit(o,i.id,h,p,"suppressed","rate_limited"),g.push({registryKey:o.registryKey,sequence:i.sequence});continue}const b=this.deps.observations.getBySource(o);if(!b?.workspacePath||b.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const v=w([i]),C=o.binding.activationMode==="mention_only"?Q([...I.map(_=>_.deliveryText),v]):v;this.latestContexts.set(o.registryKey,a),d.push({registration:o,input:{params:{sessionId:o.nianSessionId,text:C,agentType:l(o.runtimeAdapter),sessionMode:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.sessionMode??"standard",managerConfig:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig??null,managerDefaultWorkerAgentType:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:a.agentsByIdentityId?.[o.binding.agentIdentityId]?.managerConfig?.defaultWorkerModelId??null,workDir:b.workspacePath,agentSessionId:o.sourceSessionKey,queueMessageId:`room-activation-${o.binding.bindingId}-${i.id}`,clientMessageId:`room-activation-${o.binding.bindingId}-${i.id}`,roomMessageCreatedAt:i.createdAt},context:{registryKey:o.registryKey,causeMessageId:i.id,rootCauseMessageId:h,sequence:i.sequence,hopCount:p}}}),o.binding.activationMode==="mention_only"&&u.set(o.registryKey,[])}}else o.binding.activationMode==="mention_only"&&i.kind!=="system"&&i.author.membershipId!==o.binding.agentMembershipId&&(I.some(p=>p.sequence===i.sequence)||I.push({sequence:i.sequence,deliveryText:w([i])}));g.push({registryKey:o.registryKey,sequence:i.sequence})}}await this.deps.enqueueConversationBatch(d.map(i=>i.input));for(const i of m)this.deps.registry.retireLegacyActivationState(i,this.deps.now?.()??new Date);for(const[i,s]of u)this.deps.registry.getByRegistryKey(i)?.lifecycleState!=="closed"&&this.deps.registry.replaceMentionContext(i,s,this.deps.now?.()??new Date);for(const i of d)this.audit(i.registration,i.input.context.causeMessageId,i.input.context.rootCauseMessageId,i.input.context.hopCount,"queued","conversation_queue");for(const i of g)this.deps.registry.getByRegistryKey(i.registryKey)?.lifecycleState!=="closed"&&this.deps.registry.advanceCursor({registryKey:i.registryKey,cursor:"activated",sequence:i.sequence})}async prepareConversationTurn(e){const r=e.at(-1);if(!r)throw new Error("managed_room_queue_context_missing");const t=this.deps.registry.getByRegistryKey(r.registryKey);if(!t)throw new Error("managed_room_binding_unavailable");const a=await this.prepareManagedTurn(t,r.causeMessageId);return this.audit(t,r.causeMessageId,r.rootCauseMessageId,r.hopCount,"activated","conversation_queue_dispatch"),a}async prepareDirectConversationTurn(e,r={}){const t=r.roomOrigin?this.deps.registry.getByRegistryKey(r.roomOrigin.registryKey):this.deps.registry.getByNianSessionId(e);return t?.nianSessionId!==e||!t||r.roomOrigin&&(t.registrationEpoch!==r.roomOrigin.registrationEpoch||t.binding?.bindingId!==r.roomOrigin.bindingId||t.binding.roomId!==r.roomOrigin.roomId)?null:this.prepareManagedTurn(t,void 0,r)}async prepareManagedTurn(e,r,t){if(!f(this.deps.registry,e)||!e.binding||e.binding.status!=="active")throw new Error("managed_room_binding_unavailable");const a=this.deps.loadContext?await this.deps.loadContext(e):this.latestContexts.get(e.registryKey);if(!a)throw new Error("managed_room_prompt_context_unavailable");const d=a.agentsByIdentityId?.[e.binding.agentIdentityId];if(d?.configurationPending)throw new Error("managed_agent_configuration_pending");if(!d?.permissionPolicy)throw new Error("managed_agent_policy_missing");const g=d.sessionMode==="manager"?"manager":"standard",u=g==="manager"?{...d.managerConfig,defaultWorkerAgentType:d.managerConfig?.defaultWorkerAgentType??l(e.runtimeAdapter),defaultWorkerModelId:d.managerConfig?.defaultWorkerModelId??null}:null,m=this.deps.observations.getBySource(e);if(!m?.workspacePath||m.lifecycleState==="closed")throw new Error("managed_room_workspace_unavailable");const c=this.deps.resolveManagedAgentPolicy?await this.deps.resolveManagedAgentPolicy({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:m.workspacePath}):await U({registration:e,permissionPolicy:d.permissionPolicy,permissionConfirmation:d.permissionConfirmation,workDir:m.workspacePath});if(!f(this.deps.registry,e))throw new Error("managed_room_binding_unavailable");const i=H(a,e,c),s=this.deps.issueManagedRoomContext(e.registryKey,r,t?.allowedRoomActions);if(!/^mrc_[A-Za-z0-9_-]{43}$/.test(s))throw new Error("managed_room_context_unavailable");return{agentType:l(e.runtimeAdapter),workDir:c.adapterPolicy.workDir,agentSessionId:e.sourceSessionKey,systemPrompt:t?G({managerEvent:t.managerEvent===!0,roomSendAllowed:t.allowedRoomActions?.includes("send")===!0,agentSystemPrompt:a.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId],managedAgentPolicy:c,runtimeAdapter:l(e.runtimeAdapter)}):i.systemPrompt,sessionMode:g,managerConfig:u,managerDefaultWorkerAgentType:u?.defaultWorkerAgentType??null,managerDefaultWorkerModelId:u?.defaultWorkerModelId??null,roomContextRegistryKey:e.registryKey,managedRoomContextToken:s,managedAgentPolicy:c,managedRoomPromptSnapshot:i.snapshot}}audit(e,r,t,a,d,g){e.binding&&this.deps.audit?.record({roomId:e.binding.roomId,bindingId:e.binding.bindingId,causeMessageId:r,rootCauseMessageId:t,hopCount:a,outcome:d,reason:g,at:this.deps.now?.()})}}function w(n){const e=n.map(r=>{const t=r.text?.trim()||"\u53D1\u9001\u4E86\u4E00\u6761\u6D88\u606F",a=r.attachments.length>0?`
|
|
2
|
+
${r.attachments.map(L).join(`
|
|
3
|
+
`)}`:"";return{speaker:r.author.displayName,body:`${t}${a}`}});return A(e)}function L(n){return`\u9644\u4EF6\uFF1A${`${j(n.name)} \xB7 ${z(n.byteSize)}`}\uFF5C${n.mimeType}\uFF5CattachmentId=${n.attachmentId}`}function j(n){return String(n||"\u9644\u4EF6").replace(/[[\]\\]/g,"\\$&").replace(/[\r\n]+/g," ")}function z(n){const e=Number(n);if(!Number.isFinite(e)||e<0)return`${n} B`;if(e<1024)return`${e} B`;const r=["KB","MB","GB","TB"];let t=e,a=-1;do t/=1024,a+=1;while(t>=1024&&a<r.length-1);const d=t>=100?0:t>=10?1:2;return`${t.toFixed(d).replace(/\.0+$|(?<=\.[0-9])0+$/,"")} ${r[a]}`}function Q(n){return A(n.map(X))}function X(n){const e=n.match(/^\[群消息\][ \t]+<([^>\n]+)>\n([\s\S]*)$/);if(e)return{speaker:e[1],body:e[2]};const r=n.match(/^\[群消息\]\n<([^>\n]+)>\n([\s\S]*)$/);if(r)return{speaker:r[1],body:r[2]};const t=n.match(/^群消息\n([^\n]+)\n([\s\S]*)$/);if(t)return{speaker:t[1],body:t[2]};const a=n.match(/^([^\n]+)\n([\s\S]*)$/);return a?{speaker:a[1],body:a[2]}:{speaker:"\u7FA4\u6210\u5458",body:n.trim()}}function A(n){if(n.length===1){const[e]=n;return`[\u7FA4\u6D88\u606F] <${e.speaker}>
|
|
4
4
|
${e.body}`}return`[\u7FA4\u6D88\u606F]
|
|
5
5
|
${n.map(e=>`<${e.speaker}>
|
|
6
6
|
${e.body}`).join(`
|
|
7
7
|
|
|
8
|
-
`)}`}function G(n){const e=E({requestedPolicy:n.managedAgentPolicy.requestedPolicy,enforcement:n.managedAgentPolicy.enforcement,resolvedWorkDir:n.managedAgentPolicy.adapterPolicy.workDir}),
|
|
9
|
-
${
|
|
8
|
+
`)}`}function G(n){const e=E({requestedPolicy:n.managedAgentPolicy.requestedPolicy,enforcement:n.managedAgentPolicy.enforcement,resolvedWorkDir:n.managedAgentPolicy.adapterPolicy.workDir}),r=n.managerEvent&&n.roomSendAllowed?`\u8FD9\u662F\u7531\u5F53\u524D\u7FA4\u804A\u4EFB\u52A1\u521B\u5EFA\u7684 Worker \u89E6\u53D1\u7684\u79C1\u6709 Manager \u7EED\u8F6E\u3002\u5148\u4F7F\u7528 Manager \u5DE5\u5177\u8BFB\u53D6\u5E76\u6838\u9A8C Worker \u7ED3\u679C\u3002\u53EA\u628A\u9762\u5411\u7528\u6237\u7684\u6700\u7EC8\u8FDB\u5C55\u6216\u7ED3\u8BBA\u53D1\u9001\u5230\u8BE5\u4EFB\u52A1\u539F\u59CB\u7FA4\u804A\uFF0C\u4E0D\u53D1\u9001 Worker \u539F\u59CB\u6D3B\u52A8\u3001\u5DE5\u5177\u65E5\u5FD7\u3001\u5185\u90E8\u6807\u8BC6\u6216\u8C03\u5EA6\u7EC6\u8282\u3002\u9700\u8981\u53D1\u5E03\u65F6\u5FC5\u987B\u663E\u5F0F\u4F7F\u7528${n.runtimeAdapter==="codex"?" shennian_room_send \u5DE5\u5177":" shennian room send \u547D\u4EE4"}\uFF1B\u53EA\u5728\u5F53\u524D\u5185\u90E8\u5BF9\u8BDD\u4E2D\u751F\u6210\u6587\u5B57\u4E0D\u4F1A\u53D1\u9001\u5230\u7FA4\u804A\u3002`:n.managerEvent?"\u8FD9\u662F Worker \u89E6\u53D1\u7684\u79C1\u6709 Manager \u7EED\u8F6E\u3002\u53EF\u4EE5\u4F7F\u7528 Manager \u5DE5\u5177\u8BFB\u53D6\u5E76\u6838\u9A8C Worker \u7ED3\u679C\uFF0C\u4F46\u4E0D\u5F97\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u56DE\u590D\u4EFB\u4F55 Room \u5185\u5BB9\u3002":"\u8FD9\u662F Owner \u4E0E Manager \u7684\u79C1\u6709\u5BF9\u8BDD\u3002\u53EF\u4EE5\u4F7F\u7528 Manager \u5DE5\u5177\u7BA1\u7406 Worker\uFF0C\u4F46\u4E0D\u5F97\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u56DE\u590D\u4EFB\u4F55 Room \u5185\u5BB9\u3002",t=n.agentSystemPrompt?.trim();return[`[\u795E\u5FF5\u53D7\u7BA1\u79C1\u6709\u7EED\u8F6E\u89C4\u5219]
|
|
9
|
+
${r}
|
|
10
10
|
\u4E0D\u5F97\u731C\u6D4B\u6216\u5C55\u793A Room\u3001Agent\u3001Session\u3001Binding\u3001Membership\u3001Machine \u6807\u8BC6\u6216\u4EFB\u4F55\u51ED\u636E\u3002`,e,t?`[\u667A\u80FD\u4F53\u79C1\u6709\u7CFB\u7EDF\u63D0\u793A\u8BCD]
|
|
11
11
|
${t}`:""].filter(Boolean).join(`
|
|
12
12
|
|
|
13
|
-
`)}function H(n,e,
|
|
13
|
+
`)}function H(n,e,r){const t=e.binding?n.agentsByIdentityId?.[e.binding.agentIdentityId]:void 0;if(!e.binding||!t)throw new Error("managed_room_prompt_incomplete");return R({runtimeAdapter:l(e.runtimeAdapter),agent:{displayName:t.displayName,description:t.description,systemPrompt:n.agentSystemPromptsByIdentityId?.[e.binding.agentIdentityId],configurationVersion:t.configurationVersion},room:{name:n.roomName,activationMode:e.binding.activationMode,members:n.members},runtimePermissions:{requestedPolicy:r.requestedPolicy,enforcement:r.enforcement,resolvedWorkDir:r.adapterPolicy.workDir}})}async function U(n){const e=l(n.registration.runtimeAdapter),r=k(e)?B(e):null,t=e.startsWith("custom:")?x().customAgents?.[e.slice(7)]:void 0;t&&K(e,t);const a=n.runtime?.version??(r?S(e,r):t?.caps.version),d=n.runtime?{platform:n.runtime.runtimePlatform??process.platform,isWsl:n.runtime.runtimeIsWsl??!1,executableFingerprint:n.runtime.executableFingerprint}:r?W(r):{platform:process.platform,isWsl:process.platform==="linux"&&!!(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP),executableFingerprint:t?$(t):void 0},g={agentType:e,runtimeVersion:a,platform:d.platform,isWsl:d.isWsl,executableFingerprint:d.executableFingerprint,requireExecutableFingerprint:!0},u=q(g),m=Z({confirmation:n.permissionConfirmation,policy:n.permissionPolicy,runtimeAdapter:e,protection:u}),c=await T({...g,policy:n.permissionPolicy,workDir:n.workDir,hostFullAccessConfirmed:m&&n.permissionConfirmation?.hostFullAccess,limitedProtectionConfirmed:m&&n.permissionConfirmation?.limitedProtection});if(!c.ok){const i=c.unsupportedCapabilities?.length?`:${c.unsupportedCapabilities.join(",")}`:"";throw new Error(`${c.errorCode}${i}`)}return c.value}function Z(n){const{confirmation:e,policy:r,runtimeAdapter:t}=n,a=P(r);return!!(e&&e.policyVersion===r.version&&e.runtimeAdapter===t&&(e.executionMode??"restricted")===a&&e.hostFullAccess===(a==="native-full"||r.fileAccess==="host-full"))}function l(n){return n==="claude_code"?"claude":n}function J(n,e){const r=BigInt(n),t=BigInt(e);return r<t?-1:r>t?1:0}export{ce as RoomActivationService,Z as managedAgentPermissionConfirmationMatches,U as resolveManagedAgentPolicy};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReqFrame, RoomMessageView } from '@shennian/wire';
|
|
2
|
+
import type { CliRelayClient } from '../relay/client.js';
|
|
3
|
+
import type { RoomMirrorSyncService } from './mirror-sync-service.js';
|
|
4
|
+
export declare function handleLiveRoomDelivery(client: Pick<CliRelayClient, 'sendRes'>, mirror: RoomMirrorSyncService | null, req: ReqFrame): Promise<void>;
|
|
5
|
+
export declare function parseRoomMessage(value: unknown): RoomMessageView;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function n(r,e,o){try{if(!e)throw new Error("room_delivery_unavailable");const t=s(o.params.message),a=await e.deliverLive(t);r.sendRes({type:"res",id:o.id,ok:!0,payload:a})}catch(t){r.sendRes({type:"res",id:o.id,ok:!1,error:t instanceof Error?t.message:"invalid_room_delivery"})}}function s(r){(!r||typeof r!="object"||Array.isArray(r))&&i();const e=r;return(typeof e.id!="string"||!e.id||e.id.length>191||typeof e.roomId!="string"||!e.roomId||e.roomId.length>191||typeof e.sequence!="string"||!/^\d+$/.test(e.sequence)||e.kind!=="text"&&e.kind!=="attachment"&&e.kind!=="mixed"&&e.kind!=="system"||!e.author||typeof e.author!="object"||typeof e.author.membershipId!="string"||!e.author.membershipId||e.author.principalType!=="human"&&e.author.principalType!=="agent"||typeof e.author.ownerUserId!="string"||!e.author.ownerUserId||typeof e.author.displayName!="string"||!e.author.displayName||typeof e.createdAt!="string"||!Number.isFinite(Date.parse(e.createdAt))||!Array.isArray(e.attachments))&&i(),e}function i(){throw new Error("invalid_room_delivery")}export{n as handleLiveRoomDelivery,s as parseRoomMessage};
|
|
@@ -29,6 +29,17 @@ export declare class RoomMirrorSyncService {
|
|
|
29
29
|
});
|
|
30
30
|
start(): void;
|
|
31
31
|
stop(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Fast path for an authoritative Room message delivered over the machine
|
|
34
|
+
* Relay. The message body is already present, so this must not perform the
|
|
35
|
+
* normal status/read HTTP round-trip. The periodic cursor sync remains the
|
|
36
|
+
* compatibility and reconnect repair path for older servers and missed
|
|
37
|
+
* frames.
|
|
38
|
+
*/
|
|
39
|
+
deliverLive(message: RoomMessageView): Promise<{
|
|
40
|
+
accepted: boolean;
|
|
41
|
+
registrationCount: number;
|
|
42
|
+
}>;
|
|
32
43
|
runOnce(): Promise<void>;
|
|
33
44
|
private syncRoom;
|
|
34
45
|
private pullPages;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{RoomActionHttpError as h}from"./room-action-client.js";import p from"node:crypto";import{isCurrentRegistration as
|
|
1
|
+
import{RoomActionHttpError as h}from"./room-action-client.js";import p from"node:crypto";import{isCurrentRegistration as y,matchesRoomOwner as f}from"./registration-authority.js";class M{deps;timer=null;running=!1;started=!1;constructor(i){this.deps=i}start(){this.started||(this.started=!0,this.arm(this.deps.initialDelayMs??0))}stop(){this.started=!1,this.timer&&(this.deps.clearTimeout??clearTimeout)(this.timer),this.timer=null}async deliverLive(i){const n=this.deps.registry.list().filter(s=>s.binding?.roomId!==i.roomId||s.binding.status!=="active"||s.binding.activationMode==="paused"||!y(this.deps.registry,s)?!1:BigInt(i.sequence)>BigInt(s.binding.lastDeliveredSequence));if(n.length===0)return{accepted:!1,registrationCount:0};this.deps.onMessages&&await this.deps.onMessages(i.roomId,[i],n,{roomName:"\u5F53\u524D\u7FA4\u804A"});for(const s of n){const o=this.deps.registry.getByRegistryKey(s.registryKey);o?.binding?.status==="active"&&o.binding.activationMode!=="paused"&&BigInt(i.sequence)>BigInt(o.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:o.registryKey,cursor:"delivered",sequence:i.sequence})}return{accepted:!0,registrationCount:n.length}}async runOnce(){if(this.running)return;const i=this.deps.getMachineCredentials().machineToken?.trim();if(i){this.running=!0;try{for(const s of this.deps.registry.list())s.lifecycleState!=="closed"&&this.deps.isSessionDeleted?.(s.nianSessionId)&&this.retire(s);const n=I(this.deps.registry.list());for(const[s,o]of n)await this.syncRoom(s,o,i)}finally{this.running=!1}}}async syncRoom(i,n,s){const o=n.filter(e=>e.binding&&(e.binding.status==="active"||e.binding.status==="paused"));if(o.length===0)return;let u=0,c=null,r=null;for(const e of o){if(!y(this.deps.registry,e))continue;const a=e.binding,g={machineToken:s,roomId:i,bindingId:a.bindingId};try{const t=await this.deps.actionClient.status(g);if(!y(this.deps.registry,e))continue;if(!f(e,t)||this.deps.isSessionDeleted?.(e.nianSessionId)){this.retire(e);continue}t.activationCapability==="managed"&&t.managedWorkspacePath&&!this.deps.observations?.getBySource(e)&&this.deps.observations?.observe({machineId:e.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,hostContextHash:l(e.hostProcessIdentity),workspaceHash:l(t.managedWorkspacePath),workspacePath:t.managedWorkspacePath,lifecycleState:"active",eventName:"ManagedSync"});const m=this.deps.registry.setBinding({registryKey:e.registryKey,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:t.status,activationMode:t.activationMode});if((t.status==="paused"||t.activationMode==="paused")&&this.deps.onBindingPaused?.(m),t.status==="detached"){u+=1;continue}if(t.status==="pending_approval")continue;c??=g,r??={roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:{},agentSystemPromptsByIdentityId:{}},t.agent&&(r.agentsByIdentityId[t.agentIdentityId]=t.agent,r.agentSystemPromptsByIdentityId[t.agentIdentityId]=t.agent.systemPrompt??"")}catch(t){if(b(t)){this.deps.registry.detachBinding(e.registryKey),u+=1;continue}return}}if(c){const e=n.filter(a=>y(this.deps.registry,a)).map(a=>this.deps.registry.getByRegistryKey(a.registryKey)??a);await this.pullPages(c,i,e,r);return}}async pullPages(i,n,s,o){if(s.length===0)return;const u=this.deps.maxPagesPerRun??100;let c=s.map(r=>r.binding?.lastDeliveredSequence??"0").reduce((r,e)=>BigInt(r)<BigInt(e)?r:e);for(let r=0;r<u;r+=1){const e=await this.deps.actionClient.read({...i,afterSequence:c,limit:100}),a=s.filter(g=>y(this.deps.registry,g));if(a.length===0)return;this.deps.onMessages&&await this.deps.onMessages(n,e.messages,a,o??{roomName:e.roomName});for(const g of a){const t=this.deps.registry.getByRegistryKey(g.registryKey);t?.binding&&(t.binding.status==="active"||t.binding.status==="paused")&&BigInt(e.nextSequence)>BigInt(t.binding.lastDeliveredSequence)&&this.deps.registry.advanceCursor({registryKey:g.registryKey,cursor:"delivered",sequence:e.nextSequence})}if(!e.hasMore)return;if(e.nextSequence===c)throw new h("room_sync_no_progress","Room mirror synchronization made no progress.");c=e.nextSequence}}retire(i){const n=this.deps.registry.updateLifecycle(i.registryKey,"closed");this.deps.onBindingPaused?.(n)}arm(i){if(!this.started||this.timer)return;const n=this.deps.setTimeout??setTimeout;this.timer=n(()=>{this.timer=null,this.runOnce().catch(()=>{console.warn("[room.sync] delivery failed; retrying on next poll")}).finally(()=>{this.started&&this.arm(this.deps.intervalMs??5e3)})},i),this.timer.unref?.()}}function l(d){return p.createHash("sha256").update(d,"utf8").digest("hex")}function I(d){const i=new Map;for(const n of d){if(!n.binding||n.lifecycleState==="closed")continue;const s=i.get(n.binding.roomId)??[];s.push(n),i.set(n.binding.roomId,s)}return i}function b(d){return d instanceof h&&(d.code==="room_binding_unavailable"||d.code==="room_action_http_403")}export{M as RoomMirrorSyncService};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildUserMessagePayload as ie}from"@shennian/wire";import{createHash as Me,randomUUID as Ae}from"node:crypto";import de from"node:os";import{reportLog as b}from"../../log-reporter.js";import{getPersonalSyncIdentity as Pe}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ee}from"../../room/managed-prompt-composer.js";import{parsePersonalAttachmentReferences as De}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as be,markAttachmentReferences as Te}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as _}from"../index-publisher.js";import{isTrustedManagerEvent as xe}from"../manager-event-authority.js";import{mergeProjectedSessions as Ce}from"../projection.js";import{appendMessage as le,getDaemonInstallationId as ce,getMessageDeliveryStatus as $e,upsertMessage as S}from"../store.js";import{configureAdapterForSession as ue,maybeResolveClaudeImportedWorkDir as We,replaceManagedRoomContextToken as ge,revokeManagedRoomContextToken as me}from"./chat-adapter-config.js";import{resolveNativeSessionVisibility as qe,sendSessionUpdateEvent as je,validateManagedRoomExecutionContext as Fe}from"./chat-context.js";import{getNativeSourceAgentType as Be}from"./chat-events.js";import{createActiveSession as Je,disposeSession as C,prepareAdapterTurn as Oe,rememberProcessedReqId as ze,sendAdapterTurn as pe}from"./chat-session-lifecycle.js";import{sendSessionUpdateEvent as mn,validateManagedRoomExecutionContext as pn}from"./chat-context.js";function fe(e,o){return`Agent send failed: ${o instanceof Error?o.message:String(o)}`}function Ue(e,o){const t=e.sessions.get(o),u=t?.currentRunId;if(!t||!u)return;const n=t.nextEventSeq;e.runTextAcc.delete(`${o}:${u}`),me(e,t),t.pendingTextEvent=null,t.currentRunId=null,t.currentRunPhase=null,t.nextEventSeq=0,t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:o,runId:u,seq:n},seq:n,id:`agent-evt-${u}-${n}`}),t.journalRunId&&(e.runJournal?.remove(o,t.journalRunId),e.recoveryBarrier?.end(o),t.journalRunId=null),e.chatQueue?.noteTerminal(o)}async function ln(e,o,t){const u=t?o.params.managedRoomContextToken??null:null;let n=!1;try{await He(e,o,t,()=>{n=!0})}finally{u&&!n&&e.revokeManagedRoomContextToken?.(u)}}async function He(e,o,t,u){if(e.processedReqIds.has(o.id)){e.client.sendRes({type:"res",id:o.id,ok:!0});return}const{sessionId:n,text:Q,agentType:A,sessionMode:ye,managerConfig:L,workDir:ve,agentSessionId:m,modelId:h,systemPrompt:Z,managerDefaultWorkerAgentType:$,managerDefaultWorkerModelId:W,reasoningEffort:q,clientMessageId:he,sessionListProjection:Ie,waitForDispatch:G,responseId:Re,managedRoomContextToken:I,suppressExternalOwnerEcho:_e,queuedBatchItems:j,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:F,managerWorkerNativeFull:B,managedRecoverySource:P}=o.params,l=Re||o.id,X=o.params;if((X.managerEventId||X.managerEventRoomOrigin||g||F||B||P)&&!xe(o))throw new Error("manager_event_source_untrusted");Ce(Ie);const T=null,J="";if(t?.source==="room_activation"&&(Fe(t,Z,I),A==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(t?.source==="manager_event"){if(!t.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(!I||!/^mrc_[A-Za-z0-9_-]{43}$/.test(I))throw new Error("managed_room_context_unavailable")}if(ze(e,o.id),!n||!Q){e.processedReqIds.delete(o.id),e.client.sendRes({type:"res",id:l,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(o.id),e.client.sendRes({type:"res",id:l,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(n)&&await e.recoveryBarrier.wait(n);const d=A,R=d==="manager"||ye==="manager"?"manager":"standard";R==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(n,$??null,W??null);const E=(d==="claude"||d==="codex"||d==="workbuddy"||d==="pi-coding-agent"||d.startsWith("acp:"))&&typeof q=="string"&&q.trim()?q.trim():void 0,y=e.resolvePath(We(d,ve||de.homedir(),m)||de.homedir()),f=t?.managedAgentPolicy??(g?e.managerRuntime?.getManagedAgentPolicyForWorker(g,y)??null:null);if(g&&!B&&!f)throw new Error("managed_worker_permission_context_unavailable");const O=qe(n,d,R,!!(f||g||t)),z=g&&f?Ee({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement,resolvedWorkDir:f.adapterPolicy.workDir}):Z??null,Se=y,x=De(o.params.attachments);x?.length&&be(y,x);const p={text:Q,attachments:x,localized:!1};let r=e.sessions.get(n);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==d||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==R||r.workDir!==y||(r.nativeSessionVisibility??"user")!==O||JSON.stringify(r.externalChannel??null)!==JSON.stringify(T??null)){e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(o.id)}r=void 0}else if(m&&r.agentSessionId!==m)try{await r.adapter.resume(m),r.agentSessionId=m}catch{e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(o.id)}r=void 0}}if(!r)try{r=await Je(e,n,d,R,y,m,T,J,f,O)}catch(a){const i=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${A}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${o.id} sessionId=${n} agentType=${A} workDir=${y} agentSessionId=${m??""}: ${i}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:i,runId:"",seq:0}}),e.processedReqIds.delete(o.id),e.client.sendRes({type:"res",id:l,ok:!1,error:i});return}const c={id:he??`user-${o.id}`,sessionId:n,role:"user",ts:Date.now(),payload:ie(p.text,p.attachments)},Y=Array.isArray(j)&&j.length>0?j.map(a=>({id:a.clientMessageId??a.id,sessionId:n,role:P==="manager_event"||o.params.scheduledDelivery===!0?"agent":"user",ts:P==="manager_event"?Date.parse(a.createdAt):c.ts,payload:ie(a.text,a.attachments)})):null,k=Y??[c],U=$e(n,c.id),ee=U.state==="queued";if(U.state!=="not_found"&&!ee){e.client.sendRes({type:"res",id:l,ok:!0,payload:U});return}if(G){(t?.source==="room_activation"||t?.source==="manager_event")&&ue(r.adapter,n,d,T,J,I,t.managedAgentPolicy,e.managedRoomExecutor);try{await Oe(r.adapter,p.text,h,E,p.attachments,z,t?.source==="room_activation"?{source:"room_activation"}:t?.source==="manager_event"?{source:"managed_private"}:void 0)}catch(a){e.processedReqIds.delete(o.id),e.sessions.delete(n),await C(e,r),e.client.sendRes({type:"res",id:l,ok:!1,error:a instanceof Error?a.message:String(a)});return}}b({level:"info",sessionId:n,wsEvent:"chat.send.start",metadata:{reqId:o.id,agentType:d,sessionMode:R,managerConfig:L??null,modelId:h,managerDefaultWorkerAgentType:$??null,managerDefaultWorkerModelId:W??null,reasoningEffort:E,...t?.source==="room_activation"?{managedRoomPromptSnapshot:t.managedRoomPromptSnapshot}:{}}});const w=(a,i)=>({...Pe(ce()),sessionId:n,clientMessageId:c.id,messageId:c.id,deliveryState:a,bodyRevision:i,committedAt:new Date(c.ts).toISOString()}),H=a=>{e.nativeFusion?.registerManagedSend({sessionId:n,agentType:d,sourceAgentType:Be(d,h),canonicalMessageId:c.id,sourceSessionKey:r.agentSessionId??m??null,text:p.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!_e?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??c.id}`:null})},ne=()=>{r.currentRunId||(r.currentRunId=`dispatch:${o.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0)},ae=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${Ae()}`;e.runJournal.begin({daemonInstallationId:ce(),sessionId:n,runId:a,clientMessageId:c.id,agentType:d,sessionMode:R,workDir:y,agentSessionId:r.agentSessionId??m??null,modelId:h??null,reasoningEffort:E??null,nativeSessionVisibility:O,providerRunId:null,source:P==="manager_event"?"manager_event":t?.source==="room_activation"?P==="managed_direct"?"managed_direct":"managed_room":g?"manager_worker":"personal",parentSessionId:g??null,managerWorkerNativeFull:B===!0,roomContextRegistryKey:typeof o.params.roomContextRegistryKey=="string"?o.params.roomContextRegistryKey:null,activationCauseMessageId:typeof o.params.activationCauseMessageId=="string"?o.params.activationCauseMessageId:null,rootCauseMessageId:typeof o.params.rootCauseMessageId=="string"?o.params.rootCauseMessageId:null,activationSequence:typeof o.params.activationSequence=="string"?o.params.activationSequence:null,activationHopCount:typeof o.params.activationHopCount=="number"?o.params.activationHopCount:null,managedPolicyVersion:t?.source==="room_activation"?t.managedRoomPromptSnapshot.managedRoomPolicyVersion:t?.source==="manager_event"?t.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(t?.source==="room_activation"?t.managedRoomPromptSnapshot.runtimePermissionPolicyHash:t?.source==="manager_event"?t.managedRuntimePolicyHash:null)??(f?Me("sha256").update(JSON.stringify({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement})).digest("hex"):null)}),r.journalRunId=a},oe=()=>{if(H(),ee){let a=0;for(const[i,s]of k.entries()){const v=S(n,s,"queued");i===0&&(a=v)}return w("queued",a)}try{je(e,{sessionId:n,agentType:d,sessionMode:R,managerConfig:L??null,workDir:Se,agentSessionId:r.agentSessionId??m??null,modelId:h,managerDefaultWorkerAgentType:$??null,managerDefaultWorkerModelId:W??null});let a=0;for(const[i,s]of k.entries()){const v=Y?S(n,s,"committed"):le(n,s,"committed");i===0&&(a=v)}return Te(y,x??[]),_(e.client),w("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(c.id),a}},te=a=>{r.journalRunId&&e.runJournal?.update(n,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??c.id}`:null})},re=a=>{r.currentRunId||(r.nextEventSeq=0),te(a),H(a);const i=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let s=0;for(const[v,D]of k.entries()){const M=S(n,D,i);v===0&&(s=M)}return _(e.client),w(i,s)},N=async(a,i)=>{e.nativeFusion?.cancelManagedSend?.(c.id);const s=fe(d,a);if(console.error(`[chat.send] send failed reqId=${o.id} sessionId=${n} agentType=${A} workDir=${y} agentSessionId=${r.agentSessionId??m??""}: ${s}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){i&&e.client.sendRes({type:"res",id:l,ok:!0,payload:w("queued",0)});return}e.sessions.delete(n),r.journalRunId&&(e.runJournal?.update(n,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.journalRunId=null);try{await C(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:s,runId:"",seq:0}}),!i){const D={id:`agent-error-${o.id}-${Date.now()}`,sessionId:n,role:"agent",ts:Date.now(),payload:s};le(n,D),_(e.client)}i&&(e.processedReqIds.delete(o.id),e.client.sendRes({type:"res",id:l,ok:!1,error:s}))};if(G){try{oe()}catch(s){e.processedReqIds.delete(o.id),e.client.sendRes({type:"res",id:l,ok:!1,error:`Durable message commit failed: ${s instanceof Error?s.message:String(s)}`});return}ae(),ne();let a;try{(t?.source==="room_activation"||t?.source==="manager_event")&&(ge(e,r,I),u());const s=p.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,F),a=await pe(r.adapter,p.text,h,E,s,z,t?.source==="room_activation"?{source:"room_activation"}:t?.source==="manager_event"?{source:"managed_private"}:void 0),b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:o.id}})}catch(s){if(g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),r.adapter.hasPendingAuthenticationRetry?.()){let M=0;for(const[V,K]of k.entries()){const we=S(n,K,"queued");V===0&&(M=we)}_(e.client),await N(s,!1),e.client.sendRes({type:"res",id:l,ok:!0,payload:w("queued",M)});return}let v=0;for(const[M,V]of k.entries()){const K=S(n,V,"failed");M===0&&(v=K)}_(e.client);const D=fe(d,s);await N(s,!1),e.client.sendRes({type:"res",id:l,ok:!0,payload:{...w("failed",v),failureMessage:D}});return}const i=re(a??void 0);e.client.sendRes({type:"res",id:l,ok:!0,payload:{...i,...p.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:o.id,ok:!0}});return}let se;try{se=oe()}catch(a){e.processedReqIds.delete(o.id),e.client.sendRes({type:"res",id:l,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}ae(),ne(),(t?.source==="room_activation"||t?.source==="manager_event")&&(ue(r.adapter,n,d,T,J,I,t.managedAgentPolicy,e.managedRoomExecutor),ge(e,r,I),u()),re(),e.client.sendRes({type:"res",id:l,ok:!0,payload:{...se,...p.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:o.id,ok:!0}});const ke=p.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,F),pe(r.adapter,p.text,h,E,ke,z,t?.source==="room_activation"?{source:"room_activation"}:t?.source==="manager_event"?{source:"managed_private"}:void 0).then(a=>{if(a?.deliveryMode==="external_owner_queue"){te(a),H(a);for(const i of k)S(n,i,"queued");_(e.client)}b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:o.id}})}).catch(a=>{g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),S(n,c,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),_(e.client),N(a,!1)})}async function cn(e,o){const{sessionId:t,discardPending:u}=o.params,n=e.sessions.get(t);if(n){n.journalRunId&&e.runJournal?.markAborted(t,n.journalRunId),me(e,n);try{await n.adapter.stop()}catch{e.client.sendRes({type:"res",id:o.id,ok:!1,error:"agent_stop_failed"});return}Ue(e,t),e.activityPublisher?.publish(t,null)}u===!0&&e.chatQueue?.purgeSession(t),e.client.sendRes({type:"res",id:o.id,ok:!0})}export{cn as handleChatAbort,ln as handleChatSend,mn as sendSessionUpdateEvent,pn as validateManagedRoomExecutionContext};
|
|
1
|
+
import{buildUserMessagePayload as ie}from"@shennian/wire";import{createHash as Me,randomUUID as Ae}from"node:crypto";import de from"node:os";import{reportLog as b}from"../../log-reporter.js";import{getPersonalSyncIdentity as Pe}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ee}from"../../room/managed-prompt-composer.js";import{parsePersonalAttachmentReferences as De}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as be,markAttachmentReferences as Te}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as _}from"../index-publisher.js";import{isTrustedManagerEvent as xe}from"../manager-event-authority.js";import{mergeProjectedSessions as Ce}from"../projection.js";import{appendMessage as le,getDaemonInstallationId as ce,getMessageDeliveryStatus as $e,upsertMessage as S}from"../store.js";import{configureAdapterForSession as ue,maybeResolveClaudeImportedWorkDir as We,replaceManagedRoomContextToken as ge,revokeManagedRoomContextToken as me}from"./chat-adapter-config.js";import{resolveNativeSessionVisibility as qe,sendSessionUpdateEvent as je,validateManagedRoomExecutionContext as Fe}from"./chat-context.js";import{getNativeSourceAgentType as Be}from"./chat-events.js";import{createActiveSession as Je,disposeSession as C,prepareAdapterTurn as Oe,rememberProcessedReqId as ze,sendAdapterTurn as pe}from"./chat-session-lifecycle.js";import{sendSessionUpdateEvent as mn,validateManagedRoomExecutionContext as pn}from"./chat-context.js";function fe(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function Ue(e,t){const o=e.sessions.get(t),u=o?.currentRunId;if(!o||!u)return;const n=o.nextEventSeq;e.runTextAcc.delete(`${t}:${u}`),me(e,o),o.pendingTextEvent=null,o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,o.heartbeatTimer&&(clearInterval(o.heartbeatTimer),o.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:u,seq:n},seq:n,id:`agent-evt-${u}-${n}`}),o.journalRunId&&(e.runJournal?.remove(t,o.journalRunId),e.recoveryBarrier?.end(t),o.journalRunId=null),e.chatQueue?.noteTerminal(t)}async function ln(e,t,o){const u=o?t.params.managedRoomContextToken??null:null;let n=!1;try{await He(e,t,o,()=>{n=!0})}finally{u&&!n&&e.revokeManagedRoomContextToken?.(u)}}async function He(e,t,o,u){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:n,text:Q,agentType:A,sessionMode:ye,managerConfig:L,workDir:ve,agentSessionId:m,modelId:h,systemPrompt:Z,managerDefaultWorkerAgentType:$,managerDefaultWorkerModelId:W,reasoningEffort:q,clientMessageId:he,sessionListProjection:Ie,waitForDispatch:G,responseId:Re,managedRoomContextToken:I,suppressExternalOwnerEcho:_e,queuedBatchItems:j,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:F,managerWorkerNativeFull:B,managedRecoverySource:P}=t.params,l=Re||t.id,X=t.params;if((X.managerEventId||X.managerEventRoomOrigin||g||F||B||P)&&!xe(t))throw new Error("manager_event_source_untrusted");Ce(Ie);const T=null,J="";if(o?.source==="room_activation"&&(Fe(o,Z,I),A==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(o?.source==="manager_event"){if(!o.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(!I||!/^mrc_[A-Za-z0-9_-]{43}$/.test(I))throw new Error("managed_room_context_unavailable")}if(ze(e,t.id),!n||!Q){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(n)&&await e.recoveryBarrier.wait(n);const d=A,R=d==="manager"||ye==="manager"?"manager":"standard";R==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(n,$??null,W??null);const E=(d==="claude"||d==="codex"||d==="workbuddy"||d==="pi-coding-agent"||d.startsWith("acp:"))&&typeof q=="string"&&q.trim()?q.trim():void 0,y=e.resolvePath(We(d,ve||de.homedir(),m)||de.homedir()),f=o?.managedAgentPolicy??(g?e.managerRuntime?.getManagedAgentPolicyForWorker(g,y)??null:null);if(g&&!B&&!f)throw new Error("managed_worker_permission_context_unavailable");const O=qe(n,d,R,!!(f||g||o)),z=g&&f?Ee({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement,resolvedWorkDir:f.adapterPolicy.workDir}):Z??null,Se=y,x=De(t.params.attachments);x?.length&&be(y,x);const p={text:Q,attachments:x,localized:!1};let r=e.sessions.get(n);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==d||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==R||r.workDir!==y||(r.nativeSessionVisibility??"user")!==O||JSON.stringify(r.externalChannel??null)!==JSON.stringify(T??null)){e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(t.id)}r=void 0}else if(m&&r.agentSessionId!==m)try{await r.adapter.resume(m),r.agentSessionId=m}catch{e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(t.id)}r=void 0}}if(!r)try{r=await Je(e,n,d,R,y,m,T,J,f,O)}catch(a){const i=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${A}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${n} agentType=${A} workDir=${y} agentSessionId=${m??""}: ${i}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:i,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:i});return}const c={id:he??`user-${t.id}`,sessionId:n,role:"user",ts:Date.now(),payload:ie(p.text,p.attachments)},Y=Array.isArray(j)&&j.length>0?j.map(a=>({id:a.clientMessageId??a.id,sessionId:n,role:P==="manager_event"||t.params.scheduledDelivery===!0?"agent":"user",ts:P==="manager_event"||o?.source==="room_activation"?Date.parse(a.createdAt):c.ts,payload:ie(a.text,a.attachments)})):null,k=Y??[c],U=$e(n,c.id),ee=U.state==="queued";if(U.state!=="not_found"&&!ee){e.client.sendRes({type:"res",id:l,ok:!0,payload:U});return}if(G){(o?.source==="room_activation"||o?.source==="manager_event")&&ue(r.adapter,n,d,T,J,I,o.managedAgentPolicy,e.managedRoomExecutor);try{await Oe(r.adapter,p.text,h,E,p.attachments,z,o?.source==="room_activation"?{source:"room_activation"}:o?.source==="manager_event"?{source:"managed_private"}:void 0)}catch(a){e.processedReqIds.delete(t.id),e.sessions.delete(n),await C(e,r),e.client.sendRes({type:"res",id:l,ok:!1,error:a instanceof Error?a.message:String(a)});return}}b({level:"info",sessionId:n,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:d,sessionMode:R,managerConfig:L??null,modelId:h,managerDefaultWorkerAgentType:$??null,managerDefaultWorkerModelId:W??null,reasoningEffort:E,...o?.source==="room_activation"?{managedRoomPromptSnapshot:o.managedRoomPromptSnapshot}:{}}});const w=(a,i)=>({...Pe(ce()),sessionId:n,clientMessageId:c.id,messageId:c.id,deliveryState:a,bodyRevision:i,committedAt:new Date(c.ts).toISOString()}),H=a=>{e.nativeFusion?.registerManagedSend({sessionId:n,agentType:d,sourceAgentType:Be(d,h),canonicalMessageId:c.id,sourceSessionKey:r.agentSessionId??m??null,text:p.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!_e?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??c.id}`:null})},ne=()=>{r.currentRunId||(r.currentRunId=`dispatch:${t.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0)},ae=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${Ae()}`;e.runJournal.begin({daemonInstallationId:ce(),sessionId:n,runId:a,clientMessageId:c.id,agentType:d,sessionMode:R,workDir:y,agentSessionId:r.agentSessionId??m??null,modelId:h??null,reasoningEffort:E??null,nativeSessionVisibility:O,providerRunId:null,source:P==="manager_event"?"manager_event":o?.source==="room_activation"?P==="managed_direct"?"managed_direct":"managed_room":g?"manager_worker":"personal",parentSessionId:g??null,managerWorkerNativeFull:B===!0,roomContextRegistryKey:typeof t.params.roomContextRegistryKey=="string"?t.params.roomContextRegistryKey:null,activationCauseMessageId:typeof t.params.activationCauseMessageId=="string"?t.params.activationCauseMessageId:null,rootCauseMessageId:typeof t.params.rootCauseMessageId=="string"?t.params.rootCauseMessageId:null,activationSequence:typeof t.params.activationSequence=="string"?t.params.activationSequence:null,activationHopCount:typeof t.params.activationHopCount=="number"?t.params.activationHopCount:null,managedPolicyVersion:o?.source==="room_activation"?o.managedRoomPromptSnapshot.managedRoomPolicyVersion:o?.source==="manager_event"?o.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(o?.source==="room_activation"?o.managedRoomPromptSnapshot.runtimePermissionPolicyHash:o?.source==="manager_event"?o.managedRuntimePolicyHash:null)??(f?Me("sha256").update(JSON.stringify({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement})).digest("hex"):null)}),r.journalRunId=a},oe=()=>{if(H(),ee){let a=0;for(const[i,s]of k.entries()){const v=S(n,s,"queued");i===0&&(a=v)}return w("queued",a)}try{je(e,{sessionId:n,agentType:d,sessionMode:R,managerConfig:L??null,workDir:Se,agentSessionId:r.agentSessionId??m??null,modelId:h,managerDefaultWorkerAgentType:$??null,managerDefaultWorkerModelId:W??null});let a=0;for(const[i,s]of k.entries()){const v=Y?S(n,s,"committed"):le(n,s,"committed");i===0&&(a=v)}return Te(y,x??[]),_(e.client),w("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(c.id),a}},te=a=>{r.journalRunId&&e.runJournal?.update(n,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??c.id}`:null})},re=a=>{r.currentRunId||(r.nextEventSeq=0),te(a),H(a);const i=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let s=0;for(const[v,D]of k.entries()){const M=S(n,D,i);v===0&&(s=M)}return _(e.client),w(i,s)},N=async(a,i)=>{e.nativeFusion?.cancelManagedSend?.(c.id);const s=fe(d,a);if(console.error(`[chat.send] send failed reqId=${t.id} sessionId=${n} agentType=${A} workDir=${y} agentSessionId=${r.agentSessionId??m??""}: ${s}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){i&&e.client.sendRes({type:"res",id:l,ok:!0,payload:w("queued",0)});return}e.sessions.delete(n),r.journalRunId&&(e.runJournal?.update(n,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.journalRunId=null);try{await C(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:s,runId:"",seq:0}}),!i){const D={id:`agent-error-${t.id}-${Date.now()}`,sessionId:n,role:"agent",ts:Date.now(),payload:s};le(n,D),_(e.client)}i&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:s}))};if(G){try{oe()}catch(s){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:`Durable message commit failed: ${s instanceof Error?s.message:String(s)}`});return}ae(),ne();let a;try{(o?.source==="room_activation"||o?.source==="manager_event")&&(ge(e,r,I),u());const s=p.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,F),a=await pe(r.adapter,p.text,h,E,s,z,o?.source==="room_activation"?{source:"room_activation"}:o?.source==="manager_event"?{source:"managed_private"}:void 0),b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(s){if(g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),r.adapter.hasPendingAuthenticationRetry?.()){let M=0;for(const[V,K]of k.entries()){const we=S(n,K,"queued");V===0&&(M=we)}_(e.client),await N(s,!1),e.client.sendRes({type:"res",id:l,ok:!0,payload:w("queued",M)});return}let v=0;for(const[M,V]of k.entries()){const K=S(n,V,"failed");M===0&&(v=K)}_(e.client);const D=fe(d,s);await N(s,!1),e.client.sendRes({type:"res",id:l,ok:!0,payload:{...w("failed",v),failureMessage:D}});return}const i=re(a??void 0);e.client.sendRes({type:"res",id:l,ok:!0,payload:{...i,...p.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let se;try{se=oe()}catch(a){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}ae(),ne(),(o?.source==="room_activation"||o?.source==="manager_event")&&(ue(r.adapter,n,d,T,J,I,o.managedAgentPolicy,e.managedRoomExecutor),ge(e,r,I),u()),re(),e.client.sendRes({type:"res",id:l,ok:!0,payload:{...se,...p.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const ke=p.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,F),pe(r.adapter,p.text,h,E,ke,z,o?.source==="room_activation"?{source:"room_activation"}:o?.source==="manager_event"?{source:"managed_private"}:void 0).then(a=>{if(a?.deliveryMode==="external_owner_queue"){te(a),H(a);for(const i of k)S(n,i,"queued");_(e.client)}b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(a=>{g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),S(n,c,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),_(e.client),N(a,!1)})}async function cn(e,t){const{sessionId:o,discardPending:u}=t.params,n=e.sessions.get(o);if(n){n.journalRunId&&e.runJournal?.markAborted(o,n.journalRunId),me(e,n);try{await n.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ue(e,o),e.activityPublisher?.publish(o,null)}u===!0&&e.chatQueue?.purgeSession(o),e.client.sendRes({type:"res",id:t.id,ok:!0})}export{cn as handleChatAbort,ln as handleChatSend,mn as sendSessionUpdateEvent,pn as validateManagedRoomExecutionContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SESSION_SYNC_PROTOCOL_VERSION as g,SESSION_SYNC_DATA_EPOCH as f}from"@shennian/wire";import h from"node:crypto";import{getRegisteredAgents as I,unregisterAgent as A}from"../agents/adapter.js";import{resetCommandSpecCache as T}from"../agents/command-spec.js";import{loadConfig as P}from"../config/index.js";import{handleUpgradeStart as C,handleUpgradeStatus as M}from"../commands/upgrade.js";import{handleAgentsRefresh as D,handleModelsRefresh as x,refreshMachineAgents as E}from"./handlers/agents.js";import{AcpDiscoveryWatcher as F}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as O}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as Q,handleAgentConfigGet as _,handleAgentConfigTest as j,handleAgentConfigUpsert as B}from"./handlers/agent-config.js";import{handleChatAbort as U,handleChatSend as z}from"./handlers/chat.js";import{recoverJournaledRun as W}from"./handlers/chat-recovery.js";import{handleSessionRefresh as H}from"./handlers/session-refresh.js";import{handleSessionToolDetail as $}from"./handlers/tool-detail.js";import{handleSessionTitleSet as J}from"./handlers/title.js";import{handleSessionMessageStatus as N}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as V}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as L}from"./handlers/voice-window.js";import{handleRemoteAccessPause as G}from"./handlers/remote-access.js";import{cleanupPendingTransfers as Y,handleFsLs as K,handleFsRead as X,handleFsRename as Z,handleFsWrite as q,handleFsTransferAbort as ee,handleFsTransferChunk as te,handleFsTransferFinish as se,handleFsTransferStart as ae,handleFsExportMarkdownPdf as ne,handleFsExportMarkdownPdfSetup as re,handleFsArchiveZip as ie}from"./handlers/fs.js";import{handleRegionProbe as oe,handleRegionSwitch as ce,handleUpgradeSetPolicy as le}from"./handlers/control.js";import{ManagerRuntimeService as de,setManagerRuntimeService as v}from"../manager/runtime.js";import{ChatQueueManager as he}from"./queue.js";import{createAuthorizedFsRoot as ue,createAuthorizedFsRootAsync as me,resolveAuthorizedPath as pe,resolveAuthorizedPathAsync as ge,resolveSessionWorkDir as fe}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/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as ve}from"../agents/custom.js";import{handleManagedRoomBind as ye,handleManagedRoomRestart as Re,discardPreBindingSession as be}from"./handlers/room-managed.js";import{getDaemonInstallationId as u,listSessionRecords as m,purgeCanonicalSession as Se,purgeSessionRecord as we,recordSession as ke,upsertMessage as Ie}from"./store.js";import{requestSessionIndexPublish as Ae}from"./index-publisher.js";import{reportUpgradeAggregateMetric as Te}from"../log-reporter.js";import{PersonalHistorySubscriptions as Pe}from"./history-subscriptions.js";import{sweepAttachmentTransfers as Ce}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Me}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as De}from"./tool-detail-store.js";import{readDeletionSyncRevision as xe,writeDeletionSyncRevision as y}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as Ee,clearDeletionSyncFailure as Fe,DeletionSyncRetryWaitError as Oe,DeletionSyncTerminalError as Qe,recordDeletionSyncFailure as p}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as _e}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as c}from"../personal-sync/epoch.js";import{ActiveRunJournal as je}from"./run-recovery/journal.js";import{RunRecoveryBarrier as Be}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Ue}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as ze}from"./project-fs-store.js";import{handleProjectFsRequest as We}from"./handlers/project-fs.js";import{handleAgentPermissionResponse as He,handleAcpAgentRequest as $e}from"./handlers/agent-control.js";import{validateDeletionSyncResponse as Je}from"./deletion-sync-validation.js";import{dispatchManagedChat as Ne,dispatchQueuedChat as Ve,interruptQueuedChat as Le}from"./queued-chat-dispatch.js";const Ge=50;import{resolveSessionWorkDir as qt}from"../fs/boundary.js";function Ye(){return"\u795E\u5FF5\u5347\u7EA7\u4E2D\u65AD\u4E86\u672C\u6B21\u56DE\u590D\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u53EF\u91CD\u8BD5\u6216\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002"}class Kt{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;getAgentSnapshot;scheduleHandler;getScheduleQueue(){return this.chatQueue}sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new Be;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;projectFsHandleStore=new ze;constructor(e,s=null,t,r=null,n=null,d=null,o=null,R=null,b=null,S=null,w=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=r,this.managedRoomBinder=n,this.remoteAccessPauseController=d,this.managedRoomContextRevoker=o,this.managedRoomExecutor=R,this.trustedProjectFsRequestResolver=S,this.getAgentSnapshot=w,this.runJournal=b??new je(u());for(const a of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(a.sessionId);this.recoveryCoordinator=new Ue({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(a,i)=>W(this.getRuntime(),a,i),commitTerminal:(a,i)=>this.commitRecoveryTerminal(a,i),isCanonicalTerminal:a=>{const i=m().find(k=>k.sessionId===a.sessionId),l=`terminal:${h.createHash("sha256").update(`${a.runId}\0`).digest("hex")}`;return i?.lastAttentionId===l},onOutcome:a=>Te({kind:"recovery",adapterType:a.adapterType,result:a.result,stage:a.result==="continued"?"continue":a.result==="reattached"?"reattach":a.result==="resumed"?"resume":a.result==="terminal_reconciled"?"inspect":"terminal",durationMs:a.durationMs,retries:Math.max(0,a.attempts-1),...a.reason?{errorCode:a.reason}:{}})}),this.managerRuntime=new de({getRuntime:()=>this.getRuntime(),getAgentSnapshot:this.getAgentSnapshot??void 0,dispatchReq:a=>this.handleReq(a,{propagateErrors:!0})}),this.chatQueue=new he({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a),dispatchManagedReq:(a,i)=>Ne(this.getRuntime(),a,i),dispatchQueuedReq:(a,i)=>Ve(this.getRuntime(),a,i),interruptQueuedSession:a=>Le(this.getRuntime(),a)}),this.historySubscriptions=new Pe(this.client),this.nativeCleanupOutbox=new _e({logger:console.info}),this.acpDiscoveryWatcher=new F(async()=>{T(),await E(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(a=>{this.handleExternalTerminal(a)}),this.nativeFusion?.setExternalClaimHandler?.((a,i)=>{this.chatQueue.noteExternalClaim(a,i)}),this.nativeCleanupOutbox.start(),v(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(a){console.warn(`[acp.discovery] watcher unavailable: ${a instanceof Error?a.message:String(a)}`)}this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const a=new Set([...m().map(i=>i.workDir),...Array.from(this.pendingTransfers.values()).map(i=>i.rootPath)]);for(const i of a)try{Ce(this.resolvePath(i))}catch(l){console.error("[attachment.cleanup] failed",l instanceof Error?l.message:String(l))}},6e4),this.attachmentCleanupTimer.unref?.()}executeManagedManagerAction(e){return this.managerRuntime.executeScopedWorkerAction(e.managerSessionId,e.action,e.params,e.roomOrigin)}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,s)=>this.resolveAuthorizedPath(e,s),resolveAuthorizedPathAsync:(e,s)=>this.resolveAuthorizedPathAsync(e,s),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,s)=>this.publishSessionActivity(e,s)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const s=[...this.sessions.values()].find(t=>t.agentType===e&&t.adapter.hasPendingAuthenticationRetry?.());return s?.adapter.resumeAfterExternalAuthentication?(await s.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(s=>s.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}}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const s=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:s}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}async commitRecoveryTerminal(e,s){const t=this.sessions.get(e.sessionId);t&&(t.currentRunId=null,t.currentRunPhase=null,t.journalRunId=null,t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const r=Ye();Ie(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:r,error:!0,recoveryState:s.result,recoveryReason:s.reason})}),ke({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),this.managerRuntime.noteAgentEvent(e.sessionId,{state:"error",runId:e.providerRunId??e.runId,seq:e.lastObservedEventSeq+1,message:r}),Ae(this.client);const n=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:s.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:n,reason:s.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:n,reason:s.reason,...s.message?{message:s.message}:{}},seq:n,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const s=e.map(t=>{const r=m().find(n=>n.sessionId===t.params.sessionId);return{context:t.context,params:{...t.params,modelId:r?.modelId??t.params.modelId,sessionMode:r?.sessionMode??t.params.sessionMode,managerConfig:r?.managerConfig??t.params.managerConfig,managerDefaultWorkerAgentType:r?.managerDefaultWorkerAgentType??t.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:r?.managerDefaultWorkerModelId??t.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(s)}async retireManagedRoomSession(e){this.chatQueue.purgeSession(e),await be(this.getRuntime(),e)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async synchronizeSessionDeletions(){Ee();try{await this.synchronizeSessionDeletionsOnce(),Fe()}catch(e){if(e instanceof Oe||e instanceof Qe)throw e;const s=e instanceof Error?e.message:String(e),t=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(s)?"transport_unavailable":/invalid (?:deletion sync|session deletion)|deletion revision gap/i.test(s)?"invalid_deletion_sync_response":"deletion_sync_failed";throw console.warn("[session.deletions] retrying failed synchronization",{code:t}),p({code:t,retryable:!0}),e}}async synchronizeSessionDeletionsOnce(){const e=u(),s=xe(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${h.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,afterRevision:s}});if(!t.ok){const o=t.rejection;throw o?(p({code:o.code,retryable:o.retryable&&o.requiredAction==="retry"}),new Error(o.code)):(p({code:"unstructured_rejection",retryable:!0}),new Error(t.error??"unstructured_rejection"))}const r=t.payload;c(r),Je(r,s);let n=r.baselineRevision;n>s&&y(n);for(const o of r.deletions)o.deletionRevision<=n||(await this.applySessionDeletion(o),n=o.deletionRevision,y(n));if(n!==r.latestRevision)throw new Error(`deletion revision gap: applied ${n}, latest ${r.latestRevision}`);if(n===0)return;const d=await this.client.sendReq({type:"req",id:`session-deletions-ack-${h.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,deletionRevision:n}});if(!d.ok)throw new Error(d.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=new Me({daemonInstallationId:u()});s.recordSession(e);for(const r of e.sources){const n=s.find(r.agentType,r.sourceSessionKey);n&&n.sessionId!==e.sessionId||s.record({...e,...r})}this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const t=this.sessions.get(e.sessionId);if(t){this.revokeManagedRoomToken(t);try{await t.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),De(e.sessionId),Se(e.sessionId),we(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,s){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:s}})}async handleExternalTerminal(e){const s=this.sessions.get(e);s&&(s.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,s.journalRunId),s.journalRunId=null),this.sessions.delete(e),this.revokeManagedRoomToken(s),s.heartbeatTimer&&clearInterval(s.heartbeatTimer),s.adapter.removeAllListeners(),await s.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,s]of this.sessions.entries()){if(s.heartbeatTimer||!s.currentRunId||!s.adapter.getStatus)continue;const t=await s.adapter.getStatus().catch(()=>null);if(!t?.active||!t.runPhase)continue;const r=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:t.runId||s.currentRunId,runPhase:t.runPhase,startedAt:new Date(s.lastActiveAt).toISOString(),updatedAt:r,canStop:t.canStop??!0});const n=s.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.runId||s.currentRunId,seq:n,runPhase:t.runPhase,canStop:t.canStop??!0},seq:n,id:`agent-status-${t.runId||s.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const s of I())s.startsWith("custom:")&&A(s);const e=P();for(const[s,t]of Object.entries(e.customAgents??{}))ve(s,t)}async handleReq(e,s={}){const t=this.getRuntime();let r;this.activeRequests++;try{switch(e.method){case"schedule.deliver":if(!this.scheduleHandler)throw new Error("schedules_unavailable");await this.scheduleHandler(e);break;case"chat.send":if(e.params.scheduledDelivery)throw new Error("schedule_source_untrusted");c(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?r={durableQueueHandoff:await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"})}:await z(t,e);break;case"chat.enqueue":c(e.params),r={durableQueueHandoff:await this.chatQueue.handleEnqueue(e)};break;case"chat.queue.get":c(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":c(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":c(e.params),await this.chatQueue.handleDelete(e);break;case"chat.queue.promote":c(e.params),await this.chatQueue.handlePromote(e);break;case"chat.abort":c(e.params),await U(t,e);break;case"session.refresh":c(e.params),await H(t,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":N(t,e);break;case"session.tool.detail":await $(t,e);break;case"voice.session.body.window":L(t,e);break;case"session.title.set":await J(t,e);break;case"room.agent.bind-managed":await ye(t,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await Re(t,this.managedRoomBinder,e);break;case"fs.ls":await K(t,e);break;case"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await We(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);break;case"fs.read":await X(t,e);break;case"fs.write":await q(t,e);break;case"fs.export.markdown-pdf":await ne(t,e);break;case"fs.export.markdown-pdf.setup":await re(t,e);break;case"fs.archive.zip":await ie(t,e);break;case"fs.rename":await Z(t,e);break;case"fs.transfer.start":await ae(t,e);break;case"fs.transfer.chunk":await te(t,e);break;case"fs.transfer.finish":await se(t,e);break;case"fs.transfer.abort":await ee(t,e);break;case"region.probe":await oe(t,e);break;case"region.switch":await ce(t,e);break;case"upgrade.start":await C(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:n=>this.prepareForUpgrade(n),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await M(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await le(t,e);break;case"machine.remote-access.pause":await G(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await D(t,e);break;case"agent.permission.respond":await He(t,e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await $e(t,e);break;case"models.refresh":await x(t,e);break;case"agent.config.get":await _(t,e);break;case"agent.config.upsert":await B(t,e);break;case"agent.config.clear":await Q(t,e);break;case"agent.config.test":await j(t,e);break;case"agent.workspace.validate":await V(t,e);break;case"agent.capabilities.list":await O(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(n){if(s.propagateErrors)throw n;this.client.sendRes({type:"res",id:e.id,ok:!1,error:n instanceof Error?n.message:String(n)})}finally{this.activeRequests--}return r}evictIdleSessions(){if(this.sessions.size<Ge)return;let e=null;for(const[s,t]of this.sessions)(!e||t.lastActiveAt<e.session.lastActiveAt)&&(e={key:s,session:t});e&&(this.revokeManagedRoomToken(e.session),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 fe(e)}resolveAuthorizedPath(e,s){return pe(e,ue(s))}async resolveAuthorizedPathAsync(e,s){return ge(e,await me(s))}async cleanup(){this.chatQueue.stop(),this.projectFsHandleStore.revokeAll(),this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),Y(this.getRuntime()),await this.managerRuntime.stop(),v(null)}revokeManagedRoomToken(e){const s=e.managedRoomContextToken;e.managedRoomContextToken=null,s&&this.managedRoomContextRevoker?.(s)}}export{Kt as SessionManager,qt as resolveSessionWorkDir,Ye as upgradeRecoveryVisibleMessage};
|
|
1
|
+
import{SESSION_SYNC_PROTOCOL_VERSION as g,SESSION_SYNC_DATA_EPOCH as f}from"@shennian/wire";import h from"node:crypto";import{getRegisteredAgents as I,unregisterAgent as A}from"../agents/adapter.js";import{resetCommandSpecCache as T}from"../agents/command-spec.js";import{loadConfig as P}from"../config/index.js";import{handleUpgradeStart as C,handleUpgradeStatus as M}from"../commands/upgrade.js";import{handleAgentsRefresh as D,handleModelsRefresh as x,refreshMachineAgents as E}from"./handlers/agents.js";import{AcpDiscoveryWatcher as F}from"../agents/acp/watcher.js";import{handleAgentCapabilitiesList as O}from"./handlers/agent-capabilities.js";import{handleAgentConfigClear as Q,handleAgentConfigGet as _,handleAgentConfigTest as j,handleAgentConfigUpsert as B}from"./handlers/agent-config.js";import{handleChatAbort as U,handleChatSend as z}from"./handlers/chat.js";import{recoverJournaledRun as W}from"./handlers/chat-recovery.js";import{handleSessionRefresh as H}from"./handlers/session-refresh.js";import{handleSessionToolDetail as $}from"./handlers/tool-detail.js";import{handleSessionTitleSet as J}from"./handlers/title.js";import{handleSessionMessageStatus as N}from"./handlers/message-status.js";import{handleAgentWorkspaceValidation as V}from"./handlers/agent-workspace-validation.js";import{handleVoiceSessionBodyWindow as L}from"./handlers/voice-window.js";import{handleRemoteAccessPause as G}from"./handlers/remote-access.js";import{cleanupPendingTransfers as Y,handleFsLs as K,handleFsRead as X,handleFsRename as Z,handleFsWrite as q,handleFsTransferAbort as ee,handleFsTransferChunk as te,handleFsTransferFinish as se,handleFsTransferStart as ae,handleFsExportMarkdownPdf as ne,handleFsExportMarkdownPdfSetup as re,handleFsArchiveZip as ie}from"./handlers/fs.js";import{handleRegionProbe as oe,handleRegionSwitch as ce,handleUpgradeSetPolicy as le}from"./handlers/control.js";import{ManagerRuntimeService as de,setManagerRuntimeService as v}from"../manager/runtime.js";import{ChatQueueManager as he}from"./queue.js";import{createAuthorizedFsRoot as ue,createAuthorizedFsRootAsync as me,resolveAuthorizedPath as pe,resolveAuthorizedPathAsync as ge,resolveSessionWorkDir as fe}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/pi-coding-agent.js";import"../agents/manager.js";import{registerCustomAgent as ve}from"../agents/custom.js";import{handleManagedRoomBind as ye,handleManagedRoomRestart as Re,discardPreBindingSession as be}from"./handlers/room-managed.js";import{getDaemonInstallationId as u,listSessionRecords as m,purgeCanonicalSession as Se,purgeSessionRecord as we,recordSession as ke,upsertMessage as Ie}from"./store.js";import{requestSessionIndexPublish as Ae}from"./index-publisher.js";import{reportUpgradeAggregateMetric as Te}from"../log-reporter.js";import{PersonalHistorySubscriptions as Pe}from"./history-subscriptions.js";import{sweepAttachmentTransfers as Ce}from"./attachment-transfer-store.js";import{NativeSourceDeletionStore as Me}from"../native-fusion/native-source-deletions.js";import{purgeToolDetails as De}from"./tool-detail-store.js";import{readDeletionSyncRevision as xe,writeDeletionSyncRevision as y}from"./deletion-sync-state.js";import{assertDeletionSyncRunnable as Ee,clearDeletionSyncFailure as Fe,DeletionSyncRetryWaitError as Oe,DeletionSyncTerminalError as Qe,recordDeletionSyncFailure as p}from"./deletion-sync-state.js";import{NativeSessionCleanupOutbox as _e}from"./native-cleanup-outbox.js";import{assertPersonalSyncIdentity as c}from"../personal-sync/epoch.js";import{ActiveRunJournal as je}from"./run-recovery/journal.js";import{RunRecoveryBarrier as Be}from"./run-recovery/barrier.js";import{RunRecoveryCoordinator as Ue}from"./run-recovery/coordinator.js";import{ProjectFsHandleStore as ze}from"./project-fs-store.js";import{handleProjectFsRequest as We}from"./handlers/project-fs.js";import{handleAgentPermissionResponse as He,handleAcpAgentRequest as $e}from"./handlers/agent-control.js";import{validateDeletionSyncResponse as Je}from"./deletion-sync-validation.js";import{dispatchManagedChat as Ne,dispatchQueuedChat as Ve,interruptQueuedChat as Le}from"./queued-chat-dispatch.js";const Ge=50;import{resolveSessionWorkDir as qt}from"../fs/boundary.js";function Ye(){return"\u795E\u5FF5\u5347\u7EA7\u4E2D\u65AD\u4E86\u672C\u6B21\u8FD0\u884C\u3002\u5DF2\u5B8C\u6210\u7684\u56DE\u590D\u6216\u64CD\u4F5C\u4ECD\u7136\u6709\u6548\uFF0C\u672A\u5B8C\u6210\u90E8\u5206\u53EF\u91CD\u8BD5\u3002\u539F\u4F1A\u8BDD\u5DF2\u4FDD\u7559\u3002"}class Kt{client;nativeFusion;cliVersion;upgradePolicyController;managedRoomBinder;remoteAccessPauseController;managedRoomContextRevoker;managedRoomExecutor;trustedProjectFsRequestResolver;getAgentSnapshot;scheduleHandler;getScheduleQueue(){return this.chatQueue}sessions=new Map;processedReqIds=new Set;runTextAcc=new Map;pendingTransfers=new Map;managerRuntime;chatQueue;historySubscriptions;nativeCleanupOutbox;activityProbeTimer=null;attachmentCleanupTimer=null;activeRequests=0;acpDiscoveryWatcher;runJournal;recoveryBarrier=new Be;recoveryCoordinator;recoveryPass=null;prepareManagedRecovery;projectFsHandleStore=new ze;constructor(e,s=null,t,r=null,n=null,d=null,o=null,R=null,b=null,S=null,w=null){this.client=e,this.nativeFusion=s,this.cliVersion=t,this.upgradePolicyController=r,this.managedRoomBinder=n,this.remoteAccessPauseController=d,this.managedRoomContextRevoker=o,this.managedRoomExecutor=R,this.trustedProjectFsRequestResolver=S,this.getAgentSnapshot=w,this.runJournal=b??new je(u());for(const a of this.runJournal.loadPendingReconciliation().records)this.recoveryBarrier.begin(a.sessionId);this.recoveryCoordinator=new Ue({journal:this.runJournal,barrier:this.recoveryBarrier,recover:(a,i)=>W(this.getRuntime(),a,i),commitTerminal:(a,i)=>this.commitRecoveryTerminal(a,i),isCanonicalTerminal:a=>{const i=m().find(k=>k.sessionId===a.sessionId),l=`terminal:${h.createHash("sha256").update(`${a.runId}\0`).digest("hex")}`;return i?.lastAttentionId===l},onOutcome:a=>Te({kind:"recovery",adapterType:a.adapterType,result:a.result,stage:a.result==="continued"?"continue":a.result==="reattached"?"reattach":a.result==="resumed"?"resume":a.result==="terminal_reconciled"?"inspect":"terminal",durationMs:a.durationMs,retries:Math.max(0,a.attempts-1),...a.reason?{errorCode:a.reason}:{}})}),this.managerRuntime=new de({getRuntime:()=>this.getRuntime(),getAgentSnapshot:this.getAgentSnapshot??void 0,dispatchReq:a=>this.handleReq(a,{propagateErrors:!0})}),this.chatQueue=new he({getRuntime:()=>this.getRuntime(),dispatchReq:a=>this.handleReq(a),dispatchManagedReq:(a,i)=>Ne(this.getRuntime(),a,i),dispatchQueuedReq:(a,i)=>Ve(this.getRuntime(),a,i),interruptQueuedSession:a=>Le(this.getRuntime(),a)}),this.historySubscriptions=new Pe(this.client),this.nativeCleanupOutbox=new _e({logger:console.info}),this.acpDiscoveryWatcher=new F(async()=>{T(),await E(this.getRuntime())}),this.nativeFusion?.setExternalTerminalHandler?.(a=>{this.handleExternalTerminal(a)}),this.nativeFusion?.setExternalClaimHandler?.((a,i)=>{this.chatQueue.noteExternalClaim(a,i)}),this.nativeCleanupOutbox.start(),v(this.managerRuntime),this.managerRuntime.start(),this.reloadCustomAgents();try{this.acpDiscoveryWatcher.start()}catch(a){console.warn(`[acp.discovery] watcher unavailable: ${a instanceof Error?a.message:String(a)}`)}this.activityProbeTimer=setInterval(()=>{this.publishManagedActivitySnapshots().catch(a=>{console.error("[session.activity] managed probe failed",a)})},15e3),this.activityProbeTimer.unref?.(),this.attachmentCleanupTimer=setInterval(()=>{const a=new Set([...m().map(i=>i.workDir),...Array.from(this.pendingTransfers.values()).map(i=>i.rootPath)]);for(const i of a)try{Ce(this.resolvePath(i))}catch(l){console.error("[attachment.cleanup] failed",l instanceof Error?l.message:String(l))}},6e4),this.attachmentCleanupTimer.unref?.()}executeManagedManagerAction(e){return this.managerRuntime.executeScopedWorkerAction(e.managerSessionId,e.action,e.params,e.roomOrigin)}getRuntime(){return{client:this.client,pendingTransfers:this.pendingTransfers,processedReqIds:this.processedReqIds,reloadCustomAgents:()=>this.reloadCustomAgents(),resolvePath:e=>this.resolvePath(e),resolveAuthorizedPath:(e,s)=>this.resolveAuthorizedPath(e,s),resolveAuthorizedPathAsync:(e,s)=>this.resolveAuthorizedPathAsync(e,s),runTextAcc:this.runTextAcc,sessions:this.sessions,evictIdleSessions:()=>this.evictIdleSessions(),nativeFusion:this.nativeFusion,managerRuntime:this.managerRuntime,chatQueue:this.chatQueue,activityPublisher:{publish:(e,s)=>this.publishSessionActivity(e,s)},upgradePolicyController:this.upgradePolicyController,revokeManagedRoomContextToken:this.managedRoomContextRevoker,managedRoomExecutor:this.managedRoomExecutor,runJournal:this.runJournal,recoveryBarrier:this.recoveryBarrier,prepareManagedRecovery:this.prepareManagedRecovery}}async resumePendingAcpAuthentication(e){const s=[...this.sessions.values()].find(t=>t.agentType===e&&t.adapter.hasPendingAuthenticationRetry?.());return s?.adapter.resumeAfterExternalAuthentication?(await s.adapter.resumeAfterExternalAuthentication(),!0):!1}getUpgradeIdleState(){const e=[];return[...this.sessions.values()].some(s=>s.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}}prepareForUpgrade(e){this.recoveryBarrier.freezeDispatch();const s=this.runJournal.markPlannedUpgrade(e);return this.runJournal.sync(),{activeRunCount:s}}cancelUpgradePreparation(){this.recoveryBarrier.unfreezeDispatch()}async recoverInterruptedRuns(){this.recoveryPass??=this.recoveryCoordinator.recoverAll(),await this.recoveryPass}getPendingRecoveryCount(){return this.runJournal.loadPendingReconciliation().records.length}revokeProjectFsHandles(){this.projectFsHandleStore.revokeAll()}async commitRecoveryTerminal(e,s){const t=this.sessions.get(e.sessionId);t&&(t.currentRunId=null,t.currentRunPhase=null,t.journalRunId=null,t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)),this.runTextAcc.delete(`${e.sessionId}:${e.providerRunId??e.runId}`),this.publishSessionActivity(e.sessionId,null);const r=Ye();Ie(e.sessionId,{id:`upgrade-recovery-terminal-${e.runId}`,sessionId:e.sessionId,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:"error",content:r,error:!0,recoveryState:s.result,recoveryReason:s.reason})}),ke({sessionId:e.sessionId,agentType:e.agentType,sessionMode:e.sessionMode,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,status:"failed",attentionId:`upgrade-recovery:${e.runId}`,lastActivityAt:new Date().toISOString()}),this.managerRuntime.noteAgentEvent(e.sessionId,{state:"error",runId:e.providerRunId??e.runId,seq:e.lastObservedEventSeq+1,message:r}),Ae(this.client);const n=e.lastObservedEventSeq+1;this.client.sendAgentEvent({type:"event",event:"agent",payload:s.result==="interrupted_by_upgrade"?{state:"interrupted_by_upgrade",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:n,reason:s.reason}:{state:"recovery_failed",sessionId:e.sessionId,runId:e.providerRunId??e.runId,seq:n,reason:s.reason,...s.message?{message:s.message}:{}},seq:n,id:`agent-recovery-terminal-${e.runId}`}),this.chatQueue.noteTerminal(e.sessionId)}setManagedRoomTurnPreparer(e){this.chatQueue.setManagedRoomTurnPreparer(e)}setManagedAgentDirectTurnPreparer(e){this.chatQueue.setManagedAgentDirectTurnPreparer(e)}setManagedRecoveryPreparer(e){this.prepareManagedRecovery=e}async enqueueManagedRoomBatch(e){const s=e.map(t=>{const r=m().find(n=>n.sessionId===t.params.sessionId);return{context:t.context,params:{...t.params,modelId:r?.modelId??t.params.modelId,sessionMode:r?.sessionMode??t.params.sessionMode,managerConfig:r?.managerConfig??t.params.managerConfig,managerDefaultWorkerAgentType:r?.managerDefaultWorkerAgentType??t.params.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:r?.managerDefaultWorkerModelId??t.params.managerDefaultWorkerModelId}}});await this.chatQueue.enqueueManagedRoomBatch(s)}async retireManagedRoomSession(e){this.chatQueue.purgeSession(e),await be(this.getRuntime(),e)}discardPendingManagedRoomMessages(e){return this.chatQueue.discardPendingManagedRoomMessages(e)}async synchronizeSessionDeletions(){Ee();try{await this.synchronizeSessionDeletionsOnce(),Fe()}catch(e){if(e instanceof Oe||e instanceof Qe)throw e;const s=e instanceof Error?e.message:String(e),t=/offline|not connected|disconnect|timed out|timeout|temporar|unavailable|ECONN|ENOTFOUND|EIO|EBUSY/i.test(s)?"transport_unavailable":/invalid (?:deletion sync|session deletion)|deletion revision gap/i.test(s)?"invalid_deletion_sync_response":"deletion_sync_failed";throw console.warn("[session.deletions] retrying failed synchronization",{code:t}),p({code:t,retryable:!0}),e}}async synchronizeSessionDeletionsOnce(){const e=u(),s=xe(),t=await this.client.sendReq({type:"req",id:`session-deletions-sync-${h.randomUUID()}`,method:"session.deletions.sync",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,afterRevision:s}});if(!t.ok){const o=t.rejection;throw o?(p({code:o.code,retryable:o.retryable&&o.requiredAction==="retry"}),new Error(o.code)):(p({code:"unstructured_rejection",retryable:!0}),new Error(t.error??"unstructured_rejection"))}const r=t.payload;c(r),Je(r,s);let n=r.baselineRevision;n>s&&y(n);for(const o of r.deletions)o.deletionRevision<=n||(await this.applySessionDeletion(o),n=o.deletionRevision,y(n));if(n!==r.latestRevision)throw new Error(`deletion revision gap: applied ${n}, latest ${r.latestRevision}`);if(n===0)return;const d=await this.client.sendReq({type:"req",id:`session-deletions-ack-${h.randomUUID()}`,method:"session.deletions.ack",params:{protocolVersion:g,dataEpoch:f,daemonInstallationId:e,deletionRevision:n}});if(!d.ok)throw new Error(d.error??"session deletion acknowledgement failed")}async applySessionDeletion(e){const s=new Me({daemonInstallationId:u()});s.recordSession(e);for(const r of e.sources){const n=s.find(r.agentType,r.sourceSessionKey);n&&n.sessionId!==e.sessionId||s.record({...e,...r})}this.runJournal.markSessionAborted(e.sessionId),this.recoveryBarrier.end(e.sessionId);const t=this.sessions.get(e.sessionId);if(t){this.revokeManagedRoomToken(t);try{await t.adapter.stop()}catch{}this.sessions.delete(e.sessionId)}this.historySubscriptions.closeSession(e.sessionId),this.nativeFusion?.handleSessionDeleted(e.sessionId),this.chatQueue.purgeSession(e.sessionId),De(e.sessionId),Se(e.sessionId),we(e.sessionId),this.runTextAcc.delete(e.sessionId),this.publishSessionActivity(e.sessionId,null),this.nativeCleanupOutbox.enqueue(e),this.nativeCleanupOutbox.kick()}publishSessionActivity(e,s){this.client.sendEvent({type:"event",event:"session.activity",payload:{sessionId:e,activity:s}})}async handleExternalTerminal(e){const s=this.sessions.get(e);s&&(s.journalRunId&&(this.recoveryCoordinator.completeFromProvider(e,s.journalRunId),s.journalRunId=null),this.sessions.delete(e),this.revokeManagedRoomToken(s),s.heartbeatTimer&&clearInterval(s.heartbeatTimer),s.adapter.removeAllListeners(),await s.adapter.stop().catch(()=>{})),this.chatQueue.noteTerminal(e)}async publishManagedActivitySnapshots(){for(const[e,s]of this.sessions.entries()){if(s.heartbeatTimer||!s.currentRunId||!s.adapter.getStatus)continue;const t=await s.adapter.getStatus().catch(()=>null);if(!t?.active||!t.runPhase)continue;const r=new Date().toISOString();this.publishSessionActivity(e,{sessionId:e,runId:t.runId||s.currentRunId,runPhase:t.runPhase,startedAt:new Date(s.lastActiveAt).toISOString(),updatedAt:r,canStop:t.canStop??!0});const n=s.heartbeatSeq++;this.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.runId||s.currentRunId,seq:n,runPhase:t.runPhase,canStop:t.canStop??!0},seq:n,id:`agent-status-${t.runId||s.currentRunId}-${Date.now()}`})}}reloadCustomAgents(){for(const s of I())s.startsWith("custom:")&&A(s);const e=P();for(const[s,t]of Object.entries(e.customAgents??{}))ve(s,t)}async handleReq(e,s={}){const t=this.getRuntime();let r;this.activeRequests++;try{switch(e.method){case"schedule.deliver":if(!this.scheduleHandler)throw new Error("schedules_unavailable");await this.scheduleHandler(e);break;case"chat.send":if(e.params.scheduledDelivery)throw new Error("schedule_source_untrusted");c(e.params),this.recoveryBarrier.isDispatchFrozen()||this.recoveryBarrier.isRecovering(String(e.params.sessionId??""))?r={durableQueueHandoff:await this.chatQueue.handleEnqueue({...e,method:"chat.enqueue"})}:await z(t,e);break;case"chat.enqueue":c(e.params),r={durableQueueHandoff:await this.chatQueue.handleEnqueue(e)};break;case"chat.queue.get":c(e.params),await this.chatQueue.handleGet(e);break;case"chat.queue.edit":c(e.params),await this.chatQueue.handleEdit(e);break;case"chat.queue.delete":c(e.params),await this.chatQueue.handleDelete(e);break;case"chat.queue.promote":c(e.params),await this.chatQueue.handlePromote(e);break;case"chat.abort":c(e.params),await U(t,e);break;case"session.refresh":c(e.params),await H(t,e);break;case"session.history.open":this.historySubscriptions.handleOpen(e);break;case"session.history.sync":this.historySubscriptions.handleSync(e);break;case"session.history.page":this.historySubscriptions.handlePage(e);break;case"session.history.close":this.historySubscriptions.handleClose(e);break;case"session.body.replica.subscribe":this.historySubscriptions.handleReplicaSubscribe(e);break;case"session.body.replica.unsubscribe":this.historySubscriptions.handleReplicaUnsubscribe(e);break;case"session.history":this.client.sendRes({type:"res",id:e.id,ok:!1,error:"upgrade_required"});break;case"session.message.status":N(t,e);break;case"session.tool.detail":await $(t,e);break;case"voice.session.body.window":L(t,e);break;case"session.title.set":await J(t,e);break;case"room.agent.bind-managed":await ye(t,this.managedRoomBinder,e);break;case"room.agent.restart-managed":await Re(t,this.managedRoomBinder,e);break;case"fs.ls":await K(t,e);break;case"project.fs.open":case"project.fs.list":case"project.fs.read":case"project.fs.close":await We(this.client,this.projectFsHandleStore,e,this.trustedProjectFsRequestResolver);break;case"fs.read":await X(t,e);break;case"fs.write":await q(t,e);break;case"fs.export.markdown-pdf":await ne(t,e);break;case"fs.export.markdown-pdf.setup":await re(t,e);break;case"fs.archive.zip":await ie(t,e);break;case"fs.rename":await Z(t,e);break;case"fs.transfer.start":await ae(t,e);break;case"fs.transfer.chunk":await te(t,e);break;case"fs.transfer.finish":await se(t,e);break;case"fs.transfer.abort":await ee(t,e);break;case"region.probe":await oe(t,e);break;case"region.switch":await ce(t,e);break;case"upgrade.start":await C(this.client,e.id,e.params.version,{currentVersion:this.cliVersion,confirmedMajor:e.params.confirmedMajor===!0,prepareForUpgrade:n=>this.prepareForUpgrade(n),cancelUpgradePreparation:()=>this.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()}});break;case"upgrade.status":await M(this.client,e.id,{currentVersion:this.cliVersion});break;case"upgrade.set-policy":await le(t,e);break;case"machine.remote-access.pause":await G(this.client,this.remoteAccessPauseController,e);break;case"agents.refresh":await D(t,e);break;case"agent.permission.respond":await He(t,e);break;case"agent.auth.start":case"agent.auth.logout":case"agent.session-options.get":case"agent.external-sessions.list":case"agent.external-sessions.import":case"agent.external-sessions.close":case"agent.external-sessions.delete":await $e(t,e);break;case"models.refresh":await x(t,e);break;case"agent.config.get":await _(t,e);break;case"agent.config.upsert":await B(t,e);break;case"agent.config.clear":await Q(t,e);break;case"agent.config.test":await j(t,e);break;case"agent.workspace.validate":await V(t,e);break;case"agent.capabilities.list":await O(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(n){if(s.propagateErrors)throw n;this.client.sendRes({type:"res",id:e.id,ok:!1,error:n instanceof Error?n.message:String(n)})}finally{this.activeRequests--}return r}evictIdleSessions(){if(this.sessions.size<Ge)return;let e=null;for(const[s,t]of this.sessions)(!e||t.lastActiveAt<e.session.lastActiveAt)&&(e={key:s,session:t});e&&(this.revokeManagedRoomToken(e.session),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 fe(e)}resolveAuthorizedPath(e,s){return pe(e,ue(s))}async resolveAuthorizedPathAsync(e,s){return ge(e,await me(s))}async cleanup(){this.chatQueue.stop(),this.projectFsHandleStore.revokeAll(),this.acpDiscoveryWatcher.stop(),this.nativeFusion?.setExternalTerminalHandler?.(null),this.nativeCleanupOutbox.stop(),this.historySubscriptions.closeAll(),this.activityProbeTimer&&(clearInterval(this.activityProbeTimer),this.activityProbeTimer=null),this.attachmentCleanupTimer&&(clearInterval(this.attachmentCleanupTimer),this.attachmentCleanupTimer=null);for(const[,e]of this.sessions)this.revokeManagedRoomToken(e),e.heartbeatTimer&&(clearInterval(e.heartbeatTimer),e.heartbeatTimer=null),e.adapter.removeAllListeners(),await e.adapter.stop().catch(()=>{});this.sessions.clear(),this.runTextAcc.clear(),Y(this.getRuntime()),await this.managerRuntime.stop(),v(null)}revokeManagedRoomToken(e){const s=e.managedRoomContextToken;e.managedRoomContextToken=null,s&&this.managedRoomContextRevoker?.(s)}}export{Kt as SessionManager,qt as resolveSessionWorkDir,Ye as upgradeRecoveryVisibleMessage};
|
|
@@ -25,4 +25,7 @@ export type ManagedAgentDirectTurnOptions = {
|
|
|
25
25
|
managerEvent?: boolean;
|
|
26
26
|
};
|
|
27
27
|
export type ManagedAgentDirectTurnPreparer = (sessionId: string, options?: ManagedAgentDirectTurnOptions) => Promise<PreparedManagedRoomTurn | null>;
|
|
28
|
-
export type ManagedRoomEnqueueParams = Omit<ChatEnqueueParams, 'protocolVersion' | 'dataEpoch' | 'daemonInstallationId'
|
|
28
|
+
export type ManagedRoomEnqueueParams = Omit<ChatEnqueueParams, 'protocolVersion' | 'dataEpoch' | 'daemonInstallationId'> & {
|
|
29
|
+
/** Original authoritative Room timestamp; never accepted from App chat.enqueue. */
|
|
30
|
+
roomMessageCreatedAt?: string;
|
|
31
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as V}from"./queue-retry.js";import{findDuplicateQueuedMessage as Q,stripLocalQueueFields as _,takeCompatibleBatch as X,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as W,isControllableQueueMessage as v,takeControllableMessage as Y,normalizeQueuedModelId as F,resolveManagedRoomNativeSessionId as T,managedDirectTurnOptions as L}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as Z}from"@shennian/wire";import{isCodexThreadTailInterrupted as ee}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as te}from"./projection.js";import{assertFinishedAttachmentReferences as j,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as N,getMessageDeliveryStatus as x,listSessionRecords as z,recordSession as $,tombstoneMessage as J,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as y}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as b}from"../personal-sync/epoch.js";import{getDaemonInstallationId as E}from"../personal-sync/identity.js";import{isTrustedManagerEvent as ae}from"./manager-event-authority.js";import{TerminalQueuedChatDispatchError as D,nowIso as A,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as xe}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as qe}from"./queue-message.js";class Re{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new V(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,se(),setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e),i=this.isImmediateSendAvailable(e),n=s.filter(a=>v(a)).map(a=>({..._(a),pendingState:a.externalQueuedSubmissionId?"provider_queued":t?.has(a.id)?"submitting":"waiting",canDelete:!a.externalQueuedSubmissionId&&!t?.has(a.id),canPromote:!a.externalQueuedSubmissionId&&!t?.has(a.id)&&i}));return{...b(E()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(_),controllablePending:s.filter(a=>v(a)&&!a.externalQueuedSubmissionId&&!t?.has(a.id)).map(_),visiblePending:n}}isImmediateSendAvailable(e){const s=this.opts.getRuntime(),t=s.sessions.get(e);return t?.currentRunId?t.journalRunId?s.runJournal?.get(e,t.journalRunId)?.deliveryMode==="direct":!1:!0}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let a=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],a=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...b(E()),...r});u.origin="room",u.managedRoom=d.context,u.trigger={kind:"room_activation",...d.context};const l=t.sessions[r.sessionId]??[],g=Q(l,u),f=u.clientMessageId??u.id,k=x(r.sessionId,f);if(!g&&k.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,n.set(`${u.sessionId}\0${f}`,u),i.add(r.sessionId),a=!0;else if(g&&k.state==="not_found")n.set(`${g.sessionId}\0${f}`,g),i.add(r.sessionId);else if(g&&k.state==="queued")i.add(r.sessionId);else if(g){const M=l.filter(S=>S.id!==g.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],a=!0}}a&&y(t);let o;for(const d of i)try{for(const r of n.values())r.sessionId===d&&(N(d,R(r),"queued"),$({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:T(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:m().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(r){o=r,this.logLifecycle("enqueue.commit-failed",d,{failureClass:W(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(i.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((i.managerEventId||i.managerEventRoomOrigin||i.managedWorkerParentSessionId||i.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(te(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=L(i.managerEventId?{roomOrigin:i.managerEventRoomOrigin}:null,!!i.managedAgentDirect),o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId,a):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const d=o?z().find(H=>H.sessionId===i.sessionId):null,r=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?d?.agentSessionId&&!d.agentSessionId.startsWith("managed:")?d.agentSessionId:null:o.agentSessionId,modelId:d?.modelId??void 0,systemPrompt:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,u=t.resolvePath(r.workDir),l=K(r.attachments);l?.length&&j(u,l);const g={text:r.text,attachments:l,localized:!1},f=t.sessions.get(r.sessionId);if(r.agentType==="codex"&&r.agentSessionId&&f?.currentRunId?.startsWith("dispatch:")&&ee(r.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const k=await this.isSessionBusy(r.sessionId),M=m(),S=M.sessions[r.sessionId]??[],h=G({...r,text:g.text,attachments:g.attachments,systemPrompt:o?null:r.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const q=h.clientMessageId??h.id,c=x(r.sessionId,q);if(c.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:c.state==="queued",queueMessageId:h.id,...c,queue:this.getSnapshot(r.sessionId)}}),!0;const I=Q(S,h);I||(M.sessions[r.sessionId]=[...S,h],y(M));const p=I??h;return $({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),N(p.sessionId,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",r.sessionId,{origin:r.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[r.sessionId]??[]).length}),this.broadcast(r.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:I?.id??h.id,...x(r.sessionId,I?.clientMessageId??I?.id??h.clientMessageId??h.id),queue:this.getSnapshot(r.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?this.retries.schedule(r.sessionId):this.wakeSession(r.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.findIndex(r=>r.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!v(n[a])||n[a].externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=K(t.attachments),d=s.resolvePath(n[a].workDir);o?.length&&j(d,o),n[a]={...n[a],text:t.text,attachments:o,updatedAt:A()},i.sessions[t.sessionId]=n,y(i),C(t.sessionId,R(n[a]),"queued"),n[a].attachments?.length&&O(d,n[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.find(d=>d.id===t.queueMessageId);if(a&&(!v(a)||a.externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=n.filter(d=>d.id!==t.queueMessageId);if(o.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],y(i),J(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(n=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}if(n[0].externalQueuedSubmissionId||!this.isImmediateSendAvailable(t.sessionId)){i("queue_immediate_send_unavailable");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(d=>d.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,n)){const d=n.length===1?n[0].text:P(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:ie(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:a.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:d,attachments:B(n.flatMap(l=>l.attachments??[])),clientMessageId:r})}catch(l){throw s.nativeFusion?.cancelManagedSend(r),l}for(const l of n)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,n),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId);const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):(this.removeBatch(t.sessionId,n),o="restarted")}else{const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):this.removeBatch(t.sessionId,n)}w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(d=>d.id),mode:o}})}catch(a){i(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){const s=m().sessions[e]?.find(t=>!!t.externalQueuedSubmissionId);s&&this.noteExternalClaim(e,s.clientMessageId??s.id),this.wakeSession(e)}purgeSession(e){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=m().sessions[e]?.length??0;return this.purgeSession(e),o}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(o=>o.managedRoom&&!i.has(o.id));if(n.length===0)return 0;const a=t.filter(o=>!n.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],y(s);for(const o of n)J(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:a.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const l=m().sessions[e]?.length??0,g=this.deferredLogState.get(e),f=Date.now();(!g||g.pendingCount!==l||f-g.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:l}),this.deferredLogState.set(e,{at:f,pendingCount:l}));return}this.deferredLogState.delete(e);const n=m(),a=n.sessions[e]??[],o=a.filter(l=>{const g=l.clientMessageId??l.id,f=x(e,g).state;return f==="queued"||f==="not_found"});if(o.length!==a.length&&(o.length?n.sessions[e]=o:delete n.sessions[e],y(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length})),o.some(l=>!!l.externalQueuedSubmissionId)){this.broadcast(e);return}const d=X(o);if(d.length===0){this.broadcast(e);return}i=d,this.inFlightMessageIds.set(e,new Set(d.map(l=>l.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"}),this.broadcast(e);for(const l of d)C(e,R(l),"queued");const r=await this.dispatchQueuedMessages(d);if(r.deliveryMode==="external_owner_queue"){this.markExternalQueued(d,r.queuedSubmissionId),this.logLifecycle("dispatch.external-queued",e,{batchCount:d.length,queueMessageId:d[0].id}),this.broadcast(e);return}const u=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:u}),this.deferredLogState.delete(e),s=u>0,this.broadcast(e)}catch(n){const a=W(n instanceof Error?n.message:String(n),n instanceof D?"terminal_dispatch_failed":"transient_dispatch_failed"),o=i[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof D||a==="managed_claim_source_binding_conflict"||d){const u=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:u,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e);const n=m().sessions[e]??[];n.length?(this.broadcast(e),n.some(a=>!!a.externalQueuedSubmissionId)?this.retries.cancel(e):s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t)):this.retries.cancel(e)}}noteExternalClaim(e,s){const t=m(),i=t.sessions[e]??[],n=i.filter(a=>(a.clientMessageId??a.id)!==s);n.length!==i.length&&(n.length?t.sessions[e]=n:delete t.sessions[e],y(t),this.broadcast(e))}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(a=>a.id)),n=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return n.length?t.sessions[e]=n:delete t.sessions[e],y(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:A(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new D("schedule_target_unavailable");const i=e.flatMap(c=>{if(c.trigger?.kind==="room_activation"){const{kind:I,...p}=c.trigger;return[p]}return!c.trigger&&c.managedRoom?[c.managedRoom]:[]}),n=e.find(c=>c.trigger?.kind==="manager_event")?.trigger,a=e.some(c=>c.trigger?.kind==="managed_direct"||!!c.managedAgentDirect),o=a||!!t?.bindingId,d=e.find(c=>c.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||a,u=i.length>0?await this.prepareManagedRoomTurn?.(i):o||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,L(n?.kind==="manager_event"?n:null,a)):void 0;if(i.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!u)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const l=new Set((m().sessions[s.sessionId]??[]).map(c=>c.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(c=>!l.has(c.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new D("queued_dispatch_cancelled");const g=u??null,f=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:P(e),M=B(e.flatMap(c=>c.attachments??[])),S=o||n?.kind==="manager_event"&&!!u,h=S?u.agentSessionId?.startsWith("managed:")?T(this.opts.getRuntime(),s):u.agentSessionId:i.length>0?T(this.opts.getRuntime(),s):s.agentSessionId??null,q=u?z().find(c=>c.sessionId===s.sessionId):null;return await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...b(E()),sessionId:s.sessionId,text:k,agentType:S?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?f:s.managerConfig,workDir:S?u.workDir:s.workDir,agentSessionId:h,modelId:F(q?.modelId)??F(s.modelId),systemPrompt:u?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:g?g.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:g?g.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:M,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:u?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":o?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:d?.kind==="manager_worker"?d.parentSessionId:void 0,managerWorkerRoomOrigin:d?.kind==="manager_worker"?d.roomOrigin:void 0,managerWorkerNativeFull:d?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isExternalWorkerController(d.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(c=>{const I=_(c);return r?{...I,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:I}),waitForDispatch:!0}},u??void 0)??{}}markExternalQueued(e,s){const t=m(),i=new Set(e.map(o=>o.id)),n=t.sessions[e[0].sessionId]??[];let a=!1;for(const o of n)i.has(o.id)&&(o.externalQueuedSubmissionId=s||o.id,o.updatedAt=A(),a=!0);a&&y(t)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{Re as ChatQueueManager,qe as TerminalQueuedChatDispatchError,xe as purgeQueuedSessionData};
|
|
1
|
+
import{NativeSourceDeletionStore as U}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as V}from"./queue-retry.js";import{findDuplicateQueuedMessage as Q,stripLocalQueueFields as _,takeCompatibleBatch as X,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as F,isControllableQueueMessage as v,takeControllableMessage as Y,normalizeQueuedModelId as W,resolveManagedRoomNativeSessionId as C,managedDirectTurnOptions as L}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as Z}from"@shennian/wire";import{isCodexThreadTailInterrupted as ee}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as te}from"./projection.js";import{assertFinishedAttachmentReferences as j,markAttachmentReferences as O}from"./attachment-transfer-store.js";import{appendMessage as N,getMessageDeliveryStatus as x,listSessionRecords as z,recordSession as $,tombstoneMessage as J,upsertMessage as T}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{clearPersistedManagerRoomOrigins as se,purgeQueuedSessionData as ne,readQueue as m,writeQueue as y}from"./queue-storage.js";import{getNativeSourceAgentType as ie}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as K}from"./attachment-reference.js";import{getPersonalSyncIdentity as b}from"../personal-sync/epoch.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{isTrustedManagerEvent as ae}from"./manager-event-authority.js";import{TerminalQueuedChatDispatchError as D,nowIso as E,queueEnvelope as R,queueMessageFromParams as G}from"./queue-message.js";import{purgeQueuedSessionData as xe}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as qe}from"./queue-message.js";class Re{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new V(e=>{this.wakeSession(e)});deletions=new U;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,se(),setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=m().sessions[e]??[],t=this.inFlightMessageIds.get(e),i=this.isImmediateSendAvailable(e),n=s.filter(a=>v(a)).map(a=>({..._(a),pendingState:a.externalQueuedSubmissionId?"provider_queued":t?.has(a.id)?"submitting":"waiting",canDelete:!a.externalQueuedSubmissionId&&!t?.has(a.id),canPromote:!a.externalQueuedSubmissionId&&!t?.has(a.id)&&i}));return{...b(A()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(_),controllablePending:s.filter(a=>v(a)&&!a.externalQueuedSubmissionId&&!t?.has(a.id)).map(_),visiblePending:n}}isImmediateSendAvailable(e){const s=this.opts.getRuntime(),t=s.sessions.get(e);return t?.currentRunId?t.journalRunId?s.runJournal?.get(e,t.journalRunId)?.deliveryMode==="direct":!1:!0}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=m(),i=new Set,n=new Map;let a=!1;for(const d of e){const r=d.params;if(this.deletions.hasSession(r.sessionId)){delete t.sessions[r.sessionId],a=!0;continue}if(!r.sessionId||!r.text||!r.agentType||!r.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=G({...b(A()),...r});typeof r.roomMessageCreatedAt=="string"&&Number.isFinite(Date.parse(r.roomMessageCreatedAt))&&(u.createdAt=new Date(r.roomMessageCreatedAt).toISOString()),u.origin="room",u.managedRoom=d.context,u.trigger={kind:"room_activation",...d.context};const l=t.sessions[r.sessionId]??[],g=Q(l,u),f=u.clientMessageId??u.id,k=x(r.sessionId,f);if(!g&&k.state==="not_found")l.push(u),t.sessions[r.sessionId]=l,n.set(`${u.sessionId}\0${f}`,u),i.add(r.sessionId),a=!0;else if(g&&k.state==="not_found")n.set(`${g.sessionId}\0${f}`,g),i.add(r.sessionId);else if(g&&k.state==="queued")i.add(r.sessionId);else if(g){const M=l.filter(S=>S.id!==g.id);M.length?t.sessions[r.sessionId]=M:delete t.sessions[r.sessionId],a=!0}}a&&y(t);let o;for(const d of i)try{for(const r of n.values())r.sessionId===d&&(N(d,R(r),"queued"),$({sessionId:d,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:C(s,r),modelId:r.modelId}));this.logLifecycle("enqueue.persisted",d,{origin:"room",pendingCount:m().sessions[d]?.length??0}),this.broadcast(d),this.wakeSession(d)}catch(r){o=r,this.logLifecycle("enqueue.commit-failed",d,{failureClass:F(r instanceof Error?r.message:"","canonical_commit_failed")},"error"),this.retries.schedule(d,!0)}if(i.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),i=e.params;if(i.scheduledTask&&!s)throw new Error("schedule_source_untrusted");if((i.managerEventId||i.managerEventRoomOrigin||i.managedWorkerParentSessionId||i.managerWorkerRoomOrigin||e.params.managerWorkerNativeFull||e.params.managedRecoverySource)&&!ae(e))throw new Error("manager_event_source_untrusted");const n=!!(s&&i.scheduledTask?.bindingId);if(te(i.sessionListProjection),!i.sessionId||!i.text||!i.agentType||!i.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=L(i.managerEventId?{roomOrigin:i.managerEventRoomOrigin}:null,!!i.managedAgentDirect),o=i.managedAgentDirect||n?await this.prepareManagedAgentDirectTurn?.(i.sessionId,a):null;if((i.managedAgentDirect||n)&&!o)throw new Error("managed_agent_context_unavailable");const d=o?z().find(H=>H.sessionId===i.sessionId):null,r=o?{...i,...o,agentType:o.agentType,sessionMode:o.sessionMode,managerConfig:o.managerConfig,workDir:o.workDir,agentSessionId:o.agentSessionId?.startsWith("managed:")?d?.agentSessionId&&!d.agentSessionId.startsWith("managed:")?d.agentSessionId:null:o.agentSessionId,modelId:d?.modelId??void 0,systemPrompt:o.systemPrompt,externalChannel:i.managedAgentDirect?null:i.externalChannel,replyTarget:i.managedAgentDirect?null:i.replyTarget,managerDefaultWorkerAgentType:o.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:o.managerDefaultWorkerModelId}:i,u=t.resolvePath(r.workDir),l=K(r.attachments);l?.length&&j(u,l);const g={text:r.text,attachments:l,localized:!1},f=t.sessions.get(r.sessionId);if(r.agentType==="codex"&&r.agentSessionId&&f?.currentRunId?.startsWith("dispatch:")&&ee(r.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:Z}),!1;o?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(o.managedRoomContextToken);const k=await this.isSessionBusy(r.sessionId),M=m(),S=M.sessions[r.sessionId]??[],h=G({...r,text:g.text,attachments:g.attachments,systemPrompt:o?null:r.systemPrompt});o&&(i.managedAgentDirect&&(h.managedAgentDirect=!0),h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const q=h.clientMessageId??h.id,c=x(r.sessionId,q);if(c.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:c.state==="queued",queueMessageId:h.id,...c,queue:this.getSnapshot(r.sessionId)}}),!0;const I=Q(S,h);I||(M.sessions[r.sessionId]=[...S,h],y(M));const p=I??h;return $({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),N(p.sessionId,R(p),"queued"),O(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",r.sessionId,{origin:r.origin??"app",queueMessageId:p.id,pendingCount:(m().sessions[r.sessionId]??[]).length}),this.broadcast(r.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:I?.id??h.id,...x(r.sessionId,I?.clientMessageId??I?.id??h.clientMessageId??h.id),queue:this.getSnapshot(r.sessionId),...g.localized?{localizedAttachments:!0}:{}}}),k?this.retries.schedule(r.sessionId):this.wakeSession(r.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.findIndex(r=>r.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!v(n[a])||n[a].externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=K(t.attachments),d=s.resolvePath(n[a].workDir);o?.length&&j(d,o),n[a]={...n[a],text:t.text,attachments:o,updatedAt:E()},i.sessions[t.sessionId]=n,y(i),T(t.sessionId,R(n[a]),"queued"),n[a].attachments?.length&&O(d,n[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const i=m(),n=i.sessions[t.sessionId]??[],a=n.find(d=>d.id===t.queueMessageId);if(a&&(!v(a)||a.externalQueuedSubmissionId||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=n.filter(d=>d.id!==t.queueMessageId);if(o.length===n.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?i.sessions[t.sessionId]=o:delete i.sessions[t.sessionId],y(i),J(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,i=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){i("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){i("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let n=[];try{for(;this.draining.has(t.sessionId);)await new Promise(d=>setTimeout(d,5));if(n=Y(m().sessions[t.sessionId]??[],t.queueMessageId),!n.length){i("Queued message not found");return}if(n[0].externalQueuedSubmissionId||!this.isImmediateSendAvailable(t.sessionId)){i("queue_immediate_send_unavailable");return}this.inFlightMessageIds.set(t.sessionId,new Set(n.map(d=>d.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,n)){const d=n.length===1?n[0].text:P(n),r=n[0].clientMessageId??n[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:n[0].agentType,sourceAgentType:ie(n[0].agentType,n[0].modelId),canonicalMessageId:r,sourceSessionKey:a.agentSessionId,text:d,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:d,attachments:B(n.flatMap(l=>l.attachments??[])),clientMessageId:r})}catch(l){throw s.nativeFusion?.cancelManagedSend(r),l}for(const l of n)T(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,n),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId);const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):(this.removeBatch(t.sessionId,n),o="restarted")}else{const d=await this.dispatchQueuedMessages(n);d.deliveryMode==="external_owner_queue"?this.markExternalQueued(n,d.queuedSubmissionId):this.removeBatch(t.sessionId,n)}w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:n.map(d=>d.id),mode:o}})}catch(a){i(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),m().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){const s=m().sessions[e]?.find(t=>!!t.externalQueuedSubmissionId);s&&this.noteExternalClaim(e,s.clientMessageId??s.id),this.wakeSession(e)}purgeSession(e){ne(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=m().sessions[e]?.length??0;return this.purgeSession(e),o}const s=m(),t=s.sessions[e]??[],i=this.inFlightMessageIds.get(e)??new Set,n=t.filter(o=>o.managedRoom&&!i.has(o.id));if(n.length===0)return 0;const a=t.filter(o=>!n.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],y(s);for(const o of n)J(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:n.length,pendingCount:a.length}),this.broadcast(e),n.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=m();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,i=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const l=m().sessions[e]?.length??0,g=this.deferredLogState.get(e),f=Date.now();(!g||g.pendingCount!==l||f-g.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:l}),this.deferredLogState.set(e,{at:f,pendingCount:l}));return}this.deferredLogState.delete(e);const n=m(),a=n.sessions[e]??[],o=a.filter(l=>{const g=l.clientMessageId??l.id,f=x(e,g).state;return f==="queued"||f==="not_found"});if(o.length!==a.length&&(o.length?n.sessions[e]=o:delete n.sessions[e],y(n),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length})),o.some(l=>!!l.externalQueuedSubmissionId)){this.broadcast(e);return}const d=X(o);if(d.length===0){this.broadcast(e);return}i=d,this.inFlightMessageIds.set(e,new Set(d.map(l=>l.id))),this.logLifecycle("dispatch.start",e,{batchCount:d.length,queueMessageId:d[0].id,origin:d[0].origin??"app"}),this.broadcast(e);for(const l of d)T(e,R(l),"queued");const r=await this.dispatchQueuedMessages(d);if(r.deliveryMode==="external_owner_queue"){this.markExternalQueued(d,r.queuedSubmissionId),this.logLifecycle("dispatch.external-queued",e,{batchCount:d.length,queueMessageId:d[0].id}),this.broadcast(e);return}const u=this.removeBatch(e,d);this.logLifecycle("dispatch.accepted",e,{batchCount:d.length,queueMessageId:d[0].id,pendingCount:u}),this.deferredLogState.delete(e),s=u>0,this.broadcast(e)}catch(n){const a=F(n instanceof Error?n.message:String(n),n instanceof D?"terminal_dispatch_failed":"transient_dispatch_failed"),o=i[0],d=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(n instanceof D||a==="managed_claim_source_binding_conflict"||d){const u=this.removeBatch(e,i);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:i.length,queueMessageId:i[0]?.id,pendingCount:u,failureClass:d?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e);const n=m().sessions[e]??[];n.length?(this.broadcast(e),n.some(a=>!!a.externalQueuedSubmissionId)?this.retries.cancel(e):s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t)):this.retries.cancel(e)}}noteExternalClaim(e,s){const t=m(),i=t.sessions[e]??[],n=i.filter(a=>(a.clientMessageId??a.id)!==s);n.length!==i.length&&(n.length?t.sessions[e]=n:delete t.sessions[e],y(t),this.broadcast(e))}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),i=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(i&&s[0].systemPrompt==null&&t&&t.source===i&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return m().sessions[e]?.length??0;const t=m(),i=new Set(s.map(a=>a.id)),n=(t.sessions[e]??[]).filter(a=>!i.has(a.id));return n.length?t.sessions[e]=n:delete t.sessions[e],y(t),n.length}logLifecycle(e,s,t,i="info"){const n=`[chat.queue] ${JSON.stringify({at:E(),event:e,sessionId:s,...t})}`;i==="error"?console.error(n):console.info(n)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new D("schedule_target_unavailable");const i=e.flatMap(c=>{if(c.trigger?.kind==="room_activation"){const{kind:I,...p}=c.trigger;return[p]}return!c.trigger&&c.managedRoom?[c.managedRoom]:[]}),n=e.find(c=>c.trigger?.kind==="manager_event")?.trigger,a=e.some(c=>c.trigger?.kind==="managed_direct"||!!c.managedAgentDirect),o=a||!!t?.bindingId,d=e.find(c=>c.trigger?.kind==="manager_worker")?.trigger,r=n?.kind==="manager_event"||a,u=i.length>0?await this.prepareManagedRoomTurn?.(i):o||n?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId,L(n?.kind==="manager_event"?n:null,a)):void 0;if(i.length>0&&!u)throw new Error("managed_room_turn_preparer_unavailable");if(o&&!u)throw new Error("managed_agent_context_unavailable");if(n?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!u)throw new Error("managed_agent_context_unavailable");const l=new Set((m().sessions[s.sessionId]??[]).map(c=>c.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(c=>!l.has(c.id)))throw u?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(u.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new D("queued_dispatch_cancelled");const g=u??null,f=g?.sessionMode==="manager"?g.managerConfig:null,k=e.length===1?s.text:P(e),M=B(e.flatMap(c=>c.attachments??[])),S=o||n?.kind==="manager_event"&&!!u,h=S?u.agentSessionId?.startsWith("managed:")?C(this.opts.getRuntime(),s):u.agentSessionId:i.length>0?C(this.opts.getRuntime(),s):s.agentSessionId??null,q=u?z().find(c=>c.sessionId===s.sessionId):null;return await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...b(A()),sessionId:s.sessionId,text:k,agentType:S?u.agentType:s.agentType,sessionMode:g?.sessionMode??s.sessionMode,managerConfig:g?f:s.managerConfig,workDir:S?u.workDir:s.workDir,agentSessionId:h,modelId:W(q?.modelId)??W(s.modelId),systemPrompt:u?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:g?g.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:g?g.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:M,externalChannel:r?void 0:s.externalChannel,replyTarget:r?void 0:s.replyTarget,roomContextRegistryKey:r?void 0:u?.roomContextRegistryKey,activationCauseMessageId:r?void 0:i.at(-1)?.causeMessageId,rootCauseMessageId:r?void 0:i.at(-1)?.rootCauseMessageId,activationSequence:r?void 0:i.at(-1)?.sequence,activationHopCount:r?void 0:i.at(-1)?.hopCount,managedRecoverySource:n?.kind==="manager_event"?"manager_event":o?"managed_direct":i.length>0?"managed_room":void 0,managerEventId:n?.kind==="manager_event"?n.eventId:void 0,managedWorkerParentSessionId:d?.kind==="manager_worker"?d.parentSessionId:void 0,managerWorkerRoomOrigin:d?.kind==="manager_worker"?d.roomOrigin:void 0,managerWorkerNativeFull:d?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isExternalWorkerController(d.parentSessionId,s.workDir)||void 0,managedRoomContextToken:u?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:i.length>0||!!t||void 0,queuedBatchItems:e.map(c=>{const I=_(c);return r?{...I,systemPrompt:u?.systemPrompt??null,externalChannel:null,replyTarget:null}:I}),waitForDispatch:!0}},u??void 0)??{}}markExternalQueued(e,s){const t=m(),i=new Set(e.map(o=>o.id)),n=t.sessions[e[0].sessionId]??[];let a=!1;for(const o of n)i.has(o.id)&&(o.externalQueuedSubmissionId=s||o.id,o.updatedAt=E(),a=!0);a&&y(t)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const i=await t.adapter.getStatus().catch(()=>null);return i?.active?(i.runId&&(t.currentRunId=i.runId,t.currentRunPhase=i.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{Re as ChatQueueManager,qe as TerminalQueuedChatDispatchError,xe as purgeQueuedSessionData};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export type ReqMethod = 'schedule.deliver'
|
|
1
|
+
export type ReqMethod = 'schedule.deliver'
|
|
2
|
+
/** Server-originated durable delivery into a managed Room session queue. */
|
|
3
|
+
| 'room.message.deliver' | 'chat.send' | 'chat.enqueue' | 'chat.queue.get' | 'chat.queue.edit' | 'chat.queue.delete' | 'chat.queue.promote' | 'chat.abort' | 'session.resume' | 'session.history' | 'session.history.open' | 'session.history.sync' | 'session.history.page' | 'session.history.close' | 'session.body.replica.subscribe' | 'session.body.replica.unsubscribe' | 'session.index.update' | 'session.deletions.sync' | 'session.deletions.ack' | 'session.message.status' | 'voice.session.body.window' | 'session.refresh' | 'session.tool.detail' | 'session.title.set' | 'session.title.generate' | 'fs.ls' | 'fs.read' | 'fs.write' | 'fs.export.markdown-pdf' | 'fs.export.markdown-pdf.setup' | 'fs.archive.zip' | 'fs.transfer.start' | 'fs.transfer.chunk' | 'fs.transfer.finish' | 'fs.transfer.abort' | 'fs.rename' | 'project.fs.open' | 'project.fs.list' | 'project.fs.read' | 'project.fs.close'
|
|
2
4
|
/** Server-originated per-connection challenge; never accepted from an App client. */
|
|
3
5
|
| 'project.fs.negotiate' | 'skill.list' | 'skill.install' | 'skill.doctor' | 'skill.setup' | 'skill.use' | 'region.probe' | 'region.switch' | 'upgrade.start' | 'upgrade.status' | 'upgrade.set-policy' | 'machine.remote-access.pause' | 'agents.refresh' | 'models.refresh' | 'agent.config.get' | 'agent.config.upsert' | 'agent.config.clear' | 'agent.config.test' | 'agent.workspace.validate' | 'agent.capabilities.list' | 'agent.permission.respond' | 'agent.auth.start' | 'agent.auth.logout' | 'agent.session-options.get' | 'agent.external-sessions.list' | 'agent.external-sessions.import' | 'agent.external-sessions.close' | 'agent.external-sessions.delete' | 'manager.channel.get' | 'manager.channel.upsert' | 'session.wechat-rpa.channel.get' | 'session.wechat-rpa.channel.upsert' | 'session.wechat-rpa.channel.sync' | 'session.wechat-rpa.outbound.cancel' | 'manager.wechat-rpa.channel.get' | 'manager.wechat-rpa.channel.upsert' | 'manager.wechat-rpa.channel.sync' | 'manager.wechat-rpa.outbound.cancel' | 'wechat-rpa.helper.runtime' | 'wecom.send' | 'external.send' | 'room.sync' | 'room.send' | 'room.bind' | 'room.agent.bind-managed' | 'room.agent.restart-managed' | 'room.unbind';
|
|
@@ -35,6 +35,8 @@ export type RoomSummary = {
|
|
|
35
35
|
humanCount: number;
|
|
36
36
|
agentCount: number;
|
|
37
37
|
lastSequence: RoomSequence;
|
|
38
|
+
/** First sequence still visible to this account. Older servers may omit it. */
|
|
39
|
+
historyFromSequence?: RoomSequence;
|
|
38
40
|
lastReadSequence?: RoomSequence;
|
|
39
41
|
unreadCount?: number;
|
|
40
42
|
membershipRole?: RoomMembershipRole;
|
|
@@ -23,8 +23,8 @@ export declare const SESSION_SYNC_REQUIRED_CAPABILITIES: readonly ["session_inde
|
|
|
23
23
|
* epoch-wide minimum gate. This lets the App roll out background replication
|
|
24
24
|
* while old daemons keep serving the compatible visible-session snapshot path.
|
|
25
25
|
*/
|
|
26
|
-
export declare const SESSION_SYNC_ADDITIVE_CAPABILITIES: readonly ["session_body_replica_v1", "session_body_replica_stream_v1", "active_run_recovery_v1", "external_updater_v1"];
|
|
27
|
-
export declare const SESSION_SYNC_ADVERTISED_CAPABILITIES: readonly ["session_index_v1", "session_body_revision_v1", "session_history_subscription_v1", "local_canonical_v1", "session_title_authority_v1", "session_title_seed_v1", "session_tool_detail_v1", "voice_session_body_relay_v1", "native_source_tombstone_v1", "session_durable_send_v1", "daemon_installation_identity_v1", "session_user_boundary_v1", "session_terminal_outbox_v1", "session_list_preview_v1", "session_attention_v1", "session_data_epoch_v2", "session_structured_rejection_v2", "machine_personal_authority_v2", "session_body_replica_v1", "session_body_replica_stream_v1", "active_run_recovery_v1", "external_updater_v1"];
|
|
26
|
+
export declare const SESSION_SYNC_ADDITIVE_CAPABILITIES: readonly ["session_body_replica_v1", "session_body_replica_stream_v1", "active_run_recovery_v1", "external_updater_v1", "room_message_delivery_v1"];
|
|
27
|
+
export declare const SESSION_SYNC_ADVERTISED_CAPABILITIES: readonly ["session_index_v1", "session_body_revision_v1", "session_history_subscription_v1", "local_canonical_v1", "session_title_authority_v1", "session_title_seed_v1", "session_tool_detail_v1", "voice_session_body_relay_v1", "native_source_tombstone_v1", "session_durable_send_v1", "daemon_installation_identity_v1", "session_user_boundary_v1", "session_terminal_outbox_v1", "session_list_preview_v1", "session_attention_v1", "session_data_epoch_v2", "session_structured_rejection_v2", "machine_personal_authority_v2", "session_body_replica_v1", "session_body_replica_stream_v1", "active_run_recovery_v1", "external_updater_v1", "room_message_delivery_v1"];
|
|
28
28
|
export declare const SESSION_SYNC_EVENT_NAMES: readonly ["session.index.updated", "session.body.delta", "session.body.replica.delta", "session.body.replica.error"];
|
|
29
29
|
export declare const SESSION_HISTORY_LIMITS: {
|
|
30
30
|
readonly defaultSnapshotMessages: 100;
|
|
@@ -41,6 +41,7 @@ export const SESSION_SYNC_ADDITIVE_CAPABILITIES = [
|
|
|
41
41
|
'session_body_replica_stream_v1',
|
|
42
42
|
'active_run_recovery_v1',
|
|
43
43
|
'external_updater_v1',
|
|
44
|
+
'room_message_delivery_v1',
|
|
44
45
|
];
|
|
45
46
|
export const SESSION_SYNC_ADVERTISED_CAPABILITIES = [
|
|
46
47
|
...SESSION_SYNC_REQUIRED_CAPABILITIES,
|