shennian 0.4.65 → 0.4.67
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 +39 -34
- package/dist/src/agents/acp/manifests.d.ts +2 -2
- package/dist/src/agents/adapter.d.ts +1 -1
- package/dist/src/agents/codex.js +5 -5
- package/dist/src/index.js +3 -3
- package/dist/src/manager/registry.d.ts +10 -0
- package/dist/src/manager/registry.js +1 -1
- package/dist/src/manager/runtime.d.ts +4 -2
- package/dist/src/manager/runtime.js +6 -6
- package/dist/src/manager/worker-ipc.d.ts +5 -3
- package/dist/src/manager/worker-ipc.js +1 -1
- package/dist/src/room/activation-service.d.ts +8 -2
- package/dist/src/room/activation-service.js +10 -5
- package/dist/src/room/managed-context.d.ts +3 -1
- package/dist/src/room/managed-context.js +1 -1
- package/dist/src/room/managed-room-ipc.d.ts +2 -0
- package/dist/src/room/managed-room-ipc.js +2 -2
- package/dist/src/session/handlers/chat-recovery.js +1 -1
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/manager-event-authority.d.ts +3 -0
- package/dist/src/session/manager-event-authority.js +1 -0
- package/dist/src/session/manager.d.ts +2 -2
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/queue-helpers.d.ts +5 -0
- package/dist/src/session/queue-helpers.js +8 -8
- package/dist/src/session/queue-message.d.ts +4 -1
- package/dist/src/session/queue-message.js +1 -1
- package/dist/src/session/queue-storage.d.ts +1 -0
- package/dist/src/session/queue-storage.js +2 -2
- package/dist/src/session/queue-types.d.ts +8 -0
- package/dist/src/session/queue.d.ts +3 -3
- package/dist/src/session/queue.js +1 -1
- package/dist/src/session/queued-chat-dispatch.js +1 -1
- package/dist/src/session/turn-trigger.d.ts +5 -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": 207,
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"file": "bin/shennian.js",
|
|
@@ -121,8 +121,8 @@
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"file": "src/agents/codex.js",
|
|
124
|
-
"beforeBytes":
|
|
125
|
-
"afterBytes":
|
|
124
|
+
"beforeBytes": 69014,
|
|
125
|
+
"afterBytes": 35438
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
"file": "src/agents/command-spec.js",
|
|
@@ -296,7 +296,7 @@
|
|
|
296
296
|
},
|
|
297
297
|
{
|
|
298
298
|
"file": "src/commands/daemon-process.js",
|
|
299
|
-
"beforeBytes":
|
|
299
|
+
"beforeBytes": 5335,
|
|
300
300
|
"afterBytes": 2995
|
|
301
301
|
},
|
|
302
302
|
{
|
|
@@ -446,8 +446,8 @@
|
|
|
446
446
|
},
|
|
447
447
|
{
|
|
448
448
|
"file": "src/index.js",
|
|
449
|
-
"beforeBytes":
|
|
450
|
-
"afterBytes":
|
|
449
|
+
"beforeBytes": 39165,
|
|
450
|
+
"afterBytes": 19567
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
"file": "src/log-redaction.js",
|
|
@@ -466,8 +466,8 @@
|
|
|
466
466
|
},
|
|
467
467
|
{
|
|
468
468
|
"file": "src/manager/registry.js",
|
|
469
|
-
"beforeBytes":
|
|
470
|
-
"afterBytes":
|
|
469
|
+
"beforeBytes": 12065,
|
|
470
|
+
"afterBytes": 5661
|
|
471
471
|
},
|
|
472
472
|
{
|
|
473
473
|
"file": "src/manager/retired-external-channel-compat.js",
|
|
@@ -476,13 +476,13 @@
|
|
|
476
476
|
},
|
|
477
477
|
{
|
|
478
478
|
"file": "src/manager/runtime.js",
|
|
479
|
-
"beforeBytes":
|
|
480
|
-
"afterBytes":
|
|
479
|
+
"beforeBytes": 33902,
|
|
480
|
+
"afterBytes": 16475
|
|
481
481
|
},
|
|
482
482
|
{
|
|
483
483
|
"file": "src/manager/worker-ipc.js",
|
|
484
|
-
"beforeBytes":
|
|
485
|
-
"afterBytes":
|
|
484
|
+
"beforeBytes": 11457,
|
|
485
|
+
"afterBytes": 6210
|
|
486
486
|
},
|
|
487
487
|
{
|
|
488
488
|
"file": "src/manager/worker-markdown.js",
|
|
@@ -611,8 +611,8 @@
|
|
|
611
611
|
},
|
|
612
612
|
{
|
|
613
613
|
"file": "src/room/activation-service.js",
|
|
614
|
-
"beforeBytes":
|
|
615
|
-
"afterBytes":
|
|
614
|
+
"beforeBytes": 27657,
|
|
615
|
+
"afterBytes": 14988
|
|
616
616
|
},
|
|
617
617
|
{
|
|
618
618
|
"file": "src/room/attachment-cache.js",
|
|
@@ -646,8 +646,8 @@
|
|
|
646
646
|
},
|
|
647
647
|
{
|
|
648
648
|
"file": "src/room/managed-context.js",
|
|
649
|
-
"beforeBytes":
|
|
650
|
-
"afterBytes":
|
|
649
|
+
"beforeBytes": 4743,
|
|
650
|
+
"afterBytes": 2662
|
|
651
651
|
},
|
|
652
652
|
{
|
|
653
653
|
"file": "src/room/managed-prompt-composer.js",
|
|
@@ -661,8 +661,8 @@
|
|
|
661
661
|
},
|
|
662
662
|
{
|
|
663
663
|
"file": "src/room/managed-room-ipc.js",
|
|
664
|
-
"beforeBytes":
|
|
665
|
-
"afterBytes":
|
|
664
|
+
"beforeBytes": 14796,
|
|
665
|
+
"afterBytes": 7019
|
|
666
666
|
},
|
|
667
667
|
{
|
|
668
668
|
"file": "src/room/mirror-sync-service.js",
|
|
@@ -801,8 +801,8 @@
|
|
|
801
801
|
},
|
|
802
802
|
{
|
|
803
803
|
"file": "src/session/handlers/chat-recovery.js",
|
|
804
|
-
"beforeBytes":
|
|
805
|
-
"afterBytes":
|
|
804
|
+
"beforeBytes": 6525,
|
|
805
|
+
"afterBytes": 3449
|
|
806
806
|
},
|
|
807
807
|
{
|
|
808
808
|
"file": "src/session/handlers/chat-session-lifecycle.js",
|
|
@@ -811,8 +811,8 @@
|
|
|
811
811
|
},
|
|
812
812
|
{
|
|
813
813
|
"file": "src/session/handlers/chat.js",
|
|
814
|
-
"beforeBytes":
|
|
815
|
-
"afterBytes":
|
|
814
|
+
"beforeBytes": 35576,
|
|
815
|
+
"afterBytes": 15405
|
|
816
816
|
},
|
|
817
817
|
{
|
|
818
818
|
"file": "src/session/handlers/control.js",
|
|
@@ -879,10 +879,15 @@
|
|
|
879
879
|
"beforeBytes": 8089,
|
|
880
880
|
"afterBytes": 3965
|
|
881
881
|
},
|
|
882
|
+
{
|
|
883
|
+
"file": "src/session/manager-event-authority.js",
|
|
884
|
+
"beforeBytes": 498,
|
|
885
|
+
"afterBytes": 209
|
|
886
|
+
},
|
|
882
887
|
{
|
|
883
888
|
"file": "src/session/manager.js",
|
|
884
|
-
"beforeBytes":
|
|
885
|
-
"afterBytes":
|
|
889
|
+
"beforeBytes": 42657,
|
|
890
|
+
"afterBytes": 22491
|
|
886
891
|
},
|
|
887
892
|
{
|
|
888
893
|
"file": "src/session/native-cleanup-outbox.js",
|
|
@@ -906,13 +911,13 @@
|
|
|
906
911
|
},
|
|
907
912
|
{
|
|
908
913
|
"file": "src/session/queue-helpers.js",
|
|
909
|
-
"beforeBytes":
|
|
910
|
-
"afterBytes":
|
|
914
|
+
"beforeBytes": 8522,
|
|
915
|
+
"afterBytes": 4242
|
|
911
916
|
},
|
|
912
917
|
{
|
|
913
918
|
"file": "src/session/queue-message.js",
|
|
914
|
-
"beforeBytes":
|
|
915
|
-
"afterBytes":
|
|
919
|
+
"beforeBytes": 3257,
|
|
920
|
+
"afterBytes": 1987
|
|
916
921
|
},
|
|
917
922
|
{
|
|
918
923
|
"file": "src/session/queue-retry.js",
|
|
@@ -921,8 +926,8 @@
|
|
|
921
926
|
},
|
|
922
927
|
{
|
|
923
928
|
"file": "src/session/queue-storage.js",
|
|
924
|
-
"beforeBytes":
|
|
925
|
-
"afterBytes":
|
|
929
|
+
"beforeBytes": 2198,
|
|
930
|
+
"afterBytes": 1165
|
|
926
931
|
},
|
|
927
932
|
{
|
|
928
933
|
"file": "src/session/queue-types.js",
|
|
@@ -931,13 +936,13 @@
|
|
|
931
936
|
},
|
|
932
937
|
{
|
|
933
938
|
"file": "src/session/queue.js",
|
|
934
|
-
"beforeBytes":
|
|
935
|
-
"afterBytes":
|
|
939
|
+
"beforeBytes": 44695,
|
|
940
|
+
"afterBytes": 19911
|
|
936
941
|
},
|
|
937
942
|
{
|
|
938
943
|
"file": "src/session/queued-chat-dispatch.js",
|
|
939
|
-
"beforeBytes":
|
|
940
|
-
"afterBytes":
|
|
944
|
+
"beforeBytes": 3024,
|
|
945
|
+
"afterBytes": 1703
|
|
941
946
|
},
|
|
942
947
|
{
|
|
943
948
|
"file": "src/session/run-recovery/barrier.js",
|
|
@@ -26,8 +26,8 @@ declare const manifestSchema: z.ZodObject<{
|
|
|
26
26
|
}>>;
|
|
27
27
|
}, "strict", z.ZodTypeAny, {
|
|
28
28
|
id: string;
|
|
29
|
-
name: string;
|
|
30
29
|
schemaVersion: 1;
|
|
30
|
+
name: string;
|
|
31
31
|
args: string[];
|
|
32
32
|
command: string;
|
|
33
33
|
protocol: "acp";
|
|
@@ -40,8 +40,8 @@ declare const manifestSchema: z.ZodObject<{
|
|
|
40
40
|
} | undefined;
|
|
41
41
|
}, {
|
|
42
42
|
id: string;
|
|
43
|
-
name: string;
|
|
44
43
|
schemaVersion: 1;
|
|
44
|
+
name: string;
|
|
45
45
|
command: string;
|
|
46
46
|
protocol: "acp";
|
|
47
47
|
args?: string[] | undefined;
|
|
@@ -48,7 +48,7 @@ export type AgentSendResult = {
|
|
|
48
48
|
queuedSubmissionId?: string;
|
|
49
49
|
};
|
|
50
50
|
export type AgentSendContext = {
|
|
51
|
-
source: 'room_activation' | 'upgrade_recovery';
|
|
51
|
+
source: 'room_activation' | 'managed_private' | 'upgrade_recovery';
|
|
52
52
|
/** Marks internal recovery input; it must never be persisted as user-authored text. */
|
|
53
53
|
recovery?: 'upgrade';
|
|
54
54
|
};
|
package/dist/src/agents/codex.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{spawnSync as q}from"node:child_process";import{createInterface as w}from"node:readline";import{createHash as S,randomUUID as N}from"node:crypto";import p from"node:path";import{AgentAdapter as F,registerAgent as D}from"./adapter.js";import{resolveBuiltinCommand as
|
|
1
|
+
import{spawnSync as q}from"node:child_process";import{createInterface as w}from"node:readline";import{createHash as S,randomUUID as N}from"node:crypto";import p from"node:path";import{AgentAdapter as F,registerAgent as D}from"./adapter.js";import{resolveBuiltinCommand as T,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as L,buildManagedAgentProcessEnv as $,buildManagedNativeFullProcessEnv as U}from"../agent-env.js";import{ensurePlatformInstructionsFile as W}from"./platform-instructions.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as E}from"@shennian/wire";import{isCodexThreadTailInterrupted as B}from"../native-fusion/codex-files.js";import{resolveManagedAgentAdapterEnforcement as G,isManagedAgentNativeFull as R}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as z}from"../security/managed-agent-runtime.js";import{CODEX_MANAGED_MANAGER_DYNAMIC_TOOLS as H,CODEX_MANAGED_ROOM_DYNAMIC_TOOLS as J,CodexManagedRoomToolBinding as V}from"./codex-managed-room-tools.js";import{assertCodexManagedPermissionProfile as C,CODEX_MANAGED_RUNTIME_SECURITY as X,compileCodexManagedPermissions as K,extractCodexMcpServerNames as Q}from"./codex-managed-permissions.js";import{CODEX_APP_SERVER_CLIENT_INFO as Y,extractAppServerErrorMessage as _,formatCodexErrorMessage as j,formatTransientCodexStatus as Z,isCodexCollabAgentItem as ee,isCodexCollabAgentToolName as y,isCodexLegacyCollabAgentItem as te,isCodexUnsupportedEffortError as re,isMissingCodexRolloutError as v,isTransientCodexErrorMessage as ne,looksLikeCodexInteractiveAuthPrompt as se,looksLikeFatalCodexStderr as ie,normalizeCodexModelId as I,normalizeCodexReasoningEffort as oe,normalizeCodexStderr as A,normalizeTerminalText as ae,safeStringify as k,stripGitDirectiveArtifacts as de}from"./codex-utils.js";import{isCodexUnsupportedEffortError as Fe,isMissingCodexRolloutError as De,normalizeCodexModelId as Le,normalizeCodexReasoningEffort as $e}from"./codex-utils.js";z("codex",X);function P(o){const e=o instanceof Error?o.message:String(o);return/already has an active writer|thread-store conflict/i.test(e)}function le(o){const e=o instanceof Error?o.message:String(o);return/paginated(?:_threads|\s+(?:thread\s+)?history)?.*(?:not supported|unsupported|unavailable)/i.test(e)||/(?:not supported|unsupported|unavailable).*paginated(?:_threads|\s+(?:thread\s+)?history)?/i.test(e)}function ce(o){const e=o instanceof Error?o.message:String(o);return/unknown field [`'"]?excludeTurns/i.test(e)||/excludeTurns.*(?:not supported|unsupported|unavailable)/i.test(e)}function ue(o){const e=o instanceof Error?o.message:String(o);return/(?:method not found|unknown method).*(?:project\/list|project\/create)/i.test(e)||/(?:project\/list|project\/create).*(?:not supported|unsupported|unavailable)/i.test(e)}function he(o,e){const t=p.resolve(e);let r;for(const n of o)if(n.id)for(const i of n.roots??[]){if(!i.path)continue;const s=p.resolve(i.path),a=p.relative(s,t);!(a===""||!a.startsWith("..")&&!p.isAbsolute(a))||s.length<=(r?.rootLength??-1)||(r={id:n.id,rootLength:s.length})}return r?.id}function M(o,e){const t=e?.flatMap(n=>pe(n)).filter(n=>!/^https?:\/\//i.test(n.path)).filter(n=>n.path);return{type:"text",text:ge(o,e),text_elements:[],...t?.length?{local_images:t}:{}}}function pe(o){if(o.kind==="image")return o.previewData?[{path:o.path,data:o.previewData,mimeType:o.mimeType}]:[{path:o.path}];if(o.mimeType.startsWith("video/")){const e=me(o.path);return e?[{path:e}]:[]}return[]}function me(o){if(!o||/^https?:\/\//i.test(o))return null;const e=p.extname(o),r=`${e?o.slice(0,-e.length):o}.preview.png`;return q("ffmpeg",["-y","-ss","0","-i",o,"-frames:v","1","-vf","scale=320:-1",r],{stdio:"ignore",timeout:15e3}).status===0?r:null}function ge(o,e){const t=e?.filter(i=>i.kind!=="image").filter(i=>i.path&&!/^https?:\/\//i.test(i.path));if(!t?.length||t.every(i=>o.includes(i.path)))return o;const n=t.map((i,s)=>{const a=i.name||`attachment-${s+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${b(a)}${d}](<${i.path}>)`});return[o,"",...n].join(`
|
|
2
2
|
`)}function fe(o,e){const t=e?.filter(n=>n.path&&!/^https?:\/\//i.test(n.path)&&!o.includes(n.path));if(!t?.length)return o;const r=t.map((n,i)=>{const s=b(n.name||`attachment-${i+1}`);return n.kind==="image"?``:`[${s}](<${n.path}>)`});return[o,"",...r].join(`
|
|
3
|
-
`)}function ye(o,e,t){return["queue","--thread",o,"--message",fe(e,t)]}function b(o){return o.replace(/[[\]\\]/g,"\\$&")}class
|
|
4
|
-
${c}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const O=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,O)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,r){const n=e.trim(),i=t.replace(/\s+/g," ").trim();!n||!i||(this.agentSessionId=n,r?.trim()&&(this.workDir=r),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:n,name:i}))}async stop(){this.managedRoomTools.deactivateTurn(),await this.interruptActiveTurn().catch(()=>{}),await this.killProcess()}async getStatus(){const e=this.agentSessionId,t=this.activeTurnId;if(!e||!this.process)return{active:!1,runId:t,runPhase:null,canStop:!1};let r=null;try{r=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const n=r?.type==="active"||!!t;return{active:n,runId:t,runPhase:n?this.mapThreadStatusToRunPhase(r):null,canStop:!!(n&&t&&this.process)}}spawnCodex(e){const t=v("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const r=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=r,w({input:r.stdout}).on("line",s=>{if(s.trim())try{const a=JSON.parse(s);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:s})}});let i="";r.stderr?.on("data",s=>{i+=s.toString()}),r.on("close",s=>{this.process===r&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(s,i))}),r.on("error",s=>{this.process===r&&(this.process=null,s.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",s))})}spawnAppServer(){const e=v("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],r=this.options.modelInstructionsFile??W(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const n=x(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=n,this.stderrBuf="",this.appServerStartupError=null,w({input:n.stdout}).on("line",s=>{if(!s.trim())return;let a;try{a=JSON.parse(s)}catch{this.handleAppServerNonJsonStdout(s);return}this.handleAppServerMessage(a)}),n.stderr?.on("data",s=>{this.stderrBuf+=s.toString()}),n.on("close",s=>{if(this.process!==n)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(A(this.stderrBuf)||`codex app-server exited with code ${s}`)),this.terminalState==="open"&&a&&this.handleProcessClose(s,this.stderrBuf)}),n.on("error",s=>{this.process===n&&(this.process=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(s),s.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",s))})}async ensureAppServer(e){return this.appServerReady?this.appServerReady:(this.spawnAppServer(),this.appServerReady=this.initializeAppServer(e).catch(async t=>{throw this.appServerReady=null,await this.killProcess(),t}),this.appServerReady)}async initializeAppServer(e){const t=I(e);await this.sendRpc("initialize",{clientInfo:Y,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy&&!R(this.managedAgentPolicy)?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedAgentPolicy&&r&&!this.managedRoomTools.isConfigured()&&(this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),!this.managedRoomTools.isConfigured()))throw new Error("codex_managed_room_tool_context_unavailable");if(this.agentSessionId&&this.managedAgentPolicy&&r&&this.agentSessionId!==this.managedRoomToolThreadId){if(!this.managedRoomTools.isConfigured())throw new Error("codex_managed_room_tool_context_unavailable");this.agentSessionId=null}if(this.agentSessionId){const l={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...t?{model:t}:{}};let c;try{c=await this.sendRpc("thread/resume",{...l,excludeTurns:!0})}catch(h){if(!ce(h))throw h;c=await this.sendRpc("thread/resume",l)}this.agentSessionId=c.thread?.id??this.agentSessionId,this.managedPermissionConfig&&C(c,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&r&&(this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId);return}const n=this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession,i=n?void 0:await this.resolveCodexProjectId(),s={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",...n?{}:{threadSource:"user"},experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedAgentPolicy&&r?{dynamicTools:this.extraEnv.SHENNIAN_MANAGER_SESSION_ID?H:J}:{},...i?{projectId:i}:{},...t?{model:t}:{}};let a;if(n&&!this.managedPermissionConfig)a=await this.sendRpc("thread/start",s);else try{a=await this.sendRpc("thread/start",{...s,historyMode:"paginated"})}catch(l){throw le(l)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5728 Codex Desktop \u4E2D\u7EE7\u7EED\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u964D\u7EA7\u521B\u5EFA\u4E0D\u53EF\u89C1\u7684 legacy \u4F1A\u8BDD\u3002",{cause:l}):l}const d=a.thread?.id;if(!d)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&C(a,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&r&&(this.managedRoomTools.bindThread(d),this.managedRoomToolThreadId=d),this.agentSessionId=d}async buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)throw new Error("managed_agent_policy_missing");const t=e.adapterPolicy.externalCapabilities.mcp?[]:Q(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return K(G(e),e.adapterPolicy.workDir,{configuredMcpServerNames:t,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}async resolveCodexProjectId(){const e=this.workDir?.trim();if(e)try{const t=[];let r;do{const l=await this.sendRpc("project/list",{limit:100,...r?{cursor:r}:{}});t.push(...l.data??[]),r=l.nextCursor}while(r);const n=he(t,e);if(n)return n;const i=p.resolve(e),s=`shennian-project-${S("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:s,name:p.basename(i)||i,roots:[{path:i}],metadata:{createdBy:"Shennian"}})).project?.id;if(!d)throw new Error("codex app-server project/create returned no project id");return d}catch(t){throw ue(t)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5F52\u5C5E\u5230 Codex Desktop \u9879\u76EE\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u76F4\u63A5\u4FEE\u6539 Codex Desktop \u7684\u79C1\u6709\u4FA7\u680F\u7D22\u5F15\u3002",{cause:t}):t}}async startTurnWithRecovery(e,t,r,n,i){try{return await this.startTurn(e,t,r,n,i)}catch(s){if(!T(s))throw s;if(this.managedAgentPolicy||this.managedPermissionConfig){this.logManagedThreadLifecycle("rollout.missing",{threadId:e,recovery:"recreate"}),await this.killProcess(!0),this.agentSessionId=null,this.managedRoomToolThreadId=null,await this.ensureAppServer(r);const a=this.agentSessionId;if(!a)throw new Error("codex app-server did not recreate the managed thread",{cause:s});return this.logManagedThreadLifecycle("thread.recreated",{previousThreadId:e,threadId:a}),this.emitEvent({state:"start",agentSessionId:a}),await this.startFreshManagedTurnWithReadinessRetry(a,t,r,n,i)}return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,n,i)}}async startFreshManagedTurnWithReadinessRetry(e,t,r,n,i){const s=[0,100,300,1e3];let a;for(let d=0;d<s.length;d+=1){const l=s[d];l>0&&await new Promise(c=>setTimeout(c,l));try{const c=await this.startTurn(e,t,r,n,i);return d>0&&this.logManagedThreadLifecycle("rollout.ready",{threadId:e,attempt:d+1}),c}catch(c){if(a=c,!T(c)||d===s.length-1)throw c;this.logManagedThreadLifecycle("rollout.retry",{threadId:e,attempt:d+2,delayMs:s[d+1]})}}throw a}logManagedThreadLifecycle(e,t){console.error(`[codex.managed] ${JSON.stringify({event:e,...t})}`)}async startTurn(e,t,r,n,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[M(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...n?{effort:n}:{}})}catch(s){throw n&&re(s)?new Error(`Codex app-server does not accept reasoning effort "${n}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:s}):s}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),!(!e||!t||!this.process)&&await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3)}sendRpc(e,t,r=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const n=this.process;if(!n?.stdin)return Promise.reject(new Error("codex app-server is not running"));const i=this.rpcSeq++,s=JSON.stringify({id:i,method:e,params:t});return new Promise((a,d)=>{const l=setTimeout(()=>{this.pendingRequests.delete(i),d(new Error(`codex app-server request timed out: ${e}`))},r);this.pendingRequests.set(i,{resolve:a,reject:d,timer:l}),n.stdin.write(`${s}
|
|
5
|
-
`,c=>{c&&(clearTimeout(l),this.pendingRequests.delete(i),d(c))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const r=typeof e.id=="number"?this.pendingRequests.get(e.id):void 0;r&&(clearTimeout(r.timer),this.pendingRequests.delete(e.id),e.error?r.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):r.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,n=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";n&&(this.activeTurnId=n,this.managedRoomCapabilityExpectedForTurn&&this.managedRoomToolThreadId===i&&!this.managedRoomTools.bindTurn(i,n)&&(this.managedRoomTurnBindingFailed=!0,this.emitErrorIfOpen("codex_managed_room_turn_binding_failed")));break}case"thread/status/changed":{const r=typeof t.threadId=="string"?t.threadId:"";if(r&&this.agentSessionId&&r!==this.agentSessionId)break;const n=typeof t.status=="object"&&t.status!==null?t.status:null;n?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(n)});break}case"item/agentMessage/delta":{const r=typeof t.itemId=="string"?t.itemId:"";r&&this.deltaItemIds.add(r),typeof t.delta=="string"&&this.emitText(t.delta);break}case"item/completed":this.handleAppServerCompletedItem(t);break;case"thread/tokenUsage/updated":this.handleAppServerUsage(t);break;case"turn/completed":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,n=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||n===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"turn/failed":{const r=_(t);if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r||"turn failed");break}case"error":{const r=_(t);if(ne(r)){this.emitTransientStatus(r);return}if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r);break}}}scheduleManagedAsyncRolloutRecovery(e){if(!this.managedAgentPolicy&&!this.managedPermissionConfig||!this.managedTurnReplay||!
|
|
3
|
+
`)}function ye(o,e,t){return["queue","--thread",o,"--message",fe(e,t)]}function b(o){return o.replace(/[[\]\\]/g,"\\$&")}class ve extends F{options;get supportsActiveInput(){return!0}async injectActiveInput(e){const t=this.agentSessionId,r=this.activeTurnId;if(!t||!r||!this.process)throw new Error("codex_active_turn_unavailable");const n=await this.sendRpc("turn/steer",{threadId:t,expectedTurnId:r,input:[M(e.text,e.attachments)],...e.clientMessageId?{clientUserMessageId:e.clientMessageId}:{}});if(n.turnId&&n.turnId!==r)throw new Error("codex_active_turn_changed")}type="codex";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;workDir=null;seq=0;runId="";pendingUsage;agentSessionId=null;hasEmittedText=!1;textTail="";terminalState="open";forceCloseTimer=null;stderrBuf="";rpcSeq=1;pendingRequests=new Map;appServerReady=null;appServerStartupError=null;activeTurnId=null;deltaItemIds=new Set;lastTransientStatus="";agentSystemPrompt="";managedAgentPolicy=null;managedPermissionConfig=null;configurationRestartRequired=!1;managedRoomTools=new V;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;managedRoomCapabilityExpectedForTurn=!1;managedRoomTurnBindingFailed=!1;preparedExternalOwnerThreadId=null;managedTurnReplay=null;managedAsyncRecovery=null;persistNativeSession=!0;nativeSessionVisibility="user";constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){const t=this.configurationFingerprint();this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),("managedRoomContextToken"in e||"managedRoomExecutor"in e)&&(this.managedRoomTools.deactivateTurn(),this.configuredManagedRoomContextToken=e.managedRoomContextToken?.trim()||null,this.configuredManagedRoomExecutor=e.managedRoomExecutor??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir);const r=this.configurationFingerprint();t!==r&&this.process&&(this.configurationRestartRequired=!0)}configurationFingerprint(){const e=Object.keys(this.extraEnv).sort().map(t=>[t,this.extraEnv[t]??null]);return S("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return L({...e,...this.extraEnv});if(R(this.managedAgentPolicy))return U({...e,...this.extraEnv});const t=Object.fromEntries(Object.entries(this.extraEnv).filter(([r])=>r!=="SHENNIAN_MANAGED_ROOM_CONTEXT"&&r!=="SHENNIAN_MANAGED_ROOM_IPC_ROOT"));return $({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r,n){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=n?.persistNativeSession!==!1,this.nativeSessionVisibility=n?.nativeSessionVisibility??"user",r&&(this.agentSessionId=r)}async prepareSend(e,t,r,n,i,s){const a=i?.trim()??"";if(a!==this.agentSystemPrompt&&(this.agentSystemPrompt=a,this.process&&await this.killProcess(!0)),!(!this.agentSessionId||this.activeTurnId)){this.preparedExternalOwnerThreadId=null;try{await this.ensureAppServer(I(t))}catch(d){if(!P(d))throw d;if(s?.source==="room_activation"||s?.source==="managed_private")throw new Error("codex_managed_thread_owned_externally",{cause:d});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(E,{cause:d});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,n,i,s){if((s?.source==="room_activation"||s?.source==="managed_private")&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if((s?.source==="room_activation"||s?.source==="managed_private")&&(!this.configuredManagedRoomContextToken||!this.configuredManagedRoomExecutor))throw new Error("codex_managed_room_tool_context_unavailable");const a=s?.source==="room_activation"||s?.source==="managed_private";this.managedRoomCapabilityExpectedForTurn=!1,this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess(!0)),this.configurationRestartRequired&&this.process&&await this.killProcess(!0),this.configurationRestartRequired=!1;const d=I(t),l=oe(r),c=i?.trim()??"";if(c!==this.agentSystemPrompt&&(this.agentSystemPrompt=c,this.process&&await this.killProcess(!0)),(s?.source==="room_activation"||s?.source==="managed_private")&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=N(),this.seq=0,this.resetRunState(),this.agentSessionId&&this.preparedExternalOwnerThreadId===this.agentSessionId)return this.preparedExternalOwnerThreadId=null,{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,n)};this.managedRoomCapabilityExpectedForTurn=a;try{await this.ensureAppServer(d)}catch(u){if(!this.agentSessionId||!P(u))throw u;if(s?.source==="room_activation"||s?.source==="managed_private")throw new Error("codex_managed_thread_owned_externally",{cause:u});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(E,{cause:u});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,n)}}const h=this.agentSessionId;if(!h)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:h}),this.managedTurnReplay=this.managedPermissionConfig?{text:e,modelId:d,reasoningEffort:l,attachments:n,recoveryCount:0}:null;const m=await this.startTurnWithRecovery(h,e,d,l,n),g=this.agentSessionId??h;if(this.activeTurnId=m.turn?.id??null,this.managedRoomTurnBindingFailed)throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");if(this.managedRoomCapabilityExpectedForTurn&&this.managedRoomToolThreadId===g&&this.activeTurnId&&!this.managedRoomTools.bindTurn(g,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}isThreadTailInterrupted(e){return this.options.isThreadTailInterrupted?.(e)??B(e)}async enqueueForExternalOwner(e,t,r){const n=T("codex");if(!n)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=ye(e,t,r),s=x(n,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let l="",c="",h=!1;const m=(u,f)=>{h||(h=!0,clearTimeout(g),u?d(u):a(f))},g=setTimeout(()=>{s.kill("SIGTERM"),m(new Error("Codex Desktop message queue timed out"))},6e4);g.unref?.(),s.stdout?.on("data",u=>{l+=u.toString()}),s.stderr?.on("data",u=>{c+=u.toString()}),s.once("error",u=>m(u)),s.once("close",u=>{const f=`${l}
|
|
4
|
+
${c}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const O=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,O)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,r){const n=e.trim(),i=t.replace(/\s+/g," ").trim();!n||!i||(this.agentSessionId=n,r?.trim()&&(this.workDir=r),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:n,name:i}))}async stop(){this.managedRoomTools.deactivateTurn(),await this.interruptActiveTurn().catch(()=>{}),await this.killProcess()}async getStatus(){const e=this.agentSessionId,t=this.activeTurnId;if(!e||!this.process)return{active:!1,runId:t,runPhase:null,canStop:!1};let r=null;try{r=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const n=r?.type==="active"||!!t;return{active:n,runId:t,runPhase:n?this.mapThreadStatusToRunPhase(r):null,canStop:!!(n&&t&&this.process)}}spawnCodex(e){const t=T("codex");if(!t){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const r=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=r,w({input:r.stdout}).on("line",s=>{if(s.trim())try{const a=JSON.parse(s);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:s})}});let i="";r.stderr?.on("data",s=>{i+=s.toString()}),r.on("close",s=>{this.process===r&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(s,i))}),r.on("error",s=>{this.process===r&&(this.process=null,s.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",s))})}spawnAppServer(){const e=T("codex");if(!e){this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?'));return}const t=["app-server","--listen","stdio://"],r=this.options.modelInstructionsFile??W(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const n=x(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=n,this.stderrBuf="",this.appServerStartupError=null,w({input:n.stdout}).on("line",s=>{if(!s.trim())return;let a;try{a=JSON.parse(s)}catch{this.handleAppServerNonJsonStdout(s);return}this.handleAppServerMessage(a)}),n.stderr?.on("data",s=>{this.stderrBuf+=s.toString()}),n.on("close",s=>{if(this.process!==n)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(A(this.stderrBuf)||`codex app-server exited with code ${s}`)),this.terminalState==="open"&&a&&this.handleProcessClose(s,this.stderrBuf)}),n.on("error",s=>{this.process===n&&(this.process=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(s),s.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",s))})}async ensureAppServer(e){return this.appServerReady?this.appServerReady:(this.spawnAppServer(),this.appServerReady=this.initializeAppServer(e).catch(async t=>{throw this.appServerReady=null,await this.killProcess(),t}),this.appServerReady)}async initializeAppServer(e){const t=I(e);await this.sendRpc("initialize",{clientInfo:Y,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy&&!R(this.managedAgentPolicy)?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedAgentPolicy&&r&&!this.managedRoomTools.isConfigured()&&(this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),!this.managedRoomTools.isConfigured()))throw new Error("codex_managed_room_tool_context_unavailable");if(this.agentSessionId&&this.managedAgentPolicy&&r&&this.agentSessionId!==this.managedRoomToolThreadId){if(!this.managedRoomTools.isConfigured())throw new Error("codex_managed_room_tool_context_unavailable");this.agentSessionId=null}if(this.agentSessionId){const l={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...t?{model:t}:{}};let c;try{c=await this.sendRpc("thread/resume",{...l,excludeTurns:!0})}catch(h){if(!ce(h))throw h;c=await this.sendRpc("thread/resume",l)}this.agentSessionId=c.thread?.id??this.agentSessionId,this.managedPermissionConfig&&C(c,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&r&&(this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId);return}const n=this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession,i=n?void 0:await this.resolveCodexProjectId(),s={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",...n?{}:{threadSource:"user"},experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedAgentPolicy&&r?{dynamicTools:this.extraEnv.SHENNIAN_MANAGER_SESSION_ID?H:J}:{},...i?{projectId:i}:{},...t?{model:t}:{}};let a;if(n&&!this.managedPermissionConfig)a=await this.sendRpc("thread/start",s);else try{a=await this.sendRpc("thread/start",{...s,historyMode:"paginated"})}catch(l){throw le(l)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5728 Codex Desktop \u4E2D\u7EE7\u7EED\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u964D\u7EA7\u521B\u5EFA\u4E0D\u53EF\u89C1\u7684 legacy \u4F1A\u8BDD\u3002",{cause:l}):l}const d=a.thread?.id;if(!d)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&C(a,this.managedPermissionConfig.profileId),this.managedAgentPolicy&&r&&(this.managedRoomTools.bindThread(d),this.managedRoomToolThreadId=d),this.agentSessionId=d}async buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)throw new Error("managed_agent_policy_missing");const t=e.adapterPolicy.externalCapabilities.mcp?[]:Q(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return K(G(e),e.adapterPolicy.workDir,{configuredMcpServerNames:t,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}async resolveCodexProjectId(){const e=this.workDir?.trim();if(e)try{const t=[];let r;do{const l=await this.sendRpc("project/list",{limit:100,...r?{cursor:r}:{}});t.push(...l.data??[]),r=l.nextCursor}while(r);const n=he(t,e);if(n)return n;const i=p.resolve(e),s=`shennian-project-${S("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:s,name:p.basename(i)||i,roots:[{path:i}],metadata:{createdBy:"Shennian"}})).project?.id;if(!d)throw new Error("codex app-server project/create returned no project id");return d}catch(t){throw ue(t)?new Error("\u5F53\u524D Codex CLI \u4E0D\u652F\u6301\u521B\u5EFA\u53EF\u5F52\u5C5E\u5230 Codex Desktop \u9879\u76EE\u7684\u65B0\u4F1A\u8BDD\uFF0C\u8BF7\u624B\u52A8\u5347\u7EA7 Codex CLI \u540E\u91CD\u8BD5\u3002\u795E\u5FF5\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7\uFF0C\u4E5F\u4E0D\u4F1A\u76F4\u63A5\u4FEE\u6539 Codex Desktop \u7684\u79C1\u6709\u4FA7\u680F\u7D22\u5F15\u3002",{cause:t}):t}}async startTurnWithRecovery(e,t,r,n,i){try{return await this.startTurn(e,t,r,n,i)}catch(s){if(!v(s))throw s;if(this.managedAgentPolicy||this.managedPermissionConfig){this.logManagedThreadLifecycle("rollout.missing",{threadId:e,recovery:"recreate"}),await this.killProcess(!0),this.agentSessionId=null,this.managedRoomToolThreadId=null,await this.ensureAppServer(r);const a=this.agentSessionId;if(!a)throw new Error("codex app-server did not recreate the managed thread",{cause:s});return this.logManagedThreadLifecycle("thread.recreated",{previousThreadId:e,threadId:a}),this.emitEvent({state:"start",agentSessionId:a}),await this.startFreshManagedTurnWithReadinessRetry(a,t,r,n,i)}return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,n,i)}}async startFreshManagedTurnWithReadinessRetry(e,t,r,n,i){const s=[0,100,300,1e3];let a;for(let d=0;d<s.length;d+=1){const l=s[d];l>0&&await new Promise(c=>setTimeout(c,l));try{const c=await this.startTurn(e,t,r,n,i);return d>0&&this.logManagedThreadLifecycle("rollout.ready",{threadId:e,attempt:d+1}),c}catch(c){if(a=c,!v(c)||d===s.length-1)throw c;this.logManagedThreadLifecycle("rollout.retry",{threadId:e,attempt:d+2,delayMs:s[d+1]})}}throw a}logManagedThreadLifecycle(e,t){console.error(`[codex.managed] ${JSON.stringify({event:e,...t})}`)}async startTurn(e,t,r,n,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[M(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...n?{effort:n}:{}})}catch(s){throw n&&re(s)?new Error(`Codex app-server does not accept reasoning effort "${n}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:s}):s}}async interruptActiveTurn(){const e=this.agentSessionId,t=this.activeTurnId;this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),!(!e||!t||!this.process)&&await this.sendRpc("turn/interrupt",{threadId:e,turnId:t},5e3)}sendRpc(e,t,r=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const n=this.process;if(!n?.stdin)return Promise.reject(new Error("codex app-server is not running"));const i=this.rpcSeq++,s=JSON.stringify({id:i,method:e,params:t});return new Promise((a,d)=>{const l=setTimeout(()=>{this.pendingRequests.delete(i),d(new Error(`codex app-server request timed out: ${e}`))},r);this.pendingRequests.set(i,{resolve:a,reject:d,timer:l}),n.stdin.write(`${s}
|
|
5
|
+
`,c=>{c&&(clearTimeout(l),this.pendingRequests.delete(i),d(c))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const r=typeof e.id=="number"?this.pendingRequests.get(e.id):void 0;r&&(clearTimeout(r.timer),this.pendingRequests.delete(e.id),e.error?r.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):r.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,n=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";n&&(this.activeTurnId=n,this.managedRoomCapabilityExpectedForTurn&&this.managedRoomToolThreadId===i&&!this.managedRoomTools.bindTurn(i,n)&&(this.managedRoomTurnBindingFailed=!0,this.emitErrorIfOpen("codex_managed_room_turn_binding_failed")));break}case"thread/status/changed":{const r=typeof t.threadId=="string"?t.threadId:"";if(r&&this.agentSessionId&&r!==this.agentSessionId)break;const n=typeof t.status=="object"&&t.status!==null?t.status:null;n?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(n)});break}case"item/agentMessage/delta":{const r=typeof t.itemId=="string"?t.itemId:"";r&&this.deltaItemIds.add(r),typeof t.delta=="string"&&this.emitText(t.delta);break}case"item/completed":this.handleAppServerCompletedItem(t);break;case"thread/tokenUsage/updated":this.handleAppServerUsage(t);break;case"turn/completed":{const r=typeof t.turn=="object"&&t.turn!==null?t.turn:null,n=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||n===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"turn/failed":{const r=_(t);if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r||"turn failed");break}case"error":{const r=_(t);if(ne(r)){this.emitTransientStatus(r);return}if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r);break}}}scheduleManagedAsyncRolloutRecovery(e){if(!this.managedAgentPolicy&&!this.managedPermissionConfig||!this.managedTurnReplay||!v(new Error(e)))return!1;if(this.managedAsyncRecovery)return!0;if(this.managedTurnReplay.recoveryCount>=2)return!1;this.managedTurnReplay.recoveryCount+=1;const t=this.managedTurnReplay.recoveryCount;return this.logManagedThreadLifecycle("rollout.failed-async",{threadId:this.agentSessionId,recoveryCount:t}),this.managedAsyncRecovery=this.recoverManagedTurnAfterAsyncRolloutFailure().catch(r=>{const n=v(r)?"no rollout found after managed thread recovery":r instanceof Error?r.message:"managed thread recovery failed";this.emitErrorIfOpen(n)}).finally(()=>{this.managedAsyncRecovery=null}),!0}async recoverManagedTurnAfterAsyncRolloutFailure(){const e=this.managedTurnReplay,t=this.agentSessionId;if(!e||!t||this.terminalState!=="open")return;this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),await this.killProcess(!0),this.agentSessionId=null,this.managedRoomToolThreadId=null,await this.ensureAppServer(e.modelId);const r=this.agentSessionId;if(!r)throw new Error("codex app-server did not recreate the managed thread");this.logManagedThreadLifecycle("thread.recreated-async",{previousThreadId:t,threadId:r,recoveryCount:e.recoveryCount}),this.emitEvent({state:"start",agentSessionId:r});const n=await this.startFreshManagedTurnWithReadinessRetry(r,e.text,e.modelId,e.reasoningEffort,e.attachments);if(this.activeTurnId=n.turn?.id??null,this.managedRoomTurnBindingFailed)throw new Error("codex_managed_room_turn_binding_failed");if(this.managedRoomCapabilityExpectedForTurn&&this.managedRoomToolThreadId===r&&this.activeTurnId&&!this.managedRoomTools.bindTurn(r,this.activeTurnId))throw new Error("codex_managed_room_turn_binding_failed")}async handleManagedRoomToolCall(e,t){const r=await this.managedRoomTools.execute(t);if(this.writeAppServerResponse(e,r),r.success&&t.tool==="shennian_room_send"){const n=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:null,i=typeof n?.text=="string"?n.text.trim():"";i&&this.emitText(i,"block")}}writeAppServerResponse(e,t){const r=this.process;r?.stdin&&r.stdin.write(`${JSON.stringify({id:e,result:t})}
|
|
6
6
|
`)}mapThreadStatusToRunPhase(e){const t=e?.type==="active"&&Array.isArray(e.activeFlags)?e.activeFlags:[];return t.includes("waitingOnApproval")?"waiting_approval":t.includes("waitingOnUserInput")?"waiting_user_input":"thinking"}handleAppServerNonJsonStdout(e){const t=ae(e);se(t)&&this.failAppServerStartup(new Error("Codex \u672A\u767B\u5F55\u3002\u8BF7\u5148\u5728\u8FD9\u53F0\u7535\u8111\u7684\u7EC8\u7AEF\u8FD0\u884C `codex`\uFF0C\u6309\u63D0\u793A\u5B8C\u6210 ChatGPT \u767B\u5F55\u6216\u914D\u7F6E API key\uFF0C\u7136\u540E\u518D\u56DE\u5230\u795E\u5FF5\u53D1\u9001\u6D88\u606F\u3002"))}failAppServerStartup(e){this.appServerStartupError||(this.appServerStartupError=e,this.rejectAllPending(e),this.process?.kill("SIGTERM"))}handleAppServerCompletedItem(e){const t=typeof e.item=="object"&&e.item!==null?e.item:null;if(!(!t||typeof t.type!="string")&&!ee(t))switch(t.type){case"agentMessage":{const r=typeof t.id=="string"?t.id:"";if(r&&this.deltaItemIds.has(r))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const r=typeof t.command=="string"?t.command:"",n=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";r&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:r}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[n,t.exitCode!=null?`
|
|
7
7
|
(exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const r=typeof t.tool=="string"?t.tool:t.type;if(y(r))return;const n=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:r,args:n}),this.emitEvent({state:"tool-result",name:r,result:k(t.error??t.result??t.contentItems??"")});break}case"fileChange":break;case"approvalRequest":case"approval_request":case"permissionRequest":case"permission_request":case"userInputRequired":case"user_input_required":{this.emitApprovalPending(t);break}}}handleAppServerUsage(e){const t=typeof e.tokenUsage=="object"&&e.tokenUsage!==null?e.tokenUsage:null,r=typeof t?.last=="object"&&t.last!==null?t.last:null,n=typeof t?.total=="object"&&t.total!==null?t.total:null,i=r??n;if(!i)return;const s=typeof i.inputTokens=="number"?i.inputTokens:void 0,a=typeof i.outputTokens=="number"?i.outputTokens:void 0;(s!=null||a!=null)&&(this.pendingUsage={inputTokens:s??0,outputTokens:a??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!te(t))switch(t.type){case"agent_message":case"reasoning":{e.type==="item.completed"&&t.text&&this.emitText(t.text,"block");break}case"command_execution":{if(e.type==="item.started"&&t.command&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:t.command}}),e.type==="item.completed"){const r=[t.aggregated_output??"",t.exit_code!=null?`
|
|
8
8
|
(exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:r.join("").trim()||String(t.status??"")})}break}case"mcp_tool_call":{if(t.tool&&y(t.tool))return;if(e.type==="item.started"&&this.emitEvent({state:"tool-call",name:t.tool??"mcp_tool",args:{server:t.server,...t.arguments&&typeof t.arguments=="object"?t.arguments:{}}}),e.type==="item.completed"){const r=t.error&&typeof t.error=="object"?t.error.message:void 0;this.emitEvent({state:"tool-result",name:t.tool??"mcp_tool",result:r??k(t.result)})}break}case"file_change":break;case"web_search":{e.type==="item.completed"&&t.query&&this.emitText(`[web_search] ${t.query}`,"block");break}case"todo_list":{if(e.type==="item.completed"&&t.items?.length){const r=t.items.map(n=>`- [${n.completed?"x":" "}] ${n.text}`).join(`
|
|
@@ -14,4 +14,4 @@ ${r}`:this.textTail.endsWith(`
|
|
|
14
14
|
|
|
15
15
|
`)||(r=`
|
|
16
16
|
|
|
17
|
-
${r}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+r).slice(-4),this.emitEvent({state:"delta",text:r}))}scheduleProcessClose(){const e=this.process;!e||this.forceCloseTimer||(this.forceCloseTimer=setTimeout(()=>{this.forceCloseTimer=null,this.process===e&&(e.kill("SIGTERM"),setTimeout(()=>{this.process===e&&e.kill("SIGKILL")},1e3))},250))}clearForceCloseTimer(){this.forceCloseTimer&&(clearTimeout(this.forceCloseTimer),this.forceCloseTimer=null)}}D("codex",()=>new
|
|
17
|
+
${r}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+r).slice(-4),this.emitEvent({state:"delta",text:r}))}scheduleProcessClose(){const e=this.process;!e||this.forceCloseTimer||(this.forceCloseTimer=setTimeout(()=>{this.forceCloseTimer=null,this.process===e&&(e.kill("SIGTERM"),setTimeout(()=>{this.process===e&&e.kill("SIGKILL")},1e3))},250))}clearForceCloseTimer(){this.forceCloseTimer&&(clearTimeout(this.forceCloseTimer),this.forceCloseTimer=null)}}D("codex",()=>new ve);export{ve as CodexAdapter,ye as buildCodexQueueArgs,fe as buildCodexQueuedText,P as isCodexActiveWriterError,ce as isCodexExcludeTurnsUnsupportedError,le as isCodexPaginatedHistoryUnsupportedError,ue as isCodexProjectApiUnsupportedError,Fe as isCodexUnsupportedEffortError,De as isMissingCodexRolloutError,Le as normalizeCodexModelId,$e as normalizeCodexReasoningEffort,he as selectCodexProjectIdForWorkDir};
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as fe}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ve}from"./schedules/personal-context.js";import{executeServerSchedule as Z,deliverServerSchedule as Ie}from"./schedules/server-client.js";import{Command as Se}from"commander";import R from"chalk";import ye from"node:os";import j from"node:fs";import{loadConfig as m,saveConfig as we,getConfigPath as Ce,getShennianDir as be,resolveShennianPath as H,normalizeAutoUpgradePolicy as ke}from"./config/index.js";import{CliRelayClient as Re}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as Pe,sanitizeCliLogValue as Ee}from"./log-redaction.js";import{registerPairCommand as Ae,runSmartStart as Te}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as De,clearDaemonLauncher as oe,findRunningDaemonProcessIds as Me,isRemoteAccessDisabled as ne,prepareRemoteAccessPause as _e,stopPreparedRemoteAccessService as xe,isIsolatedDaemonE2eInstance as $e,getDaemonStatus as Ne,registerDaemonCommand as Oe,writeDaemonPid as re,writeDaemonLauncher as te}from"./commands/daemon.js";import{registerRuntimeCommand as Ve}from"./commands/runtime.js";import{registerAgentCommand as Le}from"./commands/agent.js";import{registerManagerCommand as je}from"./commands/manager.js";import{handleUpgradeStart as He,registerUpgradeCommand as Be}from"./commands/upgrade.js";import{registerToolsCommand as Fe}from"./commands/tools.js";import{registerIntegrationCommand as Ge}from"./commands/integration.js";import{registerRoomCommand as qe}from"./commands/room.js";import{SessionManager as ze}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Je}from"./session/handlers/project-fs.js";import{SERVERS as B,regionToUrl as Ke,urlToRegion as We}from"./region.js";import{clearUpgradeAttempt as Ye,getCurrentVersion as ae,handleStartupCrashCheck as Qe,readUpgradeAttempt as Ze}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Xe}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as eo}from"./upgrade/updater-bootstrap.js";import{launchUpdater as oo,markUpdaterHealthy as no}from"./upgrade/updater-launcher.js";import{detectAgents as ro}from"./agents/detect.js";import{augmentProcessPath as ie}from"./env-path.js";import{acquireDaemonInstanceLock as to}from"./commands/daemon-instance-lock.js";ie();const F=ae();import{getCachedAgentInfos as ao,resolveAgentInfos as io}from"./agents/model-registry.js";import{initCliLogReporter as so,reportLog as P,reportUpgradeAggregateMetric as co}from"./log-reporter.js";import{isNativeFusionEnabled as lo}from"./native-fusion/config.js";import{NativeSessionFusionService as mo}from"./native-fusion/service.js";import{startDaemonLogRetention as po}from"./daemon-log.js";import{startDaemonIpcServer as se}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as go,resolveManagedRoomIpcPaths as uo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as ho}from"./daemon-ipc/control.js";import{DAEMON_IPC_VERSION as fo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as vo}from"./room/session-registry.js";import{HttpRoomBindingClient as Io}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as So}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as yo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as wo}from"./room/mirror-sync-service.js";import{NativeSourceDeletionStore as Co}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as bo,isCurrentRegistration as ko}from"./room/registration-authority.js";import{RoomActivationService as Ro,resolveManagedAgentPolicy as Po}from"./room/activation-service.js";import{RoomActivationAudit as Eo}from"./room/activation-audit.js";import{RoomAttachmentCache as Ao}from"./room/attachment-cache.js";import{RoomAttachmentUploader as To}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Uo}from"./room/managed-context.js";import{createManagedRoomExecutor as Do}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ce,SESSION_SYNC_ADVERTISED_CAPABILITIES as de}from"@shennian/wire";import{getDaemonInstallationId as Mo}from"./session/store.js";import{synchronizeMachinePersonalAuthority as _o}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as le,ensurePersonalDataEpoch as xo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as $o,DeletionSyncTerminalError as No}from"./session/deletion-sync-state.js";const me=be(),Oo=H("daemon.pid"),Vo=H("daemon.instance.lock");function Lo(){try{const
|
|
2
|
-
`),process.exitCode=1}function Ho(t){const s=[`trigger=${t.trigger??"unknown"}`,`phase=${t.phase}`,`code=${t.code??"unknown"}`,`attempt=${t.reconnectAttempt}`];return t.reason&&s.push(`reason=${t.reason}`),t.error&&s.push(`error=${t.error}`),s.join(" ")}const i=new Se;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),Ge(i),qe(i),fe(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",ye.hostname()).action(async t=>{const s=m(),p=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Te(p,t.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 t=>{const s=H("env.json");try{const e=JSON.parse(j.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ie()}catch{}Pe(X(process.env,m()));const p=po();ne()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(p),process.exit(0));const c=De(),I=to({lockPath:Vo,instanceId:c});I.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(I.ownerPid?` (PID ${I.ownerPid})`:"")+", skipping duplicate start"),clearInterval(p),process.exit(0));try{const e=Lo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),I.release(),process.exit(0)}catch{}}catch{}const q=$e(process.env,me)?[]:Me(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),I.release(),process.exit(0)),re(process.pid,c,{version:F}),te(process.pid,void 0,c),process.on("exit",()=>{ee(process.pid,c),oe(c)}),process.on("exit",I.release);let $=null;try{$=eo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const E=await Qe();if(E){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{oo({fromVersion:F,targetVersion:E.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:E.jobId},{readiness:$}),console.error(`[upgrade] New version crashed ${E.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{xo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const d=m(),g=t.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??d.serverUrl??B.cn.url,h=ae(),y=await ro({serverUrl:g,authToken:d.machineToken??d.accessToken}),w=y.map(e=>e.type),N=ao(y),u=new vo,A=new yo,C=new Uo(u),v=new Io(g),ge=new Ao({actionClient:v}),ue=new To({actionClient:v});let S=null,r;const z=Do({authority:C,actionClient:v,attachmentCache:ge,attachmentUploader:ue,observations:A,getMachineCredentials:()=>({machineToken:m().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),scheduleExecutor:(e,n,o)=>Z({serverUrl:g,machineToken:m().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const a=u.getByRegistryKey(e)?.binding;a&&u.setBinding({registryKey:e,bindingId:a.bindingId,roomId:a.roomId,agentIdentityId:a.agentIdentityId,agentMembershipId:a.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let O=null,V=null,L=null;const b=await se({paths:go(),handlers:ho({daemonVersion:h,protocolVersion:fo,getStatus:()=>Ne(),getAgents:()=>y,completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!O)throw new Error("upgrade_unavailable");return O(e)},prepareExternalUpgrade:e=>{if(!V)throw new Error("external_upgrade_unavailable");return V(e)},cancelExternalUpgrade:()=>{if(!L)throw new Error("external_upgrade_unavailable");return L()}})});let k;try{k=await se({paths:uo(),handlers:{"managed-room.execute":e=>z(e),"schedule.personal":async e=>{const n=e,o=ve(n?.context);if(u.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await Z({serverUrl:g,machineToken:m().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let J=!1;const T=()=>{J||(J=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",T),process.once("SIGTERM",T),!d.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",T),process.removeListener("SIGTERM",T);const K=`${jo(g)}/relay/machine`,U=d.machineToken;if(!U)return;try{await _o({serverUrl:g,machineToken:U,machineId:d.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)}d.machineId&&so(g,d.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${h}) agents: ${w.join(",")}`),P({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:w}});let D=null,M=null,_=0,f=Ze();const W=ke(d.autoUpgrade),l=new Re({serverUrl:K,machineToken:U,cliVersion:h,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:ce,sessionSyncCapabilities:de,daemonInstallationId:Mo(),dataEpoch:le,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),P({level:"info",wsEvent:"daemon.connected"}),N.some(o=>o.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:N}}),io(y,{serverUrl:d.serverUrl??g,authToken:d.machineToken??d.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(N)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),f&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:f.from,to:f.to,...f.jobId?{jobId:f.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),x.start(),M?.start();const e=++_,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),f){const o=f;f=null,o.jobId&&no(o.jobId,o.to),Ye(),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"}),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===_&&l.getState()==="connected"&&D?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&l.getState()==="connected"){if(o instanceof No)return;const a=o instanceof $o?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},a).unref?.()}}};n()},onDisconnected:e=>{_+=1,r?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Ho(e)}`),P({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}`),P({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),r.handleReq(e)}});D=lo()?new mo(l):null,M=new wo({registry:u,observations:A,actionClient:v,getMachineCredentials:()=>({machineToken:m().machineToken}),isSessionDeleted:e=>new Co().hasSession(e),onMessages:async(e,n,o,a)=>{await S?.processMessages(e,n,o,a)},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 x=new Xe({currentVersion:h,machineId:d.machineId??"unknown-machine",serverUrl:d.serverUrl??g,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:ce,dataEpoch:le,advertisedCapabilities:de,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(l,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:a=>r.prepareForUpgrade(a),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});x.setPolicy(W),r=new ze(l,D,h,x,new So({registry:u,observations:A,bindingClient:v,getMachineCredentials:()=>{const e=m();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>_e(),isDisabled:()=>ne(),shutdownAfterResponse:()=>{setTimeout(()=>{xe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),z,null,Je(U)),O=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,a=n.confirmedMajor===!0;return await He(l,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:a,prepareForUpgrade:he=>r.prepareForUpgrade(he),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},a=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:a,...r.prepareForUpgrade(a)}},L=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),S=new Ro({registry:u,observations:A,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=m().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const a=await v.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!ko(u,e)||!bo(e,a))throw new Error("managed_room_binding_unavailable");return{roomName:a.room.name,memberCount:a.room.memberCount,members:a.room.members,agentsByIdentityId:a.agent?{[a.agentIdentityId]:a.agent}:{},agentSystemPromptsByIdentityId:a.agent?{[a.agentIdentityId]:a.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n)=>{const o=m().machineToken;if(!o)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:o,activationCauseMessageId:n})},audit:new Eo,resolveManagedAgentPolicy:e=>Po({...e,runtime:y.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Ie({serverUrl:g,machineToken:m().machineToken,req:e,client:l,queue:r.getScheduleQueue(),registry:u}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(u.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await v.status({machineToken:m().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=>S.prepareDirectConversationTurn(e)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?S.prepareDirectConversationTurn(e.sessionId):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}])),j.mkdirSync(me,{recursive:!0}),re(process.pid,c,{version:h}),te(process.pid,void 0,c),l.connect(),process.stdin.resume();let Y=!1;const Q=async()=>{if(!Y){Y=!0,console.log(R.gray(`
|
|
3
|
-
Disconnecting...`)),P({level:"info",wsEvent:"daemon.stop"}),clearInterval(p);try{await
|
|
1
|
+
import{registerScheduleCommand as fe}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ve}from"./schedules/personal-context.js";import{executeServerSchedule as Z,deliverServerSchedule as Ie}from"./schedules/server-client.js";import{Command as Se}from"commander";import R from"chalk";import ye from"node:os";import j from"node:fs";import{loadConfig as m,saveConfig as we,getConfigPath as Ce,getShennianDir as be,resolveShennianPath as H,normalizeAutoUpgradePolicy as ke}from"./config/index.js";import{CliRelayClient as Re}from"./relay/client.js";import{collectKnownLogSecrets as X,installCliLogRedaction as Pe,sanitizeCliLogValue as Ee}from"./log-redaction.js";import{registerPairCommand as Ae,runSmartStart as Te}from"./commands/pair.js";import{registerLogoutCommand as Ue}from"./commands/logout.js";import{clearDaemonPidIfOwner as ee,createDaemonInstanceId as De,clearDaemonLauncher as oe,findRunningDaemonProcessIds as Me,isRemoteAccessDisabled as ne,prepareRemoteAccessPause as _e,stopPreparedRemoteAccessService as xe,isIsolatedDaemonE2eInstance as $e,getDaemonStatus as Ne,registerDaemonCommand as Oe,writeDaemonPid as re,writeDaemonLauncher as te}from"./commands/daemon.js";import{registerRuntimeCommand as Ve}from"./commands/runtime.js";import{registerAgentCommand as Le}from"./commands/agent.js";import{registerManagerCommand as je}from"./commands/manager.js";import{handleUpgradeStart as He,registerUpgradeCommand as Be}from"./commands/upgrade.js";import{registerToolsCommand as Fe}from"./commands/tools.js";import{registerIntegrationCommand as Ge}from"./commands/integration.js";import{registerRoomCommand as qe}from"./commands/room.js";import{SessionManager as ze}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as Je}from"./session/handlers/project-fs.js";import{SERVERS as B,regionToUrl as Ke,urlToRegion as We}from"./region.js";import{clearUpgradeAttempt as Ye,getCurrentVersion as ae,handleStartupCrashCheck as Qe,readUpgradeAttempt as Ze}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Xe}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as eo}from"./upgrade/updater-bootstrap.js";import{launchUpdater as oo,markUpdaterHealthy as no}from"./upgrade/updater-launcher.js";import{detectAgents as ro}from"./agents/detect.js";import{augmentProcessPath as ie}from"./env-path.js";import{acquireDaemonInstanceLock as to}from"./commands/daemon-instance-lock.js";ie();const F=ae();import{getCachedAgentInfos as ao,resolveAgentInfos as io}from"./agents/model-registry.js";import{initCliLogReporter as so,reportLog as P,reportUpgradeAggregateMetric as co}from"./log-reporter.js";import{isNativeFusionEnabled as lo}from"./native-fusion/config.js";import{NativeSessionFusionService as mo}from"./native-fusion/service.js";import{startDaemonLogRetention as po}from"./daemon-log.js";import{startDaemonIpcServer as se}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as go,resolveManagedRoomIpcPaths as uo}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as ho}from"./daemon-ipc/control.js";import{DAEMON_IPC_VERSION as fo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as vo}from"./room/session-registry.js";import{HttpRoomBindingClient as Io}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as So}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as yo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as wo}from"./room/mirror-sync-service.js";import{NativeSourceDeletionStore as Co}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as bo,isCurrentRegistration as ko}from"./room/registration-authority.js";import{RoomActivationService as Ro,resolveManagedAgentPolicy as Po}from"./room/activation-service.js";import{RoomActivationAudit as Eo}from"./room/activation-audit.js";import{RoomAttachmentCache as Ao}from"./room/attachment-cache.js";import{RoomAttachmentUploader as To}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Uo}from"./room/managed-context.js";import{createManagedRoomExecutor as Do}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ce,SESSION_SYNC_ADVERTISED_CAPABILITIES as de}from"@shennian/wire";import{getDaemonInstallationId as Mo}from"./session/store.js";import{synchronizeMachinePersonalAuthority as _o}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as le,ensurePersonalDataEpoch as xo}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as $o,DeletionSyncTerminalError as No}from"./session/deletion-sync-state.js";const me=be(),Oo=H("daemon.pid"),Vo=H("daemon.instance.lock");function Lo(){try{const a=j.readFileSync(Oo,"utf-8").trim();if(a.startsWith("{")){const p=JSON.parse(a),c=Number(p.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 G(){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 Ho(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 Se;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(F),i.enablePositionalOptions(),Ge(i),qe(i),fe(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",ye.hostname()).action(async a=>{const s=m(),p=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??s.serverUrl??void 0;await Te(p,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=H("env.json");try{const e=JSON.parse(j.readFileSync(s,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ie()}catch{}Pe(X(process.env,m()));const p=po();ne()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(p),process.exit(0));const c=De(),S=to({lockPath:Vo,instanceId:c});S.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(S.ownerPid?` (PID ${S.ownerPid})`:"")+", skipping duplicate start"),clearInterval(p),process.exit(0));try{const e=Lo();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),S.release(),process.exit(0)}catch{}}catch{}const q=$e(process.env,me)?[]:Me(process.pid);q.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${q[0]}), skipping duplicate start`),S.release(),process.exit(0)),re(process.pid,c,{version:F}),te(process.pid,void 0,c),process.on("exit",()=>{ee(process.pid,c),oe(c)}),process.on("exit",S.release);let $=null;try{$=eo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const E=await Qe();if(E){$||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{oo({fromVersion:F,targetVersion:E.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:E.jobId},{readiness:$}),console.error(`[upgrade] New version crashed ${E.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{xo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const d=m(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??d.serverUrl??B.cn.url,h=ae(),y=await ro({serverUrl:g,authToken:d.machineToken??d.accessToken}),w=y.map(e=>e.type),N=ao(y),u=new vo,A=new yo,C=new Uo(u),v=new Io(g),ge=new Ao({actionClient:v}),ue=new To({actionClient:v});let I=null,t;const z=Do({authority:C,actionClient:v,attachmentCache:ge,attachmentUploader:ue,observations:A,getMachineCredentials:()=>({machineToken:m().machineToken}),managerExecutor:e=>t.executeManagedManagerAction(e),scheduleExecutor:(e,n,o)=>Z({serverUrl:g,machineToken:m().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const r=u.getByRegistryKey(e)?.binding;r&&u.setBinding({registryKey:e,bindingId:r.bindingId,roomId:r.roomId,agentIdentityId:r.agentIdentityId,agentMembershipId:r.agentMembershipId,status:"paused",activationMode:"paused"}),C.revokeSession(e),t?.discardPendingManagedRoomMessages(n)}});let O=null,V=null,L=null;const b=await se({paths:go(),handlers:ho({daemonVersion:h,protocolVersion:fo,getStatus:()=>Ne(),getAgents:()=>y,completeAgentAuthentication:e=>t.resumePendingAcpAuthentication(e),startUpgrade:async e=>{if(!O)throw new Error("upgrade_unavailable");return O(e)},prepareExternalUpgrade:e=>{if(!V)throw new Error("external_upgrade_unavailable");return V(e)},cancelExternalUpgrade:()=>{if(!L)throw new Error("external_upgrade_unavailable");return L()}})});let k;try{k=await se({paths:uo(),handlers:{"managed-room.execute":e=>z(e),"schedule.personal":async e=>{const n=e,o=ve(n?.context);if(u.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await Z({serverUrl:g,machineToken:m().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let J=!1;const T=()=>{J||(J=!0,Promise.allSettled([b.close(),k.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",T),process.once("SIGTERM",T),!d.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",T),process.removeListener("SIGTERM",T);const K=`${jo(g)}/relay/machine`,U=d.machineToken;if(!U)return;try{await _o({serverUrl:g,machineToken:U,machineId:d.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)}d.machineId&&so(g,d.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${K}... (v${h}) agents: ${w.join(",")}`),P({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:w}});let D=null,M=null,_=0,f=Ze();const W=ke(d.autoUpgrade),l=new Re({serverUrl:K,machineToken:U,cliVersion:h,agentList:w,autoUpgradePolicy:W,sessionSyncProtocolVersion:ce,sessionSyncCapabilities:de,daemonInstallationId:Mo(),dataEpoch:le,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),P({level:"info",wsEvent:"daemon.connected"}),N.some(o=>o.models.length>0)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:N}}),io(y,{serverUrl:d.serverUrl??g,authToken:d.machineToken??d.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(N)&&l.sendEvent({type:"event",event:"machine.agents",payload:{agentList:w,agents:o}})}).catch(()=>{}),f&&l.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:f.from,to:f.to,...f.jobId?{jobId:f.jobId}:{},activeRunCount:t.getPendingRecoveryCount()}}),x.start(),M?.start();const e=++_,n=async()=>{try{if(await t.synchronizeSessionDeletions(),await t.recoverInterruptedRuns(),f){const o=f;f=null,o.jobId&&no(o.jobId,o.to),Ye(),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"}),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===_&&l.getState()==="connected"&&D?.handleConnected()}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===_&&l.getState()==="connected"){if(o instanceof No)return;const r=o instanceof $o?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},r).unref?.()}}};n()},onDisconnected:e=>{_+=1,t?.revokeProjectFsHandles(),M?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${Ho(e)}`),P({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||t?.revokeProjectFsHandles()},onReq:e=>{console.log(`[${new Date().toISOString()}] [req] ${e.method}`),P({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),t.handleReq(e)}});D=lo()?new mo(l):null,M=new wo({registry:u,observations:A,actionClient:v,getMachineCredentials:()=>({machineToken:m().machineToken}),isSessionDeleted:e=>new Co().hasSession(e),onMessages:async(e,n,o,r)=>{await I?.processMessages(e,n,o,r)},onBindingPaused:e=>{C.revokeSession(e.registryKey),e.lifecycleState==="closed"?t?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")}):t?.discardPendingManagedRoomMessages(e.nianSessionId)}});const x=new Xe({currentVersion:h,machineId:d.machineId??"unknown-machine",serverUrl:d.serverUrl??g,getIdleState:()=>t.getUpgradeIdleState(),sessionSyncProtocolVersion:ce,dataEpoch:le,advertisedCapabilities:de,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(l,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:r=>t.prepareForUpgrade(r),cancelUpgradePreparation:()=>t.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>t.getUpgradeIdleState().idle})}});x.setPolicy(W),t=new ze(l,D,h,x,new So({registry:u,observations:A,bindingClient:v,getMachineCredentials:()=>{const e=m();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${c}`}),{prepare:()=>_e(),isDisabled:()=>ne(),shutdownAfterResponse:()=>{setTimeout(()=>{xe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>C.revokeToken(e),z,null,Je(U)),O=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,r=n.confirmedMajor===!0;return await He(l,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:r,prepareForUpgrade:he=>t.prepareForUpgrade(he),cancelUpgradePreparation:()=>t.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},V=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},r=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:r,...t.prepareForUpgrade(r)}},L=()=>(t.cancelUpgradePreparation(),{cancelled:!0}),I=new Ro({registry:u,observations:A,enqueueConversationBatch:e=>t.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=m().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const r=await v.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!ko(u,e)||!bo(e,r))throw new Error("managed_room_binding_unavailable");return{roomName:r.room.name,memberCount:r.room.memberCount,members:r.room.members,agentsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent}:{},agentSystemPromptsByIdentityId:r.agent?{[r.agentIdentityId]:r.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o)=>{const r=m().machineToken;if(!r)throw new Error("machine_not_authorized");return C.issue({registryKey:e,machineCredential:r,activationCauseMessageId:n,allowedActions:o})},audit:new Eo,resolveManagedAgentPolicy:e=>Po({...e,runtime:y.find(n=>n.type===e.registration.runtimeAdapter)})}),t.scheduleHandler=e=>Ie({serverUrl:g,machineToken:m().machineToken,req:e,client:l,queue:t.getScheduleQueue(),registry:u}),t.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(u.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await v.status({machineToken:m().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},t.setManagedRoomTurnPreparer(e=>I.prepareConversationTurn(e)),t.setManagedAgentDirectTurnPreparer((e,n)=>I.prepareDirectConversationTurn(e,n)),t.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?I.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[]}):e.source==="manager_event"?I.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:I.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),j.mkdirSync(me,{recursive:!0}),re(process.pid,c,{version:h}),te(process.pid,void 0,c),l.connect(),process.stdin.resume();let Y=!1;const Q=async()=>{if(!Y){Y=!0,console.log(R.gray(`
|
|
3
|
+
Disconnecting...`)),P({level:"info",wsEvent:"daemon.stop"}),clearInterval(p);try{await t.cleanup(),x.stop(),D?.stop(),M?.stop(),l.disconnect(),await Promise.allSettled([b.close(),k.close()]),ee(process.pid,c),oe(c)}finally{process.exit(0)}}};process.on("SIGINT",()=>{Q()}),process.on("SIGTERM",()=>{Q()})}),i.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>G()),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(()=>G()),i.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>G());const pe=i.command("config").description("View or modify configuration");pe.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${Ce()}`);const a=m(),s=Ee(a,X(process.env,a));if(a.serverUrl){const p=We(a.serverUrl);console.log(`Region: ${p} (${B[p].label})`)}console.log(JSON.stringify(s,null,2))}),pe.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 p=m();if(a==="server"){s!=="cn"&&s!=="global"&&(console.error(R.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const c=s;p.serverUrl=Ke(c),we(p),console.log(R.green(`\u2713 Server set to ${B[c].label}`)),console.log(R.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(R.red(`\u2717 Unknown config key: ${a}. Supported: server`)),process.exit(1)}),Ae(i),Ue(i),Oe(i),Ve(i),Le(i),je(i),Be(i),Fe(i),i.parse();
|
|
@@ -21,6 +21,14 @@ export type ManagedWorkerRecord = {
|
|
|
21
21
|
healthNotifiedAt?: string | null;
|
|
22
22
|
lastHealthSummary?: string | null;
|
|
23
23
|
role?: 'managed_worker' | 'project_session';
|
|
24
|
+
/** Origins indexed by Provider run id; legacy records omit this and stay private. */
|
|
25
|
+
runRoomOrigins?: Record<string, ManagedWorkerRoomOrigin>;
|
|
26
|
+
};
|
|
27
|
+
export type ManagedWorkerRoomOrigin = {
|
|
28
|
+
registryKey: string;
|
|
29
|
+
registrationEpoch: number;
|
|
30
|
+
bindingId: string;
|
|
31
|
+
roomId: string;
|
|
24
32
|
};
|
|
25
33
|
export type ManagerRecord = {
|
|
26
34
|
sessionId: string;
|
|
@@ -54,6 +62,7 @@ export type ManagerInboxEvent = {
|
|
|
54
62
|
kind: 'worker.final' | 'worker.error' | 'worker.aborted' | 'worker.health';
|
|
55
63
|
priority: 'normal' | 'high';
|
|
56
64
|
runId?: string | null;
|
|
65
|
+
roomOrigin?: ManagedWorkerRoomOrigin | null;
|
|
57
66
|
summary: string;
|
|
58
67
|
createdAt: string;
|
|
59
68
|
updatedAt: string;
|
|
@@ -106,6 +115,7 @@ export declare class ManagerRegistry {
|
|
|
106
115
|
summary?: string | null;
|
|
107
116
|
}): ManagedWorkerRecord;
|
|
108
117
|
updateWorker(sessionId: string, patch: Partial<ManagedWorkerRecord>): ManagedWorkerRecord | undefined;
|
|
118
|
+
clearWorkerRunOrigins(): void;
|
|
109
119
|
listWorkers(managerSessionId: string, opts?: {
|
|
110
120
|
includeManagers?: boolean;
|
|
111
121
|
runningSessionIds?: Set<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:fs";import
|
|
1
|
+
import c from"node:fs";import I from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as h,sessionPreviewFromPayload as k}from"@shennian/wire";import{resolveShennianPath as v}from"../config/index.js";import{readMessages as p}from"../session/store.js";const u=v("manager-registry.json");function l(){return new Date().toISOString()}function w(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class W{load(){try{const e=JSON.parse(c.readFileSync(u,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{}}}catch{return w()}}save(e){c.mkdirSync(I.dirname(u),{recursive:!0}),c.writeFileSync(u,JSON.stringify(e,null,2))}upsertManager(e){const s=this.load(),r=s.managers[e.sessionId],t=l(),n={sessionId:e.sessionId,agentSessionId:e.agentSessionId??r?.agentSessionId??null,workDir:e.workDir,machineId:e.machineId??r?.machineId??null,modelId:e.modelId,agentType:e.agentType??r?.agentType??"manager",providerModelId:e.providerModelId??r?.providerModelId??null,defaultWorkerAgentType:e.defaultWorkerAgentType??r?.defaultWorkerAgentType??null,defaultWorkerModelId:e.defaultWorkerModelId??r?.defaultWorkerModelId??null,managedRoomBound:e.managedRoomBound??r?.managedRoomBound??!1,controllerKind:e.controllerKind??r?.controllerKind??"managed_session",status:e.status??r?.status??"idle",deliveringInboxEventIds:e.deliveringInboxEventIds??r?.deliveringInboxEventIds??[],managedWorkerSessionIds:r?.managedWorkerSessionIds??[],attachedExternalChannels:r?.attachedExternalChannels??[],createdAt:r?.createdAt??t,updatedAt:t};return s.managers[e.sessionId]=n,this.save(s),n}getManager(e){return this.load().managers[e]}enqueueInboxEvent(e){const s=this.load(),r=s.managers[e.managerSessionId],t=s.workers[e.workerSessionId];if(!r||!t||t.managedBy!==r.sessionId||t.workDir!==r.workDir)return null;const n=l(),a=s.inbox[e.managerSessionId]??[],o=a.findIndex(g=>g.workerSessionId===e.workerSessionId&&(e.kind==="worker.health"?g.kind==="worker.health":g.kind===e.kind&&g.runId===e.runId)),i=o>=0?{...a[o],...e,updatedAt:n}:{...e,id:`mie_${m()}`,createdAt:n,updatedAt:n};return o>=0?a[o]=i:a.push(i),s.inbox[e.managerSessionId]=a,this.save(s),i}listInboxEvents(e){return[...this.load().inbox[e]??[]].sort((s,r)=>s.createdAt.localeCompare(r.createdAt))}removeInboxEvents(e,s){if(!s.length)return;const r=this.load(),t=new Set(s);r.inbox[e]=(r.inbox[e]??[]).filter(n=>!t.has(n.id)),this.save(r)}addWorker(e){const s=this.load(),r=s.managers[e.managerSessionId];if(!r)throw new Error("Manager runtime is not registered");const t=e.sessionId??`sess_worker_${m()}`,n=l(),a={sessionId:t,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:n,updatedAt:n,lastActivityAt:n,title:e.title??null,summary:e.summary??null,agentSessionId:null,runId:null};return s.workers[t]=a,r.managedWorkerSessionIds.includes(t)||r.managedWorkerSessionIds.push(t),r.updatedAt=n,this.save(s),a}updateWorker(e,s){const r=this.load(),t=r.workers[e];if(!t)return;const n={...t,...s,updatedAt:l()};return r.workers[e]=n,this.save(r),n}clearWorkerRunOrigins(){const e=this.load();let s=!1;for(const r of Object.values(e.workers))r.runRoomOrigins&&(delete r.runRoomOrigins,s=!0);for(const r of Object.values(e.inbox))for(const t of r)t.roomOrigin&&(delete t.roomOrigin,s=!0);s&&this.save(e)}listWorkers(e,s={}){const r=this.load(),t=r.managers[e];return t?Object.values(r.workers).filter(n=>n.managedBy===t.sessionId).filter(n=>n.workDir===t.workDir).filter(n=>s.includeManagers||n.agentType!=="manager").map(n=>this.decorateManagedWorker(n,s.runningSessionIds)).sort((n,a)=>a.lastActivityAt.localeCompare(n.lastActivityAt)):[]}getWorkerForManager(e,s){const r=this.load(),t=r.managers[e],n=r.workers[s];if(t&&n)return n.managedBy!==t.sessionId||n.workDir!==t.workDir||n.agentType==="manager"?void 0:this.decorateManagedWorker(n)}retireManager(e){const s=this.load(),r=Object.values(s.workers).filter(t=>t.managedBy===e);delete s.managers[e],delete s.inbox[e];for(const[t,n]of Object.entries(s.replyTargets))n.managerSessionId===e&&delete s.replyTargets[t];return this.save(s),r}completeRetiredWorker(e,s){const r=this.load(),t=r.workers[s];!t||t.managedBy!==e||(delete r.workers[s],this.save(r))}decorateManagedWorker(e,s){const r=s?.has(e.sessionId)?"running":e.status,t=this.readTranscriptSummary(e.sessionId,e.summary??null),n=t.lastMessagePreview??e.lastMessagePreview??e.summary??null,a=e.userGoal??t.userGoal??null;return{...e,managedBy:e.managedBy??null,status:r,title:e.title??a??n??e.summary??e.sessionId,userGoal:a,lastMessagePreview:n,summary:e.summary??n,canResume:e.canResume??e.status!=="aborted",source:e.source??"manager_registry",role:"managed_worker"}}readTranscriptSummary(e,s){const r=p(e,{limit:200});if(!r.length)return{userGoal:null,lastMessagePreview:s};const t=[...r].sort((o,i)=>o.ts-i.ts),n=t.find(o=>o.role==="user"),a=[...t].reverse().find(o=>y(o));return{userGoal:n?f(y(n),160):null,lastMessagePreview:f(y(a),180)??s}}createReplyTarget(e){const s=this.load(),r={replyTarget:`rt_${m()}`,managerSessionId:e.managerSessionId,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId??null,createdAt:l()};return s.replyTargets[r.replyTarget]=r,this.save(s),r}getReplyTarget(e){return this.load().replyTargets[e]}getLatestReplyTargetForManager(e){return Object.values(this.load().replyTargets).filter(s=>s.managerSessionId===e).sort((s,r)=>r.createdAt.localeCompare(s.createdAt))[0]}}function y(d){return!d||h(d.payload)?null:k(d.payload,180)}function f(d,e){return d?d.length>e?`${d.slice(0,e)}...`:d:null}export{W as ManagerRegistry};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentEvent, AgentAdapter } from '../agents/adapter.js';
|
|
2
2
|
import { type AgentType, type ExternalChannelSessionStatus, type ReqFrame } from '@shennian/wire';
|
|
3
|
-
import { ManagerRegistry } from './registry.js';
|
|
3
|
+
import { ManagerRegistry, type ManagedWorkerRoomOrigin } from './registry.js';
|
|
4
4
|
import type { SessionManagerRuntime } from '../session/types.js';
|
|
5
5
|
import { RetiredChannelRuntime as ChannelRuntime, type ChannelRuntimeOptions, type WeChatAutomationLane } from './retired-external-channel-compat.js';
|
|
6
6
|
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
@@ -30,6 +30,8 @@ export declare class ManagerRuntimeService {
|
|
|
30
30
|
private healthTimer;
|
|
31
31
|
private startPromise;
|
|
32
32
|
private workerTextAcc;
|
|
33
|
+
private pendingWorkerOrigins;
|
|
34
|
+
prepareWorkerTurnOrigin(sessionId: string, origin?: ManagedWorkerRoomOrigin): void;
|
|
33
35
|
constructor(opts: ManagerRuntimeServiceOptions);
|
|
34
36
|
start(): Promise<void>;
|
|
35
37
|
private doStart;
|
|
@@ -67,7 +69,7 @@ export declare class ManagerRuntimeService {
|
|
|
67
69
|
private findWorker;
|
|
68
70
|
handleAppReq(req: ReqFrame): Promise<void>;
|
|
69
71
|
private handleIpc;
|
|
70
|
-
executeScopedWorkerAction(managerSessionId: string, action: ManagedManagerAction, params: Record<string, JsonValue
|
|
72
|
+
executeScopedWorkerAction(managerSessionId: string, action: ManagedManagerAction, params: Record<string, JsonValue>, roomOrigin?: ManagedWorkerRoomOrigin): Promise<JsonValue>;
|
|
71
73
|
private dispatchChatEnqueue;
|
|
72
74
|
private appendManagerVisibleMarkdown;
|
|
73
75
|
private enqueueWorkerEvent;
|