shennian 0.5.12 → 0.5.13
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 +75 -35
- package/dist/src/agents/acp/adapter.js +2 -2
- package/dist/src/agents/claude.js +7 -7
- package/dist/src/agents/codex.js +3 -3
- package/dist/src/agents/command-spec.js +3 -3
- package/dist/src/agents/cursor-managed-permissions.js +2 -2
- package/dist/src/agents/cursor.js +1 -1
- package/dist/src/agents/custom.js +5 -5
- package/dist/src/agents/opencode-managed-permissions.js +2 -2
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/pi-coding-agent.js +3 -3
- package/dist/src/agents/public-agent-client.js +1 -1
- package/dist/src/agents/public-delivery-client.js +1 -0
- package/dist/src/agents/public-delivery-worker.js +1 -0
- package/dist/src/agents/public-reply-collector.js +1 -0
- package/dist/src/agents/public-turn-runner.js +3 -0
- package/dist/src/agents/public-workdir.js +1 -0
- package/dist/src/agents/workbuddy-managed-permissions.js +2 -2
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/commands/agent-workspace.js +3 -3
- package/dist/src/index.js +3 -3
- package/dist/src/native-fusion/local-repository.js +2 -2
- package/dist/src/native-fusion/test-workspace.js +1 -0
- package/dist/src/security/macos-file-sandbox.js +3 -0
- package/dist/src/security/managed-agent-policy.js +1 -1
- package/dist/src/security/public-agent-policy.js +1 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.js +1 -1
- package/package.json +8 -1
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as
|
|
2
|
-
`),process.exitCode=1}function on(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 be;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(T),i.enablePositionalOptions(),Xe(i),eo(i),oo(i),we(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",ke.hostname()).action(async a=>{const s=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Ne(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=z("env.json");try{const e=JSON.parse(q.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);pe()}catch{}Ue(re(process.env,p()));const g=wo();se()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const c=Oe(),w=fo({lockPath:Zo,instanceId:c});w.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(w.ownerPid?` (PID ${w.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=Xo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),w.release(),process.exit(0)}catch{}}catch{}const Q=je(process.env,ve)?[]:$e(process.pid);Q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${Q[0]}), skipping duplicate start`),w.release(),process.exit(0)),de(process.pid,c,{version:T}),le(process.pid,void 0,c),process.on("exit",()=>{ae(process.pid,c),ie(c)}),process.on("exit",w.release);let L=null;try{L=mo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const U=await so();if(U){L||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{po({fromVersion:T,targetVersion:U.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:U.jobId},{readiness:L}),console.error(`[upgrade] New version crashed ${U.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??J.cn.url,h=me(),l=await uo({serverUrl:u,authToken:m.machineToken??m.accessToken}),C=l.map(e=>e.type),j=V(l),f=new To,M=new Mo,R=new Fo(f),S=new Do(u),Se=new Ho({actionClient:S}),ye=new Bo({actionClient:S});let y=null,r;const Y=Go({authority:R,actionClient:S,attachmentCache:Se,attachmentUploader:ye,observations:M,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>te({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),R.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let H=null,B=null,F=null,G=null,Z=!1;const b=await ge({paths:Co(),handlers:{...bo({daemonVersion:h,protocolVersion:Eo,getStatus:()=>ce(),getAgents:()=>l,requestShutdown:()=>{G?G():Z=!0},completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),bindManagedRoom:e=>r.bindManagedRoomFromLocal(e),startUpgrade:async e=>{if(!H)throw new Error("upgrade_unavailable");return H(e)},prepareExternalUpgrade:e=>{if(!B)throw new Error("external_upgrade_unavailable");return B(e)},cancelExternalUpgrade:()=>{if(!F)throw new Error("external_upgrade_unavailable");return F()}}),...ko({getStatus:()=>ce(),getSnapshot:()=>V(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 Po(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=V(l);return d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(W=>W.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return Ao(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let k;try{k=await ge({paths:Ro(),handlers:{"managed-room.execute":e=>Y(e),"schedule.personal":async e=>{const n=e,o=Ce(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await te({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let X=!1;const _=()=>{X||(X=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",_),process.once("SIGTERM",_),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),k.close()]),process.exitCode=1;return}process.removeListener("SIGINT",_),process.removeListener("SIGTERM",_);const ee=`${en(u)}/relay/machine`,N=m.machineToken;if(!N)return;try{await zo({serverUrl:u,machineToken:N,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&&vo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${ee}... (v${h}) agents: ${C.join(",")}`),D({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:C}});let P=null,A=null,x=0,v=co();const oe=Te(m.autoUpgrade),d=new De({serverUrl:ee,machineToken:N,cliVersion:h,agentList:C,autoUpgradePolicy:oe,sessionSyncProtocolVersion:ue,sessionSyncCapabilities:fe,daemonInstallationId:Wo(),dataEpoch:he,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),D({level:"info",wsEvent:"daemon.connected"}),j.some(o=>o.models.length>0)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:j}}),ho(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(j)&&d.sendEvent({type:"event",event:"machine.agents",payload:{agentList:C,agents:o}})}).catch(()=>{}),v&&d.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),O.start(),A?.start();const e=++x,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&go(o.jobId,o.to),io(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),Io({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"&&P?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===x&&d.getState()==="connected"){if(o instanceof Qo)return;const t=o instanceof Ko?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{x+=1,r?.revokeProjectFsHandles(),A?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${on(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"){No(d,A,e);return}r.handleReq(e)}});P=So()?new yo(d):null,A=new _o({registry:f,observations:M,actionClient:S,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new xo().hasSession(e),onMessages:async(e,n,o,t)=>{await y?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(R.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const O=new lo({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:ue,dataEpoch:he,advertisedCapabilities:fe,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})}});O.setPolicy(oe),r=new no(d,P,h,O,new Uo({registry:f,observations:M,bindingClient:S,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>Ve(),isDisabled:()=>se(),shutdownAfterResponse:()=>{setTimeout(()=>{Le(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>R.revokeToken(e),Y,null,to(N),()=>V(l)),H=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 ze(d,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:W=>r.prepareForUpgrade(W),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},B=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)}},F=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),y=new Vo({registry:f,observations:M,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await S.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!$o(f,e)||!Oo(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return R.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new jo,resolveManagedAgentPolicy:e=>Lo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Re({serverUrl:u,machineToken:p().machineToken,req:e,client:d,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await S.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>y.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>y.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?y.prepareDirectConversationTurn(e.sessionId,qo):e.source==="manager_event"?y.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:y.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),q.mkdirSync(ve,{recursive:!0}),de(process.pid,c,{version:h}),le(process.pid,void 0,c),d.connect(),process.stdin.resume();let ne=!1;const $=async()=>{if(!ne){ne=!0,console.log(E.gray(`
|
|
3
|
-
Disconnecting...`)),
|
|
1
|
+
import{registerScheduleCommand as ke}from"./commands/schedule.js";import{resolvePersonalScheduleContext as be}from"./schedules/personal-context.js";import{executeServerSchedule as re,deliverServerSchedule as Ae}from"./schedules/server-client.js";import{Command as Pe}from"commander";import T from"chalk";import Ee from"node:os";import{randomUUID as Te}from"node:crypto";import z from"node:fs";import{loadConfig as p,saveConfig as De,getConfigPath as Ue,getShennianDir as Me,resolveShennianPath as J,normalizeAutoUpgradePolicy as _e}from"./config/index.js";import{CliRelayClient as Ne}from"./relay/client.js";import{collectKnownLogSecrets as ae,installCliLogRedaction as xe,sanitizeCliLogValue as Oe}from"./log-redaction.js";import{registerPairCommand as $e,runSmartStart as Le}from"./commands/pair.js";import{registerLogoutCommand as Ve}from"./commands/logout.js";import{clearDaemonPidIfOwner as ie,createDaemonInstanceId as je,clearDaemonLauncher as se,findRunningDaemonProcessIds as He,isRemoteAccessDisabled as ce,prepareRemoteAccessPause as Be,stopPreparedRemoteAccessService as Fe,isIsolatedDaemonE2eInstance as We,getDaemonStatus as de,registerDaemonCommand as Ge,writeDaemonPid as le,writeDaemonLauncher as me}from"./commands/daemon.js";import{registerRuntimeCommand as qe}from"./commands/runtime.js";import{registerAdapterCommand as ze}from"./commands/adapter.js";import{registerAgentCommand as Je}from"./commands/agent.js";import{registerManagerCommand as Ke}from"./commands/manager.js";import{registerWorkerCommand as Qe}from"./commands/worker.js";import{handleUpgradeStart as Ye,registerUpgradeCommand as Ze}from"./commands/upgrade.js";import{registerToolsCommand as Xe}from"./commands/tools.js";import{registerQueueCommand as eo}from"./commands/queue.js";import{registerSkillCommand as oo}from"./commands/skill.js";import{registerCommandReference as no}from"./commands/command-reference.js";import{registerIntegrationCommand as to}from"./commands/integration.js";import{registerRoomCommand as ro}from"./commands/room.js";import{registerSupportCommand as ao}from"./commands/support.js";import{SessionManager as io}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as so}from"./session/handlers/project-fs.js";import{SERVERS as K,regionToUrl as co,urlToRegion as lo}from"./region.js";import{clearUpgradeAttempt as mo,getCurrentVersion as pe,handleStartupCrashCheck as po,readUpgradeAttempt as go}from"./upgrade/engine.js";import{AutoUpgradeScheduler as uo}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as fo}from"./upgrade/updater-bootstrap.js";import{launchUpdater as ho,markUpdaterHealthy as vo}from"./upgrade/updater-launcher.js";import{detectAgents as ge}from"./agents/detect.js";import{PublicAgentDeliveryWorker as Io}from"./agents/public-delivery-worker.js";import{augmentProcessPath as ue}from"./env-path.js";import{acquireDaemonInstanceLock as So}from"./commands/daemon-instance-lock.js";ue();const D=pe();import{getCachedAgentInfos as V,resolveAgentInfos as yo}from"./agents/model-registry.js";import{initCliLogReporter as wo,reportLog as U,reportUpgradeAggregateMetric as Co}from"./log-reporter.js";import{isNativeFusionEnabled as Ro}from"./native-fusion/config.js";import{NativeSessionFusionService as ko}from"./native-fusion/service.js";import{startDaemonLogRetention as bo}from"./daemon-log.js";import{startDaemonIpcServer as fe}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as Ao,resolveManagedRoomIpcPaths as Po}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as Eo}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as To}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as he}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as Do}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as Uo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as Mo}from"./room/session-registry.js";import{HttpRoomBindingClient as _o}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as No}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as xo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as Oo}from"./room/mirror-sync-service.js";import{handleLiveRoomDelivery as $o}from"./room/live-delivery.js";import{NativeSourceDeletionStore as Lo}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as Vo,isCurrentRegistration as jo}from"./room/registration-authority.js";import{RoomActivationService as Ho,resolveManagedAgentPolicy as Bo}from"./room/activation-service.js";import{RoomActivationAudit as Fo}from"./room/activation-audit.js";import{RoomAttachmentCache as Wo}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Go}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as qo}from"./room/managed-context.js";import{createManagedRoomExecutor as zo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ve,SESSION_SYNC_ADVERTISED_CAPABILITIES as Ie}from"@shennian/wire";import{getDaemonInstallationId as Jo}from"./session/store.js";import{OWNER_DIRECT_TURN_OPTIONS as Ko}from"./session/queue-helpers.js";import{synchronizeMachinePersonalAuthority as Qo}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as Se,ensurePersonalDataEpoch as Yo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as Zo,DeletionSyncTerminalError as Xo}from"./session/deletion-sync-state.js";const ye=Me(),en=J("daemon.pid"),on=J("daemon.instance.lock");function nn(){try{const a=z.readFileSync(en,"utf-8").trim();if(a.startsWith("{")){const g=JSON.parse(a),d=Number(g.pid);return Number.isInteger(d)&&d>0?d:null}const c=parseInt(a,10);return Number.isInteger(c)&&c>0?c:null}catch{return null}}function tn(a){return a.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function Q(){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 rn(a){const c=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&c.push(`reason=${a.reason}`),a.error&&c.push(`error=${a.error}`),c.join(" ")}const i=new Pe;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(D),i.enablePositionalOptions(),to(i),ro(i),ao(i),ke(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Ee.hostname()).action(async a=>{const c=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??c.serverUrl??void 0;await Le(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 c=J("env.json");try{const e=JSON.parse(z.readFileSync(c,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ue()}catch{}xe(ae(process.env,p()));const g=bo();ce()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const d=je(),C=So({lockPath:on,instanceId:d});C.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(C.ownerPid?` (PID ${C.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=nn();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),C.release(),process.exit(0)}catch{}}catch{}const Y=We(process.env,ye)?[]:He(process.pid);Y.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${Y[0]}), skipping duplicate start`),C.release(),process.exit(0)),le(process.pid,d,{version:D}),me(process.pid,void 0,d),process.on("exit",()=>{ie(process.pid,d),se(d)}),process.on("exit",C.release);let j=null;try{j=fo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const M=await po();if(M){j||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{ho({fromVersion:D,targetVersion:M.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:M.jobId},{readiness:j}),console.error(`[upgrade] New version crashed ${M.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{Yo()}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??K.cn.url,h=pe(),l=await ge({serverUrl:u,authToken:m.machineToken??m.accessToken}),S=l.map(e=>e.type);let H=V(l);const f=new Mo,_=new xo,k=new qo(f),y=new _o(u),Ce=new Wo({actionClient:y}),Re=new Go({actionClient:y});let w=null,r;const Z=zo({authority:k,actionClient:y,attachmentCache:Ce,attachmentUploader:Re,observations:_,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>re({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"}),k.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let B=null,F=null,W=null,G=null,X=!1;const b=await fe({paths:Ao(),handlers:{...Eo({daemonVersion:h,protocolVersion:Uo,getStatus:()=>de(),getAgents:()=>l,requestShutdown:()=>{G?G():X=!0},completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),bindManagedRoom:e=>r.bindManagedRoomFromLocal(e),startUpgrade:async e=>{if(!B)throw new Error("upgrade_unavailable");return B(e)},prepareExternalUpgrade:e=>{if(!F)throw new Error("external_upgrade_unavailable");return F(e)},cancelExternalUpgrade:()=>{if(!W)throw new Error("external_upgrade_unavailable");return W()}}),...To({getStatus:()=>de(),getSnapshot:()=>V(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 he(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=V(l);return s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(q=>q.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return Do(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let A;try{A=await fe({paths:Po(),handlers:{"managed-room.execute":e=>Z(e),"schedule.personal":async e=>{const n=e,o=be(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await re({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let ee=!1;const N=()=>{ee||(ee=!0,Promise.allSettled([b.close(),A.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",N),process.once("SIGTERM",N),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),A.close()]),process.exitCode=1;return}process.removeListener("SIGINT",N),process.removeListener("SIGTERM",N);const oe=`${tn(u)}/relay/machine`,x=m.machineToken;if(!x)return;try{await Qo({serverUrl:u,machineToken:x,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(),A.close()]),process.exit(1)}m.machineId&&wo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${oe}... (v${h}) agents: ${S.join(",")}`),U({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:S}});let P=null,E=null,R=0,v=go();const ne=_e(m.autoUpgrade),O=m.machineId?new Io({serverUrl:u,machineId:m.machineId,getMachineToken:()=>p().machineToken,getDetectedAgents:()=>ge({serverUrl:u,authToken:p().machineToken}),onRuntimeChanged:async()=>{const e=R,n=await he(!0);if(l.splice(0,l.length,...n.detectedAgents),S.splice(0,S.length,...n.agentList),H=n.agents,!(await s.sendReq({type:"req",id:Te(),method:"machine.agents.report",params:{agents:n.agents}},2e4)).ok)throw new Error("machine_capability_report_rejected");e===R&&s.getState()==="connected"&&O?.start()},onError:()=>console.warn("[public-agent] delivery attempt failed")}):null,s=new Ne({serverUrl:oe,machineToken:x,cliVersion:h,agentList:S,autoUpgradePolicy:ne,sessionSyncProtocolVersion:ve,sessionSyncCapabilities:Ie,daemonInstallationId:Jo(),dataEpoch:Se,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),U({level:"info",wsEvent:"daemon.connected"}),s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:H}}),yo(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(H)&&s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:o}})}).catch(()=>{}),v&&s.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(),E?.start();const e=++R,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&vo(o.jobId,o.to),mo(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),Co({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),s.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"&&s.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===R&&s.getState()==="connected"&&(P?.handleConnected(),O?.start())}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===R&&s.getState()==="connected"){if(o instanceof Xo)return;const t=o instanceof Zo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{R+=1,r?.revokeProjectFsHandles(),O?.stop(),E?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${rn(e)}`),U({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}`),U({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){$o(s,E,e);return}r.handleReq(e)}});P=Ro()?new ko(s):null,E=new Oo({registry:f,observations:_,actionClient:y,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Lo().hasSession(e),onMessages:async(e,n,o,t)=>{await w?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(k.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new uo({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:ve,dataEpoch:Se,advertisedCapabilities:Ie,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(s,"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(ne),r=new io(s,P,h,$,new No({registry:f,observations:_,bindingClient:y,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${d}`}),{prepare:()=>Be(),isDisabled:()=>ce(),shutdownAfterResponse:()=>{setTimeout(()=>{Fe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>k.revokeToken(e),Z,null,so(x),()=>V(l)),B=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 Ye(s,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:q=>r.prepareForUpgrade(q),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},F=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)}},W=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),w=new Ho({registry:f,observations:_,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 y.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!jo(f,e)||!Vo(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return k.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new Fo,resolveManagedAgentPolicy:e=>Bo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Ae({serverUrl:u,machineToken:p().machineToken,req:e,client:s,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 y.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>w.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>w.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?w.prepareDirectConversationTurn(e.sessionId,Ko):e.source==="manager_event"?w.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:w.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),z.mkdirSync(ye,{recursive:!0}),le(process.pid,d,{version:h}),me(process.pid,void 0,d),s.connect(),process.stdin.resume();let te=!1;const L=async()=>{if(!te){te=!0,console.log(T.gray(`
|
|
3
|
+
Disconnecting...`)),U({level:"info",wsEvent:"daemon.stop"}),clearInterval(g),O?.stop();try{await r.cleanup(),$.stop(),P?.stop(),E?.stop(),s.disconnect(),await Promise.allSettled([b.close(),A.close()]),ie(process.pid,d),se(d)}finally{process.exit(0)}}};G=L,X&&L(),process.on("SIGINT",()=>{L()}),process.on("SIGTERM",()=>{L()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>Q()),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(()=>Q()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>Q());const we=i.command("config").description("View or modify configuration");we.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${Ue()}`);const a=p(),c=Oe(a,ae(process.env,a));if(a.serverUrl){const g=lo(a.serverUrl);console.log(`Region: ${g} (${K[g].label})`)}console.log(JSON.stringify(c,null,2))}),we.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,c)=>{const g=p();if(a==="server"){c!=="cn"&&c!=="global"&&(console.error(T.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const d=c;g.serverUrl=co(d),De(g),console.log(T.green(`\u2713 Server set to ${K[d].label}`)),console.log(T.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(T.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),$e(i),Ve(i),Ge(i),qe(i),ze(i,{clientVersion:D}),Je(i),Ke(i),Qe(i),Ze(i),Xe(i),eo(i),oo(i),no(i,D),i.parse();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isToolPayload as L,sessionPreviewFromPayload as B}from"@shennian/wire";import I from"node:crypto";import d from"node:fs";import{renameSyncWithRetry as P}from"../fs/atomic-rename.js";import g from"node:path";import{recordPersonalSyncLocalMetric as C}from"../personal-sync/metrics.js";import{resolvePersonalSyncV1Path as H}from"../personal-sync/paths.js";import{getCanonicalSessionStore as U,getDaemonInstallationId as V,listSessionRecords as w,recordSession as b}from"../session/store.js";import{readToolDetail as q,writeToolDetail as J}from"../session/tool-detail-store.js";import{NativeSourceDeletionStore as W}from"./native-source-deletions.js";import{isInternalNativeSource as z}from"./source-visibility.js";const E=1;class G extends Error{code="missing_user_boundary";constructor(e){super(`native source ${e} does not start at a visible user boundary`),this.name="MissingUserBoundaryError"}}class Q extends Error{code="internal_native_source";constructor(){super("internal native source cannot be imported as a personal session"),this.name="InternalNativeSourceError"}}class X extends Error{code="native_source_deleted";constructor(e){super(`native source ${e} is blocked by a durable deletion tombstone`),this.name="NativeSourceDeletedError"}}class Y extends Error{sourceEventKey;code="native_event_identity_conflict";constructor(e){super(`native event identity conflict: ${e}`),this.sourceEventKey=e,this.name="NativeEventIdentityConflictError"}}class he{options;constructor(e={}){this.options=e}commitEvents(e){return[...e].filter(s=>!this.isSourceInternal(s.agentType,s.sourceSessionKey)).sort((s,o)=>s.ts-o.ts).map(s=>this.commitEvent(s))}updateMessageDeliveryState(e,s,o){if(!this.canonicalStore().updateMessageDeliveryState(e,s,o))return;const i=(this.options.listLocalSessions??w)().find(a=>a.sessionId===e);if(!i)return;(this.options.recordLocalSession??b)({...i})}commitEvent(e){if(Z(e),this.isSourceInternal(e.agentType,e.sourceSessionKey))throw new Q;if(this.isSourceDeleted(e.agentType,e.sourceSessionKey))throw new X(e.sourceSessionKey);const s=this.installationId(),o=this.readSources(),r=D(e.agentType,e.sourceSessionKey),i=o.sources[r],c=te(e),a=this.ledgerFile(e.agentType,e.sourceEventKey),n=d.existsSync(a)?T(a):null;if(n&&n.daemonInstallationId!==s)throw new Error("native event ledger belongs to another daemon installation");if(n&&n.eventFingerprint!==c)throw new Y(e.sourceEventKey);if(!i&&!n&&e.role!=="user")throw new G(e.sourceSessionKey);const u=e.claimSessionId??null,l=i?.sessionId??n?.sessionId??u??ee(s,e.agentType,e.sourceSessionKey);if(u&&i&&i.sessionId!==u)throw new Error(`managed claim session conflicts with source binding ${e.sourceSessionKey}`);const m=this.canonicalStore(),f=e.localDisposition==="managed_echo_suppressed"||n?.mappedBy==="managed_echo_suppressed",p=n?.canonicalMessageId??(f?null:e.claimCanonicalMessageId??se(s,e.agentType,e.canonicalMessageKey??e.sourceEventKey));let S=m.getManifest(l)?.bodyRevision??0,x=!!n;const M=!f&&!!e.claimCanonicalMessageId;if(M){if(!p)throw new Error("managed echo claim requires a canonical message");const y=m.getMessageState(l,p);if(!y||y.tombstoned)throw new Error(`managed echo claim target is not durable: ${p}`);y.deliveryState==="queued"&&y.message&&(S=m.upsert(l,{...y.message,ts:e.ts},"executing").bodyRevision)}if(!n&&!M&&!f){if(!p)throw new Error("native event requires a canonical message");const y=e.canonicalMessageKey?m.getMessageState(l,p):null,v=e.canonicalMutation==="upsert"||!!y,F=v?y??m.getMessageState(l,p):null,_={id:p,sessionId:l,role:e.role,ts:F?.message?.ts??e.ts,payload:oe(F?.message?.payload,e.payload)},$=v?m.upsert(l,_):m.append(l,_);if(S=$.bodyRevision,x=$.duplicate,e.localToolDetail){const N=this.options.writeLocalToolDetail??J,j=this.options.readLocalToolDetail??q,R=ie(j(l,e.localToolDetail.toolCallId),e.localToolDetail);N({sessionId:l,...R,ts:e.ts,detailRef:{toolIndex:0}})}}e.terminal&&(S=m.completeStartedUserMessages(l,e.ts).at(-1)?.bodyRevision??S),(!n||e.terminal)&&!f&&this.recordIndex(e,l,p,S);const K=new Date().toISOString();return n||h(a,{schemaVersion:E,daemonInstallationId:s,sessionId:l,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceEventKey:e.sourceEventKey,eventFingerprint:c,canonicalMessageId:p,canonicalRevision:S,cursor:e.cursor??null,mappedBy:f?"managed_echo_suppressed":M?"managed_echo_claim":e.sourceMode,eventTs:e.ts,createdAt:K}),o.sources[r]={schemaVersion:E,daemonInstallationId:s,sessionId:l,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceMode:n?i?.sourceMode??e.sourceMode:e.sourceMode,lastCursor:n?i?.lastCursor??n.cursor:e.cursor??i?.lastCursor??null,bodyRevision:S,createdAt:i?.createdAt??K,updatedAt:K},h(this.sourcesFile(),o),!n&&e.cursor&&C("sourceCursorAdvanced"),!n&&!M&&!f&&!x&&C("canonicalCommits"),{sessionId:l,messageId:p,bodyRevision:S,duplicate:x,claimed:M,suppressed:f}}repairMissingUserBoundaries(){const e=this.options.listLocalSessions??w,s=this.options.recordLocalSession??b,o=this.canonicalStore();let r=0;for(const i of e()){if(i.localReady)continue;const a=o.getManifest(i.sessionId)?.firstAvailableSeq;if(a==null)continue;const n=o.readPage(i.sessionId,{limit:1,beforeSequence:a+1}).entries[0];!n||n.sequence!==a||n.message.role!=="user"||(s({sessionId:i.sessionId,agentType:i.agentType,sessionMode:i.sessionMode,managerConfig:i.managerConfig,workDir:i.workDir,agentSessionId:i.agentSessionId,modelId:i.modelId,managerDefaultWorkerAgentType:i.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:i.managerDefaultWorkerModelId,status:i.status,lastActivityAt:i.lastActivityAt,lastMessagePreview:i.lastMessagePreview,localReady:!0,firstUserMessageId:n.message.id,firstUserBodyRevision:n.revision}),r+=1)}return r}listManagedClaims(){return Object.values(this.readManagedEchoState().claims)}listManagedSuppressions(){return Object.values(this.readManagedEchoState().suppressions)}upsertManagedClaim(e){O(e);const s=this.readManagedEchoState();s.claims[e.canonicalMessageId]=e,h(this.managedEchoStateFile(),s)}updateManagedClaimSource(e,s,o){const r=this.readManagedEchoState();let i=!1;for(const c of Object.values(r.claims))c.sessionId!==e||c.sourceAgentType!==s||(c.sourceSessionKey=o,i=!0);i&&h(this.managedEchoStateFile(),r)}removeManagedClaim(e){const s=this.readManagedEchoState();s.claims[e]&&(delete s.claims[e],h(this.managedEchoStateFile(),s))}completeManagedClaim(e,s){const o=this.readManagedEchoState();delete o.claims[e],s&&(k(s),o.suppressions[D(s.agentType,s.sourceSessionKey)]=s),h(this.managedEchoStateFile(),o)}removeManagedSuppression(e,s){const o=this.readManagedEchoState(),r=D(e,s);o.suppressions[r]&&(delete o.suppressions[r],h(this.managedEchoStateFile(),o))}listSourceBindings(){return Object.values(this.readSources().sources).filter(e=>!this.isSourceDeleted(e.agentType,e.sourceSessionKey))}listSourceOwners(){return Object.values(this.readSources().sources).map(({agentType:e,sourceSessionKey:s,sessionId:o})=>({agentType:e,sourceSessionKey:s,sessionId:o}))}purgeSession(e){if(!e)throw new Error("sessionId is required");const s=this.readSources();let o=!1;for(const[a,n]of Object.entries(s.sources))n.sessionId===e&&(delete s.sources[a],o=!0);o&&h(this.sourcesFile(),s);const r=this.readManagedEchoState();let i=!1;for(const[a,n]of Object.entries(r.claims))n.sessionId===e&&(delete r.claims[a],i=!0);for(const[a,n]of Object.entries(r.suppressions))n.sessionId===e&&(delete r.suppressions[a],i=!0);i&&h(this.managedEchoStateFile(),r);const c=g.join(this.rootDir(),"ledger");if(d.existsSync(c))for(const a of d.readdirSync(c,{withFileTypes:!0})){if(!a.isFile()||!a.name.endsWith(".json"))continue;const n=g.join(c,a.name),u=T(n);if(u.daemonInstallationId!==this.installationId())throw new Error("native event ledger belongs to another daemon installation");u.sessionId===e&&d.unlinkSync(n)}}isSourceInternal(e,s){const o=this.rootDir();return z(e,s,{filePath:g.join(g.basename(o)==="native-fusion"?g.dirname(o):o,"native-fusion-state.json"),daemonInstallationId:this.installationId()})}isSourceDeleted(e,s){return(this.options.deletionStore??new W({rootDir:g.basename(this.rootDir())==="native-fusion"?g.dirname(this.rootDir()):this.rootDir(),daemonInstallationId:this.installationId()})).has(e,s)}observeNativeTitle(e,s,o){if(this.isSourceInternal(e,s))return!1;const r=o.trim().slice(0,120);if(!r)return!1;const i=this.readSources().sources[D(e,s)];if(!i||this.isSourceDeleted(e,s))return!1;const c=(this.options.listLocalSessions??w)().find(u=>u.sessionId===i.sessionId);if(c?.titleLockedByUser||c?.titleSource==="agent_native"&&c.title===r)return!1;const a=I.createHash("sha256").update(`${e}\0${s}\0${r}`).digest("hex");return(this.options.recordLocalSession??b)({sessionId:i.sessionId,agentType:i.agentType,workDir:c?.workDir??"",agentSessionId:s,title:r,titleSource:"agent_native",titleOriginEventId:`agent-native:${a}`}),!0}recordIndex(e,s,o,r){const i=this.options.recordLocalSession??b,a=(this.options.listLocalSessions??w)().find(f=>f.sessionId===s)?.workDir?.trim()||e.workDir||"",n=e.terminal?`terminal:${I.createHash("sha256").update(e.sourceEventKey).digest("hex")}`:void 0,u=e.role==="user"||e.role==="agent"?A(e.payload):null,l=u&&o?{lastMessagePreview:u,lastMessagePreviewMessageId:o,lastMessagePreviewAt:new Date(e.ts).toISOString()}:{},m=o&&(u||L(e.payload))?new Date(e.ts).toISOString():void 0;i({sessionId:s,agentType:e.agentType,workDir:a,agentSessionId:e.sourceSessionKey,modelId:e.modelId??null,status:e.terminal?e.terminalState==="failed"||e.terminalState==="aborted"?"failed":"completed":"active",attentionId:n,lastActivityAt:new Date(e.ts).toISOString(),lastVisibleEventAt:m,...l,...e.role==="user"&&o?{localReady:!0,firstUserMessageId:o,firstUserBodyRevision:r}:{},title:e.title?.trim()||(e.role==="user"?A(e.payload)??"":void 0),titleSource:e.title?.trim()?e.titleSource??"first_user_fallback":"first_user_fallback",titleOriginEventId:e.sourceEventKey})}readSources(){const e=this.sourcesFile();if(!d.existsSync(e))return{schemaVersion:E,daemonInstallationId:this.installationId(),sources:{}};const s=T(e);if(s.schemaVersion!==E||s.daemonInstallationId!==this.installationId()||!s.sources)throw new Error("invalid native source binding store");for(const o of Object.values(s.sources))if(o.daemonInstallationId!==this.installationId())throw new Error("native source binding belongs to another daemon installation");return s}installationId(){return this.options.daemonInstallationId??V()}canonicalStore(){return this.options.canonicalStore??U()}rootDir(){return this.options.rootDir??H("native-fusion")}sourcesFile(){return g.join(this.rootDir(),"sources.json")}managedEchoStateFile(){return g.join(this.rootDir(),"managed-echo-state.json")}readManagedEchoState(){const e=this.managedEchoStateFile();if(!d.existsSync(e))return{schemaVersion:E,daemonInstallationId:this.installationId(),claims:{},suppressions:{}};const s=T(e);if(s.schemaVersion!==E||s.daemonInstallationId!==this.installationId()||!s.claims||!s.suppressions)throw new Error("invalid managed echo state");for(const o of Object.values(s.claims))O(o);for(const o of Object.values(s.suppressions))k(o);return s}ledgerFile(e,s){const o=I.createHash("sha256").update(`${e}\0${s}`).digest("hex");return g.join(this.rootDir(),"ledger",`${o}.json`)}}function Z(t){if(!t.agentType||!t.sourceSessionKey||!t.sourceEventKey||!t.payload)throw new Error("invalid native event");if(!Number.isFinite(t.ts))throw new Error("invalid native event timestamp")}function D(t,e){return I.createHash("sha256").update(`${t}\0${e}`).digest("hex")}function ee(t,e,s){return`native-session-${I.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function se(t,e,s){return`native-${I.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function te(t){return I.createHash("sha256").update(JSON.stringify({agentType:t.agentType,sourceSessionKey:t.sourceSessionKey,sourceEventKey:t.sourceEventKey,role:t.role,ts:t.ts,payload:t.payload,canonicalMessageKey:t.canonicalMessageKey??null,canonicalMutation:t.canonicalMutation??"append"})).digest("hex")}function O(t){if(!t.sessionId||!t.canonicalMessageId||!t.textHash||!Number.isFinite(t.createdAt)||!["codex","claude","opencode"].includes(t.sourceAgentType)||!["suppress_following","import_following"].includes(t.managedEchoPolicy))throw new Error("invalid managed echo claim")}function k(t){if(!t.sessionId||!t.sourceSessionKey||!t.claimSourceEventKey||!["codex","claude","opencode"].includes(t.agentType)||!Number.isFinite(t.startedAt)||!Number.isFinite(t.endsAt)||t.endsAt<t.startedAt)throw new Error("invalid managed echo suppression")}function oe(t,e){if(!t)return e;try{const s=JSON.parse(t),o=JSON.parse(e);if(s.type!=="tool"||o.type!=="tool"||s.toolCallId!==o.toolCallId)return e;const r=typeof o.name=="string"?o.name:"",i=/(?:_output|_result)$/.test(r);return JSON.stringify({...s,...o,...i&&typeof s.name=="string"?{name:s.name}:{}})}catch{return e}}function ie(t,e){if(!t||t.toolCallId!==e.toolCallId)return e;const s=/(?:_output|_result)$/.test(e.name);return{...e,name:s?t.name:e.name,...e.args===void 0&&t.args!==void 0?{args:t.args}:{},...e.result===void 0&&t.result!==void 0?{result:t.result}:{}}}function A(t){return B(t,120)}function h(t,e){const s=g.dirname(t);d.mkdirSync(s,{recursive:!0,mode:448});const o=`${t}.tmp-${process.pid}-${I.randomBytes(6).toString("hex")}`;let r;try{r=d.openSync(o,"wx",384),d.writeFileSync(r,`${JSON.stringify(e,null,2)}
|
|
2
|
-
`,"utf8"),d.fsyncSync(r),d.closeSync(r),r=void 0,
|
|
1
|
+
import{isToolPayload as P,sessionPreviewFromPayload as H}from"@shennian/wire";import I from"node:crypto";import d from"node:fs";import{renameSyncWithRetry as U}from"../fs/atomic-rename.js";import g from"node:path";import{recordPersonalSyncLocalMetric as k}from"../personal-sync/metrics.js";import{resolvePersonalSyncV1Path as V}from"../personal-sync/paths.js";import{getCanonicalSessionStore as W,getDaemonInstallationId as q,listSessionRecords as w,recordSession as b}from"../session/store.js";import{readToolDetail as J,writeToolDetail as z}from"../session/tool-detail-store.js";import{NativeSourceDeletionStore as G}from"./native-source-deletions.js";import{isInternalNativeSource as Q}from"./source-visibility.js";import{isNativeTestWorkspace as C}from"./test-workspace.js";const E=1;class X extends Error{code="missing_user_boundary";constructor(e){super(`native source ${e} does not start at a visible user boundary`),this.name="MissingUserBoundaryError"}}class N extends Error{code="internal_native_source";constructor(){super("internal native source cannot be imported as a personal session"),this.name="InternalNativeSourceError"}}class Y extends Error{code="native_source_deleted";constructor(e){super(`native source ${e} is blocked by a durable deletion tombstone`),this.name="NativeSourceDeletedError"}}class Z extends Error{sourceEventKey;code="native_event_identity_conflict";constructor(e){super(`native event identity conflict: ${e}`),this.sourceEventKey=e,this.name="NativeEventIdentityConflictError"}}class ye{options;constructor(e={}){this.options=e}commitEvents(e){return[...e].filter(s=>!this.isSourceInternal(s.agentType,s.sourceSessionKey)&&!C(s.workDir??"")).sort((s,o)=>s.ts-o.ts).map(s=>this.commitEvent(s))}updateMessageDeliveryState(e,s,o){if(!this.canonicalStore().updateMessageDeliveryState(e,s,o))return;const i=(this.options.listLocalSessions??w)().find(a=>a.sessionId===e);if(!i)return;(this.options.recordLocalSession??b)({...i})}commitEvent(e){if(ee(e),this.isSourceInternal(e.agentType,e.sourceSessionKey))throw new N;if(this.isSourceDeleted(e.agentType,e.sourceSessionKey))throw new Y(e.sourceSessionKey);if(C(e.workDir??""))throw new N;const s=this.installationId(),o=this.readSources(),r=D(e.agentType,e.sourceSessionKey),i=o.sources[r],c=oe(e),a=this.ledgerFile(e.agentType,e.sourceEventKey),n=d.existsSync(a)?T(a):null;if(n&&n.daemonInstallationId!==s)throw new Error("native event ledger belongs to another daemon installation");if(n&&n.eventFingerprint!==c)throw new Z(e.sourceEventKey);if(!i&&!n&&e.role!=="user")throw new X(e.sourceSessionKey);const u=e.claimSessionId??null,l=i?.sessionId??n?.sessionId??u??se(s,e.agentType,e.sourceSessionKey);if(u&&i&&i.sessionId!==u)throw new Error(`managed claim session conflicts with source binding ${e.sourceSessionKey}`);const m=this.canonicalStore(),f=e.localDisposition==="managed_echo_suppressed"||n?.mappedBy==="managed_echo_suppressed",p=n?.canonicalMessageId??(f?null:e.claimCanonicalMessageId??te(s,e.agentType,e.canonicalMessageKey??e.sourceEventKey));let S=m.getManifest(l)?.bodyRevision??0,x=!!n;const M=!f&&!!e.claimCanonicalMessageId;if(M){if(!p)throw new Error("managed echo claim requires a canonical message");const y=m.getMessageState(l,p);if(!y||y.tombstoned)throw new Error(`managed echo claim target is not durable: ${p}`);y.deliveryState==="queued"&&y.message&&(S=m.upsert(l,{...y.message,ts:e.ts},"executing").bodyRevision)}if(!n&&!M&&!f){if(!p)throw new Error("native event requires a canonical message");const y=e.canonicalMessageKey?m.getMessageState(l,p):null,F=e.canonicalMutation==="upsert"||!!y,_=F?y??m.getMessageState(l,p):null,v={id:p,sessionId:l,role:e.role,ts:_?.message?.ts??e.ts,payload:ie(_?.message?.payload,e.payload)},$=F?m.upsert(l,v):m.append(l,v);if(S=$.bodyRevision,x=$.duplicate,e.localToolDetail){const R=this.options.writeLocalToolDetail??z,L=this.options.readLocalToolDetail??J,B=re(L(l,e.localToolDetail.toolCallId),e.localToolDetail);R({sessionId:l,...B,ts:e.ts,detailRef:{toolIndex:0}})}}e.terminal&&(S=m.completeStartedUserMessages(l,e.ts).at(-1)?.bodyRevision??S),(!n||e.terminal)&&!f&&this.recordIndex(e,l,p,S);const K=new Date().toISOString();return n||h(a,{schemaVersion:E,daemonInstallationId:s,sessionId:l,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceEventKey:e.sourceEventKey,eventFingerprint:c,canonicalMessageId:p,canonicalRevision:S,cursor:e.cursor??null,mappedBy:f?"managed_echo_suppressed":M?"managed_echo_claim":e.sourceMode,eventTs:e.ts,createdAt:K}),o.sources[r]={schemaVersion:E,daemonInstallationId:s,sessionId:l,agentType:e.agentType,sourceSessionKey:e.sourceSessionKey,sourceMode:n?i?.sourceMode??e.sourceMode:e.sourceMode,lastCursor:n?i?.lastCursor??n.cursor:e.cursor??i?.lastCursor??null,bodyRevision:S,createdAt:i?.createdAt??K,updatedAt:K},h(this.sourcesFile(),o),!n&&e.cursor&&k("sourceCursorAdvanced"),!n&&!M&&!f&&!x&&k("canonicalCommits"),{sessionId:l,messageId:p,bodyRevision:S,duplicate:x,claimed:M,suppressed:f}}repairMissingUserBoundaries(){const e=this.options.listLocalSessions??w,s=this.options.recordLocalSession??b,o=this.canonicalStore();let r=0;for(const i of e()){if(i.localReady)continue;const a=o.getManifest(i.sessionId)?.firstAvailableSeq;if(a==null)continue;const n=o.readPage(i.sessionId,{limit:1,beforeSequence:a+1}).entries[0];!n||n.sequence!==a||n.message.role!=="user"||(s({sessionId:i.sessionId,agentType:i.agentType,sessionMode:i.sessionMode,managerConfig:i.managerConfig,workDir:i.workDir,agentSessionId:i.agentSessionId,modelId:i.modelId,managerDefaultWorkerAgentType:i.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:i.managerDefaultWorkerModelId,status:i.status,lastActivityAt:i.lastActivityAt,lastMessagePreview:i.lastMessagePreview,localReady:!0,firstUserMessageId:n.message.id,firstUserBodyRevision:n.revision}),r+=1)}return r}listManagedClaims(){return Object.values(this.readManagedEchoState().claims)}listManagedSuppressions(){return Object.values(this.readManagedEchoState().suppressions)}upsertManagedClaim(e){O(e);const s=this.readManagedEchoState();s.claims[e.canonicalMessageId]=e,h(this.managedEchoStateFile(),s)}updateManagedClaimSource(e,s,o){const r=this.readManagedEchoState();let i=!1;for(const c of Object.values(r.claims))c.sessionId!==e||c.sourceAgentType!==s||(c.sourceSessionKey=o,i=!0);i&&h(this.managedEchoStateFile(),r)}removeManagedClaim(e){const s=this.readManagedEchoState();s.claims[e]&&(delete s.claims[e],h(this.managedEchoStateFile(),s))}completeManagedClaim(e,s){const o=this.readManagedEchoState();delete o.claims[e],s&&(A(s),o.suppressions[D(s.agentType,s.sourceSessionKey)]=s),h(this.managedEchoStateFile(),o)}removeManagedSuppression(e,s){const o=this.readManagedEchoState(),r=D(e,s);o.suppressions[r]&&(delete o.suppressions[r],h(this.managedEchoStateFile(),o))}listSourceBindings(){return Object.values(this.readSources().sources).filter(e=>!this.isSourceDeleted(e.agentType,e.sourceSessionKey))}listSourceOwners(){return Object.values(this.readSources().sources).map(({agentType:e,sourceSessionKey:s,sessionId:o})=>({agentType:e,sourceSessionKey:s,sessionId:o}))}purgeSession(e){if(!e)throw new Error("sessionId is required");const s=this.readSources();let o=!1;for(const[a,n]of Object.entries(s.sources))n.sessionId===e&&(delete s.sources[a],o=!0);o&&h(this.sourcesFile(),s);const r=this.readManagedEchoState();let i=!1;for(const[a,n]of Object.entries(r.claims))n.sessionId===e&&(delete r.claims[a],i=!0);for(const[a,n]of Object.entries(r.suppressions))n.sessionId===e&&(delete r.suppressions[a],i=!0);i&&h(this.managedEchoStateFile(),r);const c=g.join(this.rootDir(),"ledger");if(d.existsSync(c))for(const a of d.readdirSync(c,{withFileTypes:!0})){if(!a.isFile()||!a.name.endsWith(".json"))continue;const n=g.join(c,a.name),u=T(n);if(u.daemonInstallationId!==this.installationId())throw new Error("native event ledger belongs to another daemon installation");u.sessionId===e&&d.unlinkSync(n)}}isSourceInternal(e,s){const o=this.rootDir();return Q(e,s,{filePath:g.join(g.basename(o)==="native-fusion"?g.dirname(o):o,"native-fusion-state.json"),daemonInstallationId:this.installationId()})}isSourceDeleted(e,s){return(this.options.deletionStore??new G({rootDir:g.basename(this.rootDir())==="native-fusion"?g.dirname(this.rootDir()):this.rootDir(),daemonInstallationId:this.installationId()})).has(e,s)}observeNativeTitle(e,s,o){if(this.isSourceInternal(e,s))return!1;const r=o.trim().slice(0,120);if(!r)return!1;const i=this.readSources().sources[D(e,s)];if(!i||this.isSourceDeleted(e,s))return!1;const c=(this.options.listLocalSessions??w)().find(u=>u.sessionId===i.sessionId);if(c?.titleLockedByUser||c?.titleSource==="agent_native"&&c.title===r)return!1;const a=I.createHash("sha256").update(`${e}\0${s}\0${r}`).digest("hex");return(this.options.recordLocalSession??b)({sessionId:i.sessionId,agentType:i.agentType,workDir:c?.workDir??"",agentSessionId:s,title:r,titleSource:"agent_native",titleOriginEventId:`agent-native:${a}`}),!0}recordIndex(e,s,o,r){const i=this.options.recordLocalSession??b,a=(this.options.listLocalSessions??w)().find(f=>f.sessionId===s)?.workDir?.trim()||e.workDir||"",n=e.terminal?`terminal:${I.createHash("sha256").update(e.sourceEventKey).digest("hex")}`:void 0,u=e.role==="user"||e.role==="agent"?j(e.payload):null,l=u&&o?{lastMessagePreview:u,lastMessagePreviewMessageId:o,lastMessagePreviewAt:new Date(e.ts).toISOString()}:{},m=o&&(u||P(e.payload))?new Date(e.ts).toISOString():void 0;i({sessionId:s,agentType:e.agentType,workDir:a,agentSessionId:e.sourceSessionKey,modelId:e.modelId??null,status:e.terminal?e.terminalState==="failed"||e.terminalState==="aborted"?"failed":"completed":"active",attentionId:n,lastActivityAt:new Date(e.ts).toISOString(),lastVisibleEventAt:m,...l,...e.role==="user"&&o?{localReady:!0,firstUserMessageId:o,firstUserBodyRevision:r}:{},title:e.title?.trim()||(e.role==="user"?j(e.payload)??"":void 0),titleSource:e.title?.trim()?e.titleSource??"first_user_fallback":"first_user_fallback",titleOriginEventId:e.sourceEventKey})}readSources(){const e=this.sourcesFile();if(!d.existsSync(e))return{schemaVersion:E,daemonInstallationId:this.installationId(),sources:{}};const s=T(e);if(s.schemaVersion!==E||s.daemonInstallationId!==this.installationId()||!s.sources)throw new Error("invalid native source binding store");for(const o of Object.values(s.sources))if(o.daemonInstallationId!==this.installationId())throw new Error("native source binding belongs to another daemon installation");return s}installationId(){return this.options.daemonInstallationId??q()}canonicalStore(){return this.options.canonicalStore??W()}rootDir(){return this.options.rootDir??V("native-fusion")}sourcesFile(){return g.join(this.rootDir(),"sources.json")}managedEchoStateFile(){return g.join(this.rootDir(),"managed-echo-state.json")}readManagedEchoState(){const e=this.managedEchoStateFile();if(!d.existsSync(e))return{schemaVersion:E,daemonInstallationId:this.installationId(),claims:{},suppressions:{}};const s=T(e);if(s.schemaVersion!==E||s.daemonInstallationId!==this.installationId()||!s.claims||!s.suppressions)throw new Error("invalid managed echo state");for(const o of Object.values(s.claims))O(o);for(const o of Object.values(s.suppressions))A(o);return s}ledgerFile(e,s){const o=I.createHash("sha256").update(`${e}\0${s}`).digest("hex");return g.join(this.rootDir(),"ledger",`${o}.json`)}}function ee(t){if(!t.agentType||!t.sourceSessionKey||!t.sourceEventKey||!t.payload)throw new Error("invalid native event");if(!Number.isFinite(t.ts))throw new Error("invalid native event timestamp")}function D(t,e){return I.createHash("sha256").update(`${t}\0${e}`).digest("hex")}function se(t,e,s){return`native-session-${I.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function te(t,e,s){return`native-${I.createHash("sha256").update(`${t}\0${e}\0${s}`).digest("hex").slice(0,32)}`}function oe(t){return I.createHash("sha256").update(JSON.stringify({agentType:t.agentType,sourceSessionKey:t.sourceSessionKey,sourceEventKey:t.sourceEventKey,role:t.role,ts:t.ts,payload:t.payload,canonicalMessageKey:t.canonicalMessageKey??null,canonicalMutation:t.canonicalMutation??"append"})).digest("hex")}function O(t){if(!t.sessionId||!t.canonicalMessageId||!t.textHash||!Number.isFinite(t.createdAt)||!["codex","claude","opencode"].includes(t.sourceAgentType)||!["suppress_following","import_following"].includes(t.managedEchoPolicy))throw new Error("invalid managed echo claim")}function A(t){if(!t.sessionId||!t.sourceSessionKey||!t.claimSourceEventKey||!["codex","claude","opencode"].includes(t.agentType)||!Number.isFinite(t.startedAt)||!Number.isFinite(t.endsAt)||t.endsAt<t.startedAt)throw new Error("invalid managed echo suppression")}function ie(t,e){if(!t)return e;try{const s=JSON.parse(t),o=JSON.parse(e);if(s.type!=="tool"||o.type!=="tool"||s.toolCallId!==o.toolCallId)return e;const r=typeof o.name=="string"?o.name:"",i=/(?:_output|_result)$/.test(r);return JSON.stringify({...s,...o,...i&&typeof s.name=="string"?{name:s.name}:{}})}catch{return e}}function re(t,e){if(!t||t.toolCallId!==e.toolCallId)return e;const s=/(?:_output|_result)$/.test(e.name);return{...e,name:s?t.name:e.name,...e.args===void 0&&t.args!==void 0?{args:t.args}:{},...e.result===void 0&&t.result!==void 0?{result:t.result}:{}}}function j(t){return H(t,120)}function h(t,e){const s=g.dirname(t);d.mkdirSync(s,{recursive:!0,mode:448});const o=`${t}.tmp-${process.pid}-${I.randomBytes(6).toString("hex")}`;let r;try{r=d.openSync(o,"wx",384),d.writeFileSync(r,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,"utf8"),d.fsyncSync(r),d.closeSync(r),r=void 0,U(o,t)}finally{r!==void 0&&d.closeSync(r);try{d.unlinkSync(o)}catch{}}}function T(t){return JSON.parse(d.readFileSync(t,"utf8"))}export{N as InternalNativeSourceError,X as MissingUserBoundaryError,Z as NativeEventIdentityConflictError,ye as NativeFusionLocalRepository,Y as NativeSourceDeletedError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"node:fs";import a from"node:os";import t from"node:path";function c(){return t.join(a.homedir(),"tmp","shennian-tests")}function f(r){if(!t.isAbsolute(r))return!1;const o=c();let s=o;try{s=i.realpathSync.native(o)}catch{}return[o,s].some(n=>{const e=t.relative(n,t.resolve(r));return e===""||e!==".."&&!e.startsWith(`..${t.sep}`)&&!t.isAbsolute(e)})}export{f as isNativeTestWorkspace,c as nativeTestWorkspaceRoot};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import s from"node:fs";import n from"node:path";function c(e=process.platform){return e==="darwin"}function i(e){const r=n.resolve(e);try{return s.realpathSync.native(r)}catch{const t=n.dirname(r);return t===r?r:n.join(i(t),n.basename(r))}}function l(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function f(e){const r=[...new Set(e.confinedRoots.map(i))];if(!r.length)throw new Error("macos_file_sandbox_confined_root_required");const t=e.scopes.map(o=>({path:i(o.path),write:o.write})),a=["(version 1)","(allow default)"];for(const o of r)a.push(`(deny file-read-data file-write* (subpath ${l(o)}))`);for(const o of t){const p=o.write?"file-read-data file-write*":"file-read-data";a.push(`(allow ${p} (subpath ${l(o.path)}))`)}return`${a.join(`
|
|
2
|
+
`)}
|
|
3
|
+
`}function u(e){if(!n.isAbsolute(e))return e;try{return s.realpathSync.native(e)}catch{return e}}function w(e,r,t){if(!c())throw new Error("macos_file_sandbox_unsupported_platform");return{command:"sandbox-exec",args:["-p",f(t),"--",u(e),...r]}}export{f as buildMacosFileSandboxProfile,c as isMacosFileSandboxSupported,w as wrapCommandWithMacosFileSandbox};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:fs/promises";import a from"node:path";import{managedAgentExecutionMode as y,parseManagedAgentPermissionPolicy as
|
|
1
|
+
import c from"node:fs/promises";import a from"node:path";import{managedAgentExecutionMode as y,parseManagedAgentPermissionPolicy as b,resolveManagedAgentEffectivePermissionPolicy as _}from"@shennian/wire";import{getManagedAgentBaseEnvKeys as k,getManagedAgentProviderEnvKeys as d,getManagedAgentSessionEnvKeys as u}from"../agent-env.js";import{collectPolicyEnforcementGaps as C,isRestrictedRoomChatUnsupported as x,resolveManagedAgentRuntimeProtection as P,resolveManagedAgentSecurityProfile as w}from"./managed-agent-runtime.js";import{resolveProtectedShennianDirectories as A}from"./managed-agent-credential-paths.js";function z(e){return!!(e&&(e.executionMode==="native-full"||e.requestedPolicy.executionMode==="native-full"))}function I(e){return{...e.enforcement,effectivePolicy:{version:e.adapterPolicy.version,executionMode:e.executionMode??"restricted",fileAccess:e.adapterPolicy.fileAccess,externalCapabilities:{...e.adapterPolicy.externalCapabilities}}}}async function B(e){const r=b(e.policy);if(!r)return{ok:!1,errorCode:"managed_agent_policy_invalid"};const t=y(r);if((t==="native-full"||r.fileAccess==="host-full")&&!e.hostFullAccessConfirmed)return{ok:!1,errorCode:"managed_agent_host_full_confirmation_required"};if(t==="restricted"&&e.channel!=="public"&&x(e.agentType))return{ok:!1,errorCode:"managed_agent_restricted_room_unsupported"};const o=await M(e.workDir,e.resolveWorkDir);if(!o.ok)return o;const i=h(e),n=P(i),l=w(i),s=t==="restricted"?C(r,l):{unavailableCapabilities:[],overgrantedCapabilities:[]},v=g([...t==="restricted"&&n.level!=="hard"?["runtime.hardIsolation"]:[],...t==="restricted"&&e.channel!=="public"?n.unsupportedCapabilities:[],...s.unavailableCapabilities,...s.overgrantedCapabilities]),p=_(r,n),m={level:n.level,effectivePolicy:p,unsupportedCapabilities:v,runtimeVersion:n.runtimeVersion,platform:n.platform,...n.executableFingerprint?{executableFingerprint:n.executableFingerprint}:{}};return{ok:!0,value:{requestedPolicy:q(r),executionMode:t,enforcement:m,adapterPolicy:{version:r.version,workDir:o.workDir,fileAccess:r.fileAccess,externalCapabilities:{...r.externalCapabilities},denySensitiveFiles:!0,failClosed:!0},environmentPolicy:{inheritDaemonEnv:!1,loadLoginShell:!1,providerEnvKeys:[...d(e.agentType)],sessionEnvKeys:[...u(e.agentType)],shellSubprocess:{inherit:"none",allowedEnvKeys:g([...k(e.platform),...u(e.agentType)]),deniedEnvKeys:[...d(e.agentType)]}}}}}function h(e){return{agentType:e.agentType,runtimeVersion:e.runtimeVersion,platform:e.platform,isWsl:e.isWsl,executableFingerprint:e.executableFingerprint,requireExecutableFingerprint:e.requireExecutableFingerprint,securityDeclaration:e.securityDeclaration}}async function M(e,r){const t=e.trim();if(!t||!a.isAbsolute(t))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};try{const o=await(r??E)(t);if(!o||!a.isAbsolute(o))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};const i=a.normalize(o);return await D(i)?{ok:!1,errorCode:"managed_agent_workdir_invalid"}:{ok:!0,workDir:i}}catch{return{ok:!1,errorCode:"managed_agent_workdir_unavailable"}}}async function E(e){const r=await c.realpath(e);if(!(await c.stat(r)).isDirectory())throw new Error("managed_agent_workdir_not_directory");return r}async function D(e){return A().some(t=>F(e,t,process.platform))}function F(e,r,t){const o=l=>{const s=a.resolve(l);return t==="win32"?s.toLowerCase():s},i=o(e),n=o(r);return f(i,n)||f(n,i)}function f(e,r){const t=a.relative(e,r);return t===""||!t.startsWith("..")&&!a.isAbsolute(t)}function q(e){return{...e,externalCapabilities:{...e.externalCapabilities}}}function g(e){return[...new Set(e)]}export{B as compileManagedAgentPolicy,z as isManagedAgentNativeFull,I as resolveManagedAgentAdapterEnforcement,M as validateManagedAgentWorkDir};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isManagedAgentNativeFull as s}from"./managed-agent-policy.js";function n(e){if(!e||!e.enforcement.executableFingerprint||e.adapterPolicy.fileAccess!==e.requestedPolicy.fileAccess||Object.entries(e.requestedPolicy.externalCapabilities).some(([i,t])=>e.adapterPolicy.externalCapabilities[i]!==t)||!e.adapterPolicy.denySensitiveFiles||!e.adapterPolicy.failClosed||e.environmentPolicy.inheritDaemonEnv||e.environmentPolicy.loadLoginShell)throw new Error("public_agent_policy_unavailable");if(!s(e)&&(e.enforcement.level==="unverified"||e.enforcement.effectivePolicy.fileAccess!==e.requestedPolicy.fileAccess||Object.entries(e.requestedPolicy.externalCapabilities).some(([i,t])=>e.enforcement.effectivePolicy.externalCapabilities[i]!==t)||e.enforcement.unsupportedCapabilities.some(i=>i==={"workspace-read":"fileAccess.workspaceRead","workspace-write":"fileAccess.workspaceWrite","host-full":"fileAccess.hostFull"}[e.requestedPolicy.fileAccess]||Object.entries(e.requestedPolicy.externalCapabilities).some(([t,r])=>r&&i===`externalCapabilities.${t}`))))throw new Error("public_agent_policy_unavailable")}export{n as assertPublicAgentPolicy};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const u=1,
|
|
1
|
+
const u=1,s={version:1,executionMode:"restricted",fileAccess:"workspace-read",externalCapabilities:{commandNetwork:!1,webSearch:!1,browser:!1,computerUse:!1,mcp:!1}},p={version:1,executionMode:"native-full",fileAccess:"host-full",externalCapabilities:{commandNetwork:!0,webSearch:!0,browser:!0,computerUse:!0,mcp:!0}},A=s,c={version:1,executionMode:"restricted",fileAccess:"host-full",externalCapabilities:{commandNetwork:!0,webSearch:!0,browser:!0,computerUse:!0,mcp:!0}};function m(t,r){const e=r?.capabilityMatrix;if(!e)return{version:t.version,executionMode:t.executionMode??"restricted",fileAccess:"host-full",externalCapabilities:{commandNetwork:!0,webSearch:!0,browser:!0,computerUse:!0,mcp:!0}};const i=e.fileAccess[t.fileAccess]?t.fileAccess:e.fileAccess["host-full"]?"host-full":t.fileAccess,o={...t.externalCapabilities};for(const n of Object.keys(o))!o[n]&&!e.externalCapabilities[n].deny&&(o[n]=!0);return{version:t.version,executionMode:t.executionMode??"restricted",fileAccess:i,externalCapabilities:o}}function a(t){return{...t,externalCapabilities:{...t.externalCapabilities}}}function l(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const r=t;if(!Number.isSafeInteger(r.version)||Number(r.version)<1||r.executionMode!==void 0&&r.executionMode!=="native-full"&&r.executionMode!=="restricted"||r.fileAccess!=="workspace-read"&&r.fileAccess!=="workspace-write"&&r.fileAccess!=="host-full"||!r.externalCapabilities||typeof r.externalCapabilities!="object"||Array.isArray(r.externalCapabilities))return null;const e=r.externalCapabilities;return typeof e.commandNetwork!="boolean"||typeof e.webSearch!="boolean"||typeof e.browser!="boolean"||typeof e.computerUse!="boolean"||typeof e.mcp!="boolean"?null:{version:Number(r.version),executionMode:r.executionMode==="native-full"?"native-full":"restricted",fileAccess:r.fileAccess,externalCapabilities:{commandNetwork:e.commandNetwork,webSearch:e.webSearch,browser:e.browser,computerUse:e.computerUse,mcp:e.mcp}}}function f(t){return t.executionMode==="native-full"?"native-full":"restricted"}function b(t){const r=t.confirmation;if(!r||typeof r!="object"||Array.isArray(r))return!1;const e=r,i=f(t.policy);return typeof e.runtimeVersion=="string"&&e.runtimeVersion.length>0&&typeof e.platform=="string"&&e.platform.length>0&&typeof e.executableFingerprint=="string"&&/^sha256:[a-f0-9]{64}$/.test(e.executableFingerprint)&&typeof e.limitedProtection=="boolean"&&e.policyVersion===t.policy.version&&e.runtimeAdapter===t.runtimeAdapter&&(e.executionMode??"restricted")===i&&e.hostFullAccess===(i==="native-full"||t.policy.fileAccess==="host-full")&&typeof e.confirmedAt=="string"&&Number.isFinite(Date.parse(e.confirmedAt))}function x(t,r="legacy"){const e=l(t);return e||a(r==="default"?s:c)}export{s as DEFAULT_MANAGED_AGENT_PERMISSION_POLICY,p as DEFAULT_NATIVE_FULL_MANAGED_AGENT_PERMISSION_POLICY,A as DEFAULT_RESTRICTED_MANAGED_AGENT_PERMISSION_POLICY,c as LEGACY_MANAGED_AGENT_PERMISSION_POLICY,u as MANAGED_AGENT_PERMISSION_POLICY_VERSION,b as managedAgentConfirmationMatchesPolicy,f as managedAgentExecutionMode,l as parseManagedAgentPermissionPolicy,m as resolveManagedAgentEffectivePermissionPolicy,x as resolveManagedAgentPermissionPolicy};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shennian",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
4
4
|
"description": "Shennian \u2014 AI Agent Control Plane CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,13 @@
|
|
|
43
43
|
"build": "node -e \"require('node:fs').rmSync('dist/native/macos-background-client', { recursive: true, force: true })\" && tsc && node -e \"require('node:fs').chmodSync('dist/bin/shennian.js', 0o755)\" && node scripts/copy-agent-adapter-docs.mjs",
|
|
44
44
|
"build:publish": "pnpm --filter @shennian/wire build:publish && node -e \"const fs=require('node:fs'); fs.rmSync('dist', { recursive: true, force: true }); fs.rmSync('.tsbuildinfo.publish', { force: true })\" && tsc -p tsconfig.publish.json && node scripts/minify-publish-js.mjs && node scripts/strip-publish-types.mjs dist && node -e \"require('node:fs').chmodSync('dist/bin/shennian.js', 0o755)\" && node scripts/copy-agent-adapter-docs.mjs && node scripts/check-publish-artifact.mjs",
|
|
45
45
|
"test:security:codex": "tsx scripts/verify-managed-codex-sandbox.ts",
|
|
46
|
+
"test:security:public-codex-turn": "tsx scripts/probe-public-agent-turn.ts --run --provider=codex",
|
|
47
|
+
"test:security:public-claude-turn": "tsx scripts/probe-public-agent-turn.ts --run --provider=claude",
|
|
48
|
+
"test:security:public-opencode-boundary": "tsx scripts/probe-opencode-public-boundary.ts --run",
|
|
49
|
+
"test:security:public-opencode-macos-sandbox": "tsx scripts/probe-opencode-macos-file-sandbox.ts --run",
|
|
50
|
+
"test:security:public-cursor-boundary": "tsx scripts/probe-cursor-public-boundary.ts --run",
|
|
51
|
+
"test:security:public-workbuddy-boundary": "tsx scripts/probe-workbuddy-public-boundary.ts --run",
|
|
52
|
+
"test:security:public-pi-boundary": "tsx scripts/probe-pi-public-boundary.ts --run",
|
|
46
53
|
"test:security:codex-tools": "tsx scripts/verify-managed-codex-dynamic-tools.ts",
|
|
47
54
|
"dev": "tsc --watch",
|
|
48
55
|
"prepublishOnly": "pnpm build:publish"
|