shennian 0.3.73 → 0.3.75
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 +124 -59
- package/dist/src/agent-env.d.ts +30 -0
- package/dist/src/agent-env.js +4 -4
- package/dist/src/agents/adapter.d.ts +5 -0
- package/dist/src/agents/claude-managed-permissions.d.ts +33 -0
- package/dist/src/agents/claude-managed-permissions.js +1 -0
- package/dist/src/agents/claude.d.ts +11 -2
- package/dist/src/agents/claude.js +5 -5
- package/dist/src/agents/codex-managed-permissions.d.ts +32 -0
- package/dist/src/agents/codex-managed-permissions.js +1 -0
- package/dist/src/agents/codex-managed-room-tools.d.ts +40 -0
- package/dist/src/agents/codex-managed-room-tools.js +1 -0
- package/dist/src/agents/codex.d.ts +20 -2
- package/dist/src/agents/codex.js +11 -10
- package/dist/src/agents/command-spec.d.ts +8 -0
- package/dist/src/agents/command-spec.js +3 -2
- package/dist/src/agents/cursor-managed-permissions.d.ts +28 -0
- package/dist/src/agents/cursor-managed-permissions.js +2 -0
- package/dist/src/agents/cursor.d.ts +8 -2
- package/dist/src/agents/cursor.js +1 -1
- package/dist/src/agents/custom-managed-runtime.d.ts +37 -0
- package/dist/src/agents/custom-managed-runtime.js +2 -0
- package/dist/src/agents/custom.d.ts +12 -2
- package/dist/src/agents/custom.js +4 -4
- package/dist/src/agents/detect.d.ts +3 -0
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/opencode-managed-permissions.d.ts +25 -0
- package/dist/src/agents/opencode-managed-permissions.js +2 -0
- package/dist/src/agents/opencode.d.ts +8 -2
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/workbuddy-managed-permissions.d.ts +37 -0
- package/dist/src/agents/workbuddy-managed-permissions.js +2 -0
- package/dist/src/agents/workbuddy.d.ts +8 -0
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/commands/daemon.d.ts +29 -0
- package/dist/src/commands/daemon.js +14 -14
- package/dist/src/commands/room.js +3 -3
- package/dist/src/commands/runtime.js +5 -5
- package/dist/src/config/index.d.ts +22 -0
- package/dist/src/daemon-ipc/paths.d.ts +8 -0
- package/dist/src/daemon-ipc/paths.js +1 -1
- package/dist/src/daemon-ipc/server.d.ts +1 -0
- package/dist/src/daemon-ipc/server.js +3 -3
- package/dist/src/index.js +3 -3
- package/dist/src/log-redaction.d.ts +4 -0
- package/dist/src/log-redaction.js +1 -0
- package/dist/src/relay/client.d.ts +1 -0
- package/dist/src/relay/client.js +1 -1
- package/dist/src/room/activation-service.d.ts +23 -1
- package/dist/src/room/activation-service.js +7 -7
- package/dist/src/room/attachment-cache.d.ts +3 -0
- package/dist/src/room/attachment-cache.js +2 -2
- package/dist/src/room/attachment-upload.js +1 -1
- package/dist/src/room/managed-prompt-composer.d.ts +16 -3
- package/dist/src/room/managed-prompt-composer.js +26 -13
- package/dist/src/room/managed-room-binding.d.ts +3 -3
- package/dist/src/room/managed-room-binding.js +1 -1
- package/dist/src/room/managed-room-ipc.d.ts +32 -0
- package/dist/src/room/managed-room-ipc.js +3 -1
- package/dist/src/room/room-action-client.d.ts +5 -1
- package/dist/src/room/room-action-client.js +1 -1
- package/dist/src/security/managed-agent-credential-paths.d.ts +6 -0
- package/dist/src/security/managed-agent-credential-paths.js +1 -0
- package/dist/src/security/managed-agent-executable.d.ts +19 -0
- package/dist/src/security/managed-agent-executable.js +1 -0
- package/dist/src/security/managed-agent-policy.d.ts +55 -0
- package/dist/src/security/managed-agent-policy.js +1 -0
- package/dist/src/security/managed-agent-runtime.d.ts +44 -0
- package/dist/src/security/managed-agent-runtime.js +1 -0
- package/dist/src/session/handlers/chat.d.ts +8 -1
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/handlers/remote-access.d.ts +4 -0
- package/dist/src/session/handlers/remote-access.js +1 -0
- package/dist/src/session/handlers/room-managed.d.ts +1 -1
- package/dist/src/session/handlers/room-managed.js +1 -1
- package/dist/src/session/history-subscriptions.d.ts +6 -0
- package/dist/src/session/history-subscriptions.js +1 -1
- package/dist/src/session/manager.d.ts +7 -2
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue.d.ts +4 -0
- package/dist/src/session/queue.js +4 -4
- package/dist/src/session/types.d.ts +14 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +70 -0
- package/node_modules/@shennian/wire/dist/agent-workspace.js +111 -1
- package/node_modules/@shennian/wire/dist/machine.d.ts +2 -0
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +53 -3
- package/node_modules/@shennian/wire/dist/session-sync.js +5 -1
- package/node_modules/@shennian/wire/dist/session.d.ts +2 -0
- package/package.json +3 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ManagedRoomDynamicContentItem, ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
2
|
+
export type CodexDynamicToolSpec = {
|
|
3
|
+
type: 'function';
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
export type CodexDynamicToolCallParams = {
|
|
9
|
+
threadId?: unknown;
|
|
10
|
+
turnId?: unknown;
|
|
11
|
+
callId?: unknown;
|
|
12
|
+
namespace?: unknown;
|
|
13
|
+
tool?: unknown;
|
|
14
|
+
arguments?: unknown;
|
|
15
|
+
};
|
|
16
|
+
export type CodexDynamicToolCallResponse = {
|
|
17
|
+
success: boolean;
|
|
18
|
+
contentItems: ManagedRoomDynamicContentItem[];
|
|
19
|
+
};
|
|
20
|
+
export declare const CODEX_MANAGED_ROOM_DYNAMIC_TOOLS: readonly CodexDynamicToolSpec[];
|
|
21
|
+
/**
|
|
22
|
+
* Per-adapter, per-turn capability binding for Codex app-server dynamic tools.
|
|
23
|
+
* The short Room token never enters Codex's process or persisted rollout.
|
|
24
|
+
*/
|
|
25
|
+
export declare class CodexManagedRoomToolBinding {
|
|
26
|
+
private token;
|
|
27
|
+
private executor;
|
|
28
|
+
private threadId;
|
|
29
|
+
private turnId;
|
|
30
|
+
private generation;
|
|
31
|
+
private consumedCallIds;
|
|
32
|
+
private abortController;
|
|
33
|
+
configure(token: string | null | undefined, executor: ManagedRoomExecutor | null | undefined): void;
|
|
34
|
+
isConfigured(): boolean;
|
|
35
|
+
bindThread(threadId: string): void;
|
|
36
|
+
bindTurn(threadId: string, turnId: string): boolean;
|
|
37
|
+
deactivateTurn(): void;
|
|
38
|
+
reset(): void;
|
|
39
|
+
execute(params: CodexDynamicToolCallParams): Promise<CodexDynamicToolCallResponse>;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const f={shennian_room_status:"status",shennian_room_read:"read",shennian_room_send:"send",shennian_room_attachment_get:"attachment.get"},_={shennian_room_status:new Set,shennian_room_read:new Set(["before","after","limit"]),shennian_room_send:new Set(["text","clientMessageId"]),shennian_room_attachment_get:new Set(["attachmentId"])},b=[{type:"function",name:"shennian_room_status",description:"Read the current managed Shennian Room status and member summary. Use this tool instead of running a shennian room CLI command.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{type:"function",name:"shennian_room_read",description:"Read messages from the current managed Shennian Room. before and after are mutually exclusive decimal sequence cursors.",inputSchema:{type:"object",properties:{before:{type:"string",pattern:"^(0|[1-9][0-9]{0,19})$"},after:{type:"string",pattern:"^(0|[1-9][0-9]{0,19})$"},limit:{type:"integer",minimum:1,maximum:50}},additionalProperties:!1}},{type:"function",name:"shennian_room_send",description:"Send one text message as the current managed Agent to its bound Shennian Room. Local file upload is unavailable.",inputSchema:{type:"object",properties:{text:{type:"string",minLength:1},clientMessageId:{type:"string",minLength:1,maxLength:191}},required:["text"],additionalProperties:!1}},{type:"function",name:"shennian_room_attachment_get",description:"Download one attachment from the current managed Shennian Room by attachment id.",inputSchema:{type:"object",properties:{attachmentId:{type:"string",minLength:1,maxLength:191}},required:["attachmentId"],additionalProperties:!1}}];class C{token=null;executor=null;threadId=null;turnId=null;generation=0;consumedCallIds=new Set;abortController=null;configure(t,o){this.abortController?.abort(),this.generation+=1,this.token=t?.trim()||null,this.executor=o??null,this.abortController=this.token&&this.executor?new AbortController:null,this.turnId=null,this.consumedCallIds.clear()}isConfigured(){return!!(this.token&&this.executor)}bindThread(t){this.threadId=t,this.turnId=null,this.consumedCallIds.clear()}bindTurn(t,o){return!this.isConfigured()||!this.threadId||t!==this.threadId?!1:(this.turnId===o||(this.turnId=o,this.consumedCallIds.clear()),!0)}deactivateTurn(){this.abortController?.abort(),this.generation+=1,this.token=null,this.executor=null,this.abortController=null,this.turnId=null,this.consumedCallIds.clear()}reset(){this.deactivateTurn(),this.threadId=null}async execute(t){const o=l(t.threadId),r=l(t.turnId),n=l(t.callId),h=l(t.tool),a=f[h],d=this.token,c=this.executor,m=this.abortController,g=this.generation;if(!o||!r||!n||!a||t.namespace!==null||!d||!c||!m||o!==this.threadId||r!==this.turnId||this.consumedCallIds.has(n))return i("managed_room_tool_context_unavailable");const p=I(h,t.arguments);if(!p)return i("invalid_params");this.consumedCallIds.add(n);try{const s=await c({...p,context:d,action:a},{signal:m.signal,...a==="attachment.get"?{attachmentOutput:"codex-inline"}:{}});if(g!==this.generation||d!==this.token||c!==this.executor||o!==this.threadId||r!==this.turnId)return i("managed_room_tool_context_revoked");if(a==="attachment.get"){const u=y(s);return u?{success:!0,contentItems:u}:i("managed_room_attachment_presentation_invalid")}return{success:!0,contentItems:[{type:"inputText",text:JSON.stringify(s)}]}}catch(s){const u=x(s);return i(u)}}}function y(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e.codexContentItems;if(!Array.isArray(t)||t.length<1||t.length>2)return null;const o=[];for(const r of t){if(!r||typeof r!="object"||Array.isArray(r))return null;const n=r;if(n.type==="inputText"&&typeof n.text=="string"){o.push({type:"inputText",text:n.text});continue}if(n.type==="inputImage"&&typeof n.imageUrl=="string"&&/^data:image\/(?:jpeg|png|webp|gif);base64,[A-Za-z0-9+/]*={0,2}$/.test(n.imageUrl)){o.push({type:"inputImage",imageUrl:n.imageUrl});continue}if(n.type==="inputAudio"&&typeof n.audioUrl=="string"&&/^data:audio\/(?:mpeg|mp4|ogg|wav|webm|x-wav);base64,[A-Za-z0-9+/]*={0,2}$/.test(n.audioUrl)){o.push({type:"inputAudio",audioUrl:n.audioUrl});continue}return null}return o}function l(e){return typeof e=="string"&&e.trim()?e.trim():""}function I(e,t){if(t==null)return{};if(typeof t!="object"||Array.isArray(t))return null;const o=t,r=_[e];return Object.keys(o).some(n=>!r.has(n))?null:o}function x(e){const t=e&&typeof e=="object"?e.code:void 0;return typeof t=="string"&&/^[a-z0-9_]{1,80}$/.test(t)?t:"managed_room_tool_failed"}function i(e){return{success:!1,contentItems:[{type:"inputText",text:e}]}}export{b as CODEX_MANAGED_ROOM_DYNAMIC_TOOLS,C as CodexManagedRoomToolBinding};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { AgentAdapter, type AgentSendResult } from './adapter.js';
|
|
1
|
+
import { AgentAdapter, type AgentSendContext, type AgentSendResult } from './adapter.js';
|
|
2
2
|
import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
3
|
import type { SessionRunPhase } from '@shennian/wire';
|
|
4
|
+
import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
5
|
+
import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
4
6
|
export { isCodexUnsupportedEffortError, isMissingCodexRolloutError, normalizeCodexModelId, normalizeCodexReasoningEffort, } from './codex-utils.js';
|
|
5
7
|
export declare function isCodexActiveWriterError(error: unknown): boolean;
|
|
6
8
|
export declare function isCodexPaginatedHistoryUnsupportedError(error: unknown): boolean;
|
|
@@ -37,6 +39,13 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
37
39
|
private deltaItemIds;
|
|
38
40
|
private lastTransientStatus;
|
|
39
41
|
private agentSystemPrompt;
|
|
42
|
+
private managedAgentPolicy;
|
|
43
|
+
private managedPermissionConfig;
|
|
44
|
+
private configurationRestartRequired;
|
|
45
|
+
private readonly managedRoomTools;
|
|
46
|
+
private configuredManagedRoomContextToken;
|
|
47
|
+
private configuredManagedRoomExecutor;
|
|
48
|
+
private managedRoomToolThreadId;
|
|
40
49
|
constructor(options?: {
|
|
41
50
|
modelInstructionsFile?: string;
|
|
42
51
|
hidden?: boolean;
|
|
@@ -48,9 +57,14 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
48
57
|
sessionId?: string;
|
|
49
58
|
externalChannel?: ExternalChannelSessionStatus | null;
|
|
50
59
|
env?: NodeJS.ProcessEnv;
|
|
60
|
+
managedAgentPolicy?: CompiledManagedAgentPolicy | null;
|
|
61
|
+
managedRoomContextToken?: string | null;
|
|
62
|
+
managedRoomExecutor?: ManagedRoomExecutor | null;
|
|
51
63
|
}): void;
|
|
64
|
+
private configurationFingerprint;
|
|
65
|
+
private buildProcessEnv;
|
|
52
66
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
53
|
-
send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null): Promise<void | AgentSendResult>;
|
|
67
|
+
send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void | AgentSendResult>;
|
|
54
68
|
private enqueueForExternalOwner;
|
|
55
69
|
resume(agentSessionId: string): Promise<void>;
|
|
56
70
|
setTitle(agentSessionId: string, title: string, workDir?: string): Promise<void>;
|
|
@@ -65,12 +79,15 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
65
79
|
private spawnAppServer;
|
|
66
80
|
private ensureAppServer;
|
|
67
81
|
private initializeAppServer;
|
|
82
|
+
private buildManagedPermissionConfig;
|
|
68
83
|
private resolveCodexProjectId;
|
|
69
84
|
private startTurnWithRecovery;
|
|
70
85
|
private startTurn;
|
|
71
86
|
private interruptActiveTurn;
|
|
72
87
|
private sendRpc;
|
|
73
88
|
private handleAppServerMessage;
|
|
89
|
+
private handleManagedRoomToolCall;
|
|
90
|
+
private writeAppServerResponse;
|
|
74
91
|
private mapThreadStatusToRunPhase;
|
|
75
92
|
private handleAppServerNonJsonStdout;
|
|
76
93
|
private failAppServerStartup;
|
|
@@ -87,6 +104,7 @@ export declare class CodexAdapter extends AgentAdapter {
|
|
|
87
104
|
private emitFinalIfOpen;
|
|
88
105
|
private emitErrorIfOpen;
|
|
89
106
|
private resetRunState;
|
|
107
|
+
private clearConfiguredManagedRoomContext;
|
|
90
108
|
private emitTransientStatus;
|
|
91
109
|
private resetTextState;
|
|
92
110
|
private emitText;
|
package/dist/src/agents/codex.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import{spawnSync as
|
|
2
|
-
`)}function
|
|
3
|
-
`)}function
|
|
4
|
-
${u}`.trim();if(c!==0){
|
|
5
|
-
`,u=>{u&&(clearTimeout(
|
|
6
|
-
(
|
|
7
|
-
(exit ${t.
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import{spawnSync as k}from"node:child_process";import{createInterface as y}from"node:readline";import{createHash as I,randomUUID as P}from"node:crypto";import m from"node:path";import{AgentAdapter as A,registerAgent as M}from"./adapter.js";import{resolveBuiltinCommand as v,spawnAgentCommand as T}from"./command-spec.js";import{buildAgentProcessEnv as b,buildManagedAgentProcessEnv as O}from"../agent-env.js";import{ensurePlatformInstructionsFile as q}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as D}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as N}from"../security/managed-agent-runtime.js";import{CODEX_MANAGED_ROOM_DYNAMIC_TOOLS as $,CodexManagedRoomToolBinding as L}from"./codex-managed-room-tools.js";import{assertCodexManagedPermissionProfile as C,CODEX_MANAGED_RUNTIME_SECURITY as F,compileCodexManagedPermissions as U,extractCodexMcpServerNames as W}from"./codex-managed-permissions.js";import{CODEX_APP_SERVER_CLIENT_INFO as z,extractAppServerErrorMessage as G,formatCodexErrorMessage as B,formatTransientCodexStatus as H,isCodexCollabAgentItem as J,isCodexCollabAgentToolName as f,isCodexLegacyCollabAgentItem as V,isCodexUnsupportedEffortError as X,isMissingCodexRolloutError as K,isTransientCodexErrorMessage as Q,looksLikeCodexInteractiveAuthPrompt as Y,looksLikeFatalCodexStderr as j,normalizeCodexModelId as w,normalizeCodexReasoningEffort as Z,normalizeCodexStderr as S,normalizeTerminalText as ee,safeStringify as E,stripGitDirectiveArtifacts as te}from"./codex-utils.js";import{isCodexUnsupportedEffortError as Pe,isMissingCodexRolloutError as Ae,normalizeCodexModelId as Me,normalizeCodexReasoningEffort as be}from"./codex-utils.js";N("codex",F);function se(o){const e=o instanceof Error?o.message:String(o);return/already has an active writer|thread-store conflict/i.test(e)}function re(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 ne(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 ie(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 oe(o,e){const t=m.resolve(e);let s;for(const r of o)if(r.id)for(const i of r.roots??[]){if(!i.path)continue;const n=m.resolve(i.path),a=m.relative(n,t);!(a===""||!a.startsWith("..")&&!m.isAbsolute(a))||n.length<=(s?.rootLength??-1)||(s={id:r.id,rootLength:n.length})}return s?.id}function ae(o,e){const t=e?.flatMap(r=>de(r)).filter(r=>!/^https?:\/\//i.test(r.path)).filter(r=>r.path);return{type:"text",text:ce(o,e),text_elements:[],...t?.length?{local_images:t}:{}}}function de(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=le(o.path);return e?[{path:e}]:[]}return[]}function le(o){if(!o||/^https?:\/\//i.test(o))return null;const e=m.extname(o),s=`${e?o.slice(0,-e.length):o}.preview.png`;return k("ffmpeg",["-y","-ss","0","-i",o,"-frames:v","1","-vf","scale=320:-1",s],{stdio:"ignore",timeout:15e3}).status===0?s:null}function ce(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 r=t.map((i,n)=>{const a=i.name||`attachment-${n+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${R(a)}${d}](<${i.path}>)`});return[o,"",...r].join(`
|
|
2
|
+
`)}function ue(o,e){const t=e?.filter(r=>r.path&&!/^https?:\/\//i.test(r.path)&&!o.includes(r.path));if(!t?.length)return o;const s=t.map((r,i)=>{const n=R(r.name||`attachment-${i+1}`);return r.kind==="image"?``:`[${n}](<${r.path}>)`});return[o,"",...s].join(`
|
|
3
|
+
`)}function pe(o,e,t){return["queue","--thread",o,"--message",ue(e,t)]}function R(o){return o.replace(/[[\]\\]/g,"\\$&")}class me extends A{options;type="codex";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 L;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;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 s=this.configurationFingerprint();t!==s&&this.process&&(this.configurationRestartRequired=!0)}configurationFingerprint(){const e=Object.keys(this.extraEnv).sort().map(t=>[t,this.extraEnv[t]??null]);return I("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return b({...e,...this.extraEnv});const t=Object.fromEntries(Object.entries(this.extraEnv).filter(([s])=>s!=="SHENNIAN_MANAGED_ROOM_CONTEXT"&&s!=="SHENNIAN_MANAGED_ROOM_IPC_ROOT"));return O({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,s){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,s&&(this.agentSessionId=s)}async send(e,t,s,r,i,n){if(n?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(n?.source==="room_activation"&&(!this.configuredManagedRoomContextToken||!this.configuredManagedRoomExecutor))throw new Error("codex_managed_room_tool_context_unavailable");this.activeTurnId&&(await this.interruptActiveTurn().catch(()=>{}),await this.killProcess(!0)),this.configurationRestartRequired&&this.process&&await this.killProcess(!0),this.configurationRestartRequired=!1;const a=w(t),d=Z(s),l=i?.trim()??"";l!==this.agentSystemPrompt&&(this.agentSystemPrompt=l,this.process&&await this.killProcess(!0)),n?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=P(),this.seq=0,this.resetRunState();try{await this.ensureAppServer(a)}catch(p){if(!this.agentSessionId||!se(p))throw p;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:p});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,r)}}const u=this.agentSessionId;if(!u)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:u});const h=await this.startTurnWithRecovery(u,e,a,d,r);if(this.activeTurnId=h.turn?.id??null,this.managedPermissionConfig&&this.activeTurnId&&!this.managedRoomTools.bindTurn(u,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}async enqueueForExternalOwner(e,t,s){const r=v("codex");if(!r)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=pe(e,t,s),n=T(r,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let l="",u="",h=!1;const p=(c,g)=>{h||(h=!0,clearTimeout(x),c?d(c):a(g))},x=setTimeout(()=>{n.kill("SIGTERM"),p(new Error("Codex Desktop message queue timed out"))},6e4);x.unref?.(),n.stdout?.on("data",c=>{l+=c.toString()}),n.stderr?.on("data",c=>{u+=c.toString()}),n.once("error",c=>p(c)),n.once("close",c=>{const g=`${l}
|
|
4
|
+
${u}`.trim();if(c!==0){p(new Error(g||`codex queue exited with code ${c??"unknown"}`));return}const _=g.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];p(void 0,_)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,s){const r=e.trim(),i=t.replace(/\s+/g," ").trim();!r||!i||(this.agentSessionId=r,s?.trim()&&(this.workDir=s),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:r,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 s=null;try{s=(await this.sendRpc("thread/read",{threadId:e,includeTurns:!1},5e3)).thread?.status??null}catch{}const r=s?.type==="active"||!!t;return{active:r,runId:t,runPhase:r?this.mapThreadStatusToRunPhase(s):null,canStop:!!(r&&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 s=T(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=s,y({input:s.stdout}).on("line",n=>{if(n.trim())try{const a=JSON.parse(n);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:n})}});let i="";s.stderr?.on("data",n=>{i+=n.toString()}),s.on("close",n=>{this.process===s&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(n,i))}),s.on("error",n=>{this.process===s&&(this.process=null,n.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",n))})}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://"],s=this.options.modelInstructionsFile??q(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);s&&t.push("-c",`model_instructions_file=${JSON.stringify(s)}`);const r=T(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=r,this.stderrBuf="",this.appServerStartupError=null,y({input:r.stdout}).on("line",n=>{if(!n.trim())return;let a;try{a=JSON.parse(n)}catch{this.handleAppServerNonJsonStdout(n);return}this.handleAppServerMessage(a)}),r.stderr?.on("data",n=>{this.stderrBuf+=n.toString()}),r.on("close",n=>{if(this.process!==r)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(S(this.stderrBuf)||`codex app-server exited with code ${n}`)),this.terminalState==="open"&&a&&this.handleProcessClose(n,this.stderrBuf)}),r.on("error",n=>{this.process===r&&(this.process=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(n),n.code==="ENOENT"?this.emit("error",new Error('Command "codex" not found. Is OpenAI Codex CLI installed?')):this.emit("error",n))})}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=w(e);if(await this.sendRpc("initialize",{clientInfo:z,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy?await this.buildManagedPermissionConfig():null,this.managedPermissionConfig&&!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.managedPermissionConfig&&this.agentSessionId!==this.managedRoomToolThreadId){if(!this.managedRoomTools.isConfigured())throw new Error("codex_managed_room_tool_context_unavailable");this.agentSessionId=null}if(this.agentSessionId){const a={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...this.options.hidden?{threadSource:"subagent"}:{},...t?{model:t}:{}};let d;try{d=await this.sendRpc("thread/resume",{...a,excludeTurns:!0})}catch(l){if(!ne(l))throw l;d=await this.sendRpc("thread/resume",a)}this.agentSessionId=d.thread?.id??this.agentSessionId,this.managedPermissionConfig&&(C(d,this.managedPermissionConfig.profileId),this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId);return}const s=this.options.hidden?void 0:await this.resolveCodexProjectId(),r={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",threadSource:this.options.hidden?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedPermissionConfig?{dynamicTools:$}:{},...s?{projectId:s}:{},...t?{model:t}:{}};let i;if(this.options.hidden)i=await this.sendRpc("thread/start",r);else try{i=await this.sendRpc("thread/start",{...r,historyMode:"paginated"})}catch(a){throw re(a)?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:a}):a}const n=i.thread?.id;if(!n)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&(C(i,this.managedPermissionConfig.profileId),this.managedRoomTools.bindThread(n),this.managedRoomToolThreadId=n),this.agentSessionId=n}async buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)throw new Error("managed_agent_policy_missing");const t=e.adapterPolicy.externalCapabilities.mcp?[]:W(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return U(D(e),e.adapterPolicy.workDir,{configuredMcpServerNames:t,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}async resolveCodexProjectId(){const e=this.workDir?.trim();if(e)try{const t=[];let s;do{const l=await this.sendRpc("project/list",{limit:100,...s?{cursor:s}:{}});t.push(...l.data??[]),s=l.nextCursor}while(s);const r=oe(t,e);if(r)return r;const i=m.resolve(e),n=`shennian-project-${I("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:n,name:m.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 ie(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,s,r,i){try{return await this.startTurn(e,t,s,r,i)}catch(n){if(!K(n))throw n;if(this.managedPermissionConfig)throw new Error("codex_managed_thread_rollout_missing",{cause:n});return await this.killProcess(),await this.ensureAppServer(s),await this.startTurn(e,t,s,r,i)}}async startTurn(e,t,s,r,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[ae(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...s?{model:s}:{},...r?{effort:r}:{}})}catch(n){throw r&&X(n)?new Error(`Codex app-server does not accept reasoning effort "${r}" for this turn. Refresh models or upgrade Codex CLI, then retry.`,{cause:n}):n}}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,s=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const r=this.process;if(!r?.stdin)return Promise.reject(new Error("codex app-server is not running"));const i=this.rpcSeq++,n=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}`))},s);this.pendingRequests.set(i,{resolve:a,reject:d,timer:l}),r.stdin.write(`${n}
|
|
5
|
+
`,u=>{u&&(clearTimeout(l),this.pendingRequests.delete(i),d(u))})})}handleAppServerMessage(e){if(e.method==="item/tool/call"&&e.id!=null){this.handleManagedRoomToolCall(e.id,e.params??{});return}if(e.id!=null){const s=typeof e.id=="number"?this.pendingRequests.get(e.id):void 0;s&&(clearTimeout(s.timer),this.pendingRequests.delete(e.id),e.error?s.reject(new Error(e.error.message||`codex app-server error ${e.error.code??""}`.trim())):s.resolve(e.result))}if(!e.method)return;const t=e.params??{};switch(e.method){case"turn/started":{const s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null,i=typeof t.threadId=="string"?t.threadId:"";r&&(this.activeTurnId=r,this.managedPermissionConfig&&!this.managedRoomTools.bindTurn(i,r)&&this.emitErrorIfOpen("codex_managed_room_turn_binding_failed"));break}case"thread/status/changed":{const s=typeof t.threadId=="string"?t.threadId:"";if(s&&this.agentSessionId&&s!==this.agentSessionId)break;const r=typeof t.status=="object"&&t.status!==null?t.status:null;r?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(r)});break}case"item/agentMessage/delta":{const s=typeof t.itemId=="string"?t.itemId:"";s&&this.deltaItemIds.add(s),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 s=typeof t.turn=="object"&&t.turn!==null?t.turn:null,r=typeof s?.id=="string"?s.id:null;(!this.activeTurnId||r===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"error":{const s=G(t);if(Q(s)){this.emitTransientStatus(s);return}this.emitErrorIfOpen(s);break}}}async handleManagedRoomToolCall(e,t){const s=await this.managedRoomTools.execute(t);this.writeAppServerResponse(e,s)}writeAppServerResponse(e,t){const s=this.process;s?.stdin&&s.stdin.write(`${JSON.stringify({id:e,result:t})}
|
|
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=ee(e);Y(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")&&!J(t))switch(t.type){case"agentMessage":{const s=typeof t.id=="string"?t.id:"";if(s&&this.deltaItemIds.has(s))return;typeof t.text=="string"&&this.emitText(t.text,"block");break}case"commandExecution":{const s=typeof t.command=="string"?t.command:"",r=typeof t.aggregatedOutput=="string"?t.aggregatedOutput:"";s&&this.emitEvent({state:"tool-call",name:"command_execution",args:{command:s}}),this.emitEvent({state:"tool-result",name:"command_execution",result:[r,t.exitCode!=null?`
|
|
7
|
+
(exit ${t.exitCode})`:""].join("").trim()});break}case"mcpToolCall":case"dynamicToolCall":{const s=typeof t.tool=="string"?t.tool:t.type;if(f(s))return;const r=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:{};this.emitEvent({state:"tool-call",name:s,args:r}),this.emitEvent({state:"tool-result",name:s,result:E(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,s=typeof t?.last=="object"&&t.last!==null?t.last:null,r=typeof t?.total=="object"&&t.total!==null?t.total:null,i=s??r;if(!i)return;const n=typeof i.inputTokens=="number"?i.inputTokens:void 0,a=typeof i.outputTokens=="number"?i.outputTokens:void 0;(n!=null||a!=null)&&(this.pendingUsage={inputTokens:n??0,outputTokens:a??0})}handleCodexItemLine(e){const t=e.item;if(t?.type&&!V(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 s=[t.aggregated_output??"",t.exit_code!=null?`
|
|
8
|
+
(exit ${t.exit_code})`:""];this.emitEvent({state:"tool-result",name:"command_execution",result:s.join("").trim()||String(t.status??"")})}break}case"mcp_tool_call":{if(t.tool&&f(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 s=t.error&&typeof t.error=="object"?t.error.message:void 0;this.emitEvent({state:"tool-result",name:t.tool??"mcp_tool",result:s??E(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 s=t.items.map(r=>`- [${r.completed?"x":" "}] ${r.text}`).join(`
|
|
9
|
+
`);this.emitText(s,"block")}break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{(e.type==="item.started"||e.type==="item.completed")&&this.emitApprovalPending(t);break}case"error":{e.type==="item.completed"&&t.message&&this.emitEvent({state:"error",message:t.message});break}default:break}}handleStreamEvent(e){const t=e.type??e.event;switch(t?.startsWith("item.")&&this.handleCodexItemLine(e),t){case"turn.completed":{const r=e.usage;if(r){const i=r.input_tokens??r.prompt_tokens,n=r.output_tokens??r.completion_tokens;(i!=null||n!=null)&&(this.pendingUsage={inputTokens:i??0,outputTokens:n??0})}this.emitFinalIfOpen();return}case"turn.failed":{const r=typeof e.error=="object"&&e.error?.message?e.error.message:typeof e.error=="string"?e.error:"turn failed";this.emitErrorIfOpen(r);return}case"error":{const r=typeof e.message=="string"?e.message:"";if(r.includes("Reconnecting"))return;this.emitErrorIfOpen(r||(typeof e.error=="string"?e.error:"stream error"));return}case"thread.started":{const r=e.thread_id??e.session_id??e.id;typeof r=="string"&&r&&(this.agentSessionId=r,this.emitEvent({state:"start",agentSessionId:this.agentSessionId}));return}case"turn.started":return}if(t?.startsWith("item."))return;switch(e.type??e.event){case"message":case"text":case"content":{const r=e.content??e.text;r&&this.emitText(r);break}case"function_call":case"tool_call":{if(this.isApprovalLikeName(e.name)){this.emitApprovalPending(e);break}if(e.name&&f(e.name))return;this.emitEvent({state:"tool-call",name:e.name,args:e.arguments??e.input});break}case"function_call_output":case"tool_result":{if(e.name&&f(e.name))return;this.emitEvent({state:"tool-result",name:e.name,result:e.output??e.result});break}case"approval_request":case"approvalRequest":case"permission_request":case"permissionRequest":case"user_input_required":case"userInputRequired":{this.emitApprovalPending(e);break}case"error":break;case"done":case"completed":{e.usage&&(this.pendingUsage={inputTokens:e.usage.prompt_tokens??e.usage.input_tokens??0,outputTokens:e.usage.completion_tokens??e.usage.output_tokens??0}),this.emitFinalIfOpen();break}}}emitEvent(e){if(this.terminalState!=="open"&&e.state!=="final"&&e.state!=="error"&&e.state!=="aborted")return;const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}isApprovalLikeName(e){return typeof e!="string"?!1:/approval|permission|confirm|user[_-]?input|request[_-]?user/i.test(e)}emitApprovalPending(e){const t=typeof e.command=="string"?e.command:"",s=typeof e.title=="string"&&e.title.trim()?e.title:"\u9700\u8981\u7528\u6237\u786E\u8BA4",r=typeof e.prompt=="string"?e.prompt:typeof e.reason=="string"?e.reason:typeof e.message=="string"?e.message:t?`Codex \u8BF7\u6C42\u6267\u884C\u547D\u4EE4\uFF1A${t}`:"Codex \u6B63\u5728\u7B49\u5F85\u7528\u6237\u786E\u8BA4\u540E\u7EE7\u7EED\u3002";this.emitEvent({state:"approval-pending",approval:{title:s,content:r,source:"codex",actionHint:"\u5F53\u524D\u795E\u5FF5\u6682\u4E0D\u652F\u6301\u8FDC\u7A0B\u786E\u8BA4\uFF0C\u8BF7\u56DE\u5230\u8FD0\u884C Codex \u7684\u7535\u8111/\u7EC8\u7AEF\u5B8C\u6210\u786E\u8BA4\u3002"}})}async killProcess(e=!1){const t=this.process;if(this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),e||this.clearConfiguredManagedRoomContext(),!t){this.rejectAllPending(new Error("codex app-server stopped"));return}this.process=null,this.clearForceCloseTimer(),t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}),this.rejectAllPending(new Error("codex app-server stopped"))}rejectAllPending(e){for(const[t,s]of this.pendingRequests.entries())clearTimeout(s.timer),s.reject(e),this.pendingRequests.delete(t)}handleProcessClose(e,t){const s=S(t);if(e!==0&&e!==null){const r=s||`codex exited with code ${e}`;this.emitErrorIfOpen(r)}else j(s)?this.emitErrorIfOpen(s):this.emitFinalIfOpen();this.pendingUsage=void 0}emitFinalIfOpen(){this.terminalState==="open"&&(this.terminalState="final",this.managedRoomTools.deactivateTurn(),this.clearConfiguredManagedRoomContext(),this.emitEvent({state:"final",usage:this.pendingUsage,agentSessionId:this.agentSessionId??void 0}),this.killProcess().catch(()=>{}))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.managedRoomTools.deactivateTurn(),this.clearConfiguredManagedRoomContext(),this.emitEvent({state:"error",message:B(e)}),this.scheduleProcessClose())}resetRunState(){this.pendingUsage=void 0,this.terminalState="open",this.deltaItemIds.clear(),this.lastTransientStatus="",this.clearForceCloseTimer(),this.resetTextState()}clearConfiguredManagedRoomContext(){this.configuredManagedRoomContextToken=null,this.configuredManagedRoomExecutor=null}emitTransientStatus(e){const t=H(e);!t||t===this.lastTransientStatus||this.terminalState!=="open"||(this.lastTransientStatus=t,this.emitEvent({state:"delta",text:`${t}
|
|
10
|
+
`,thinking:!0}))}resetTextState(){this.hasEmittedText=!1,this.textTail=""}emitText(e,t="delta"){if(!e)return;let s=te(e);s&&(t==="block"&&this.hasEmittedText&&!s.startsWith(`
|
|
10
11
|
`)&&(this.textTail.endsWith(`
|
|
11
12
|
`)?s=`
|
|
12
13
|
${s}`:this.textTail.endsWith(`
|
|
13
14
|
|
|
14
15
|
`)||(s=`
|
|
15
16
|
|
|
16
|
-
${s}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+s).slice(-4),this.emitEvent({state:"delta",text:s}))}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)}}
|
|
17
|
+
${s}`)),this.hasEmittedText=!0,this.textTail=(this.textTail+s).slice(-4),this.emitEvent({state:"delta",text:s}))}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)}}M("codex",()=>new me);export{me as CodexAdapter,pe as buildCodexQueueArgs,ue as buildCodexQueuedText,se as isCodexActiveWriterError,ne as isCodexExcludeTurnsUnsupportedError,re as isCodexPaginatedHistoryUnsupportedError,ie as isCodexProjectApiUnsupportedError,Pe as isCodexUnsupportedEffortError,Ae as isMissingCodexRolloutError,Me as normalizeCodexModelId,be as normalizeCodexReasoningEffort,oe as selectCodexProjectIdForWorkDir};
|
|
@@ -36,4 +36,12 @@ export declare function spawnCommandString(commandString: string, runtimeArgs: s
|
|
|
36
36
|
cwd?: string;
|
|
37
37
|
}): ChildProcess;
|
|
38
38
|
export declare function getCommandVersion(spec: ResolvedCommandSpec, flag?: string): string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Cursor's launcher can survive an in-place update while continuing to report
|
|
41
|
+
* the previous build from `--version`. Its `about` command reports the build
|
|
42
|
+
* that is actually executing, which is the identity the managed-security
|
|
43
|
+
* allowlist must bind to. Do not fall back to the stale value when `about`
|
|
44
|
+
* cannot be verified: an unknown build must resolve as unverified.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getBuiltinAgentVersion(agentType: BuiltinAgentType, spec: ResolvedCommandSpec): string | undefined;
|
|
39
47
|
export {};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import{spawn as k,spawnSync as g}from"node:child_process";import y from"node:fs";import
|
|
2
|
-
${t.stderr??""}`.trim().match(/\d+\.\d+[\w.-]*/)?.[0]}
|
|
1
|
+
import{spawn as k,spawnSync as g}from"node:child_process";import y from"node:fs";import E from"node:os";import s from"node:path";const F={claude:[{command:"claude",args:[],display:"claude"}],codex:[{command:"codex",args:[],display:"codex"}],workbuddy:[{command:"codebuddy",args:[],display:"WorkBuddy"}],gemini:[{command:"gemini",args:[],display:"gemini"}],cursor:[{command:"agent",args:[],display:"agent"},{command:"cursor",args:["agent"],display:"cursor agent"}],openclaw:[],opencode:[{command:"opencode",args:[],display:"opencode"}],pi:[{command:"shennian",args:[],display:"shennian"}],manager:[{command:"shennian",args:["manager"],display:"shennian manager"}]},p=new Map,m=new Map,f=new Map,u=new Map;function K(){p.clear(),m.clear(),f.clear(),u.clear()}function d(){return process.platform}function R(n){return n.split(/\r?\n/).map(e=>e.trim()).filter(Boolean)}function b(n){if(s.basename(n)!==n)return[n];const e=E.homedir();if(d()==="win32"){const o=s.win32,r=s.extname(n)?[n]:[n,`${n}.cmd`,`${n}.exe`,`${n}.bat`],a=process.env.APPDATA||o.join(e,"AppData","Roaming"),l=process.env.LOCALAPPDATA||o.join(e,"AppData","Local"),v=[o.join(e,".shennian","node"),o.join("C:\\","nvm4w","nodejs"),o.join(l,"npm-global"),o.join(a,"npm"),o.join(l,"pnpm"),o.join(e,"scoop","shims"),o.join("C:\\","Program Files","nodejs")].flatMap(h=>r.map(w=>o.join(h,w)));if(n==="codebuddy"){const h=[o.join(l,"Programs","WorkBuddy"),o.join(l,"WorkBuddy"),o.join("C:\\","Program Files","WorkBuddy")];for(const w of h)for(const M of["codebuddy.exe","codebuddy.cmd","codebuddy"])v.push(o.join(w,"resources","app.asar.unpacked","cli","bin",M))}return v}const i=[s.join(e,".npm-global","bin"),s.join(e,".local","bin"),s.join(e,".bun","bin"),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"].map(o=>s.join(o,n));return n==="claude"&&d()==="darwin"&&i.push(...V(e)),n==="codebuddy"&&d()==="darwin"&&i.push("/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy",s.join(e,"Applications","WorkBuddy.app","Contents","Resources","app.asar.unpacked","cli","bin","codebuddy")),i}function V(n){const e=[s.join(n,"Library","Application Support","Claude","claude-code")],t=[];for(const i of e){let o;try{o=y.readdirSync(i,{withFileTypes:!0}).filter(r=>r.isDirectory()).map(r=>r.name).sort((r,a)=>a.localeCompare(r,void 0,{numeric:!0}))}catch{continue}for(const r of o)t.push(s.join(i,r,"claude.app","Contents","MacOS","claude"))}return t}function j(n,e){const t=n?.split(s.delimiter).filter(Boolean)??[];if(d()==="win32"){if(e&&s.basename(e)!==e){const i=s.win32.dirname(e);t.includes(i)||t.unshift(i)}for(const i of b("shennian")){const o=s.win32.dirname(i);t.includes(o)||t.push(o)}return t.join(s.delimiter)}if(e&&s.basename(e)!==e){const i=s.dirname(e);t.includes(i)||t.unshift(i)}for(const i of b("shennian")){const o=s.dirname(i);t.includes(o)||t.push(o)}return t.join(s.delimiter)}function c(n){if(f.has(n))return f.get(n)??[];const e=d()==="win32",i=g(e?"where":"which",[n],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:3e3,windowsHide:!0,env:{...process.env,PATH:e?process.env.PATH:j(process.env.PATH,n)}}),r=[...i.status===0?R(i.stdout??""):[]];for(const a of b(n))y.existsSync(a)&&!r.includes(a)&&r.push(a);return f.set(n,r),r}function A(){return process.env.ComSpec||"cmd.exe"}function P(n){const e=s.extname(n).toLowerCase();return e===".exe"||e===".com"}function C(n){const e=s.extname(n).toLowerCase();return e===".cmd"||e===".bat"}function $(n){const e=s.extname(n).toLowerCase();return e===".js"||e===".mjs"||e===".cjs"}function W(){return c("node.exe")[0]??c("node")[0]??"node"}function S(n){return`"${n.replace(/"/g,'""')}"`}function x(n){return`'${n.replace(/'/g,"'\\''")}'`}function N(n){if(!n)return;const e=n.replace(/\//g,"\\"),t=e.match(/^([A-Za-z]):\\(.*)$/);if(t){const[,i,o]=t,r=o.replace(/\\/g,"/");return`/mnt/${i.toLowerCase()}/${r}`}if(e.startsWith("\\\\wsl$\\"))return`/${e.replace(/^\\\\wsl\$\\[^\\]+\\?/,"").replace(/\\/g,"/")}`;if(n.startsWith("/"))return n}function B(n){const e=n.trim();if(!e)throw new Error("Command string is empty");if(/[\\/]/.test(e)||/^[A-Za-z]:/.test(e))return C(e)?{kind:"cmd-shim",path:e}:{kind:"direct",path:e};const i=c(e),o=i.find(P);if(o)return{kind:"direct",path:o};const r=i.find(C);return r?{kind:"cmd-shim",path:r}:{kind:"direct",path:e}}function L(n){if(u.has(n))return u.get(n)??!1;const e=c("wsl.exe")[0]??c("wsl")[0];if(!e)return u.set(n,!1),!1;const i=g(e,["-e","sh","-lc",`command -v ${x(n)} >/dev/null 2>&1`],{stdio:"ignore",timeout:5e3,windowsHide:!0}).status===0;return u.set(n,i),i}function Q(n){if(p.has(n))return p.get(n)??!1;const e=c(n).length>0||d()==="win32"&&L(n);return p.set(n,e),e}function z(n){const e=c(n.command),t=e.find(P);if(t)return{...n,kind:"native",path:t};const i=e.find(C);if(i)return{...n,kind:"cmd-shim",path:i};if(L(n.command)){const o=c("wsl.exe")[0]??c("wsl")[0];if(o)return{...n,kind:"wsl-bridge",path:o}}return null}function X(n){if(m.has(n))return m.get(n)??null;const e=F[n].map(t=>{if(d()==="win32")return z(t);const i=c(t.command)[0];return i?{...t,kind:"native",path:i}:null}).find(Boolean)??null;return m.set(n,e),e}function H(n,e,t){if(n.kind==="native")return d()==="win32"&&$(n.path)?{command:W(),args:[n.path,...n.args,...e],cwd:t}:{command:n.path,args:[...n.args,...e],cwd:t};if(n.kind==="cmd-shim"){const r=[n.path,...n.args,...e].map(S).join(" ");return{command:A(),args:["/d","/s","/c",`"${r}"`],cwd:t}}const i=t?N(t):void 0;if(t&&!i)throw new Error(`Cannot map Windows path to WSL workDir: ${t}`);const o=[i?`cd ${x(i)}`:"",`exec ${[n.command,...n.args,...e].map(x).join(" ")}`].filter(Boolean).join(" && ");return{command:n.path,args:["-e","sh","-lc",o],cwd:void 0}}function I(n,e,t={}){const i=H(n,e,t.cwd);return k(i.command,i.args,{...t,cwd:i.cwd,windowsHide:t.windowsHide??!0,...d()==="win32"&&n.kind==="cmd-shim"?{windowsVerbatimArguments:!0}:{}})}function Y(n,e,t={}){return I(n,e,t)}function D(n,e,t={}){const i=H(n,e,t.cwd);return g(i.command,i.args,{...t,cwd:i.cwd,windowsHide:t.windowsHide??!0,...d()==="win32"&&n.kind==="cmd-shim"?{windowsVerbatimArguments:!0}:{}})}function T(n){const e=[];let t="",i=!1,o=!1;for(const r of n){if(r==="'"&&!o){i=!i;continue}if(r==='"'&&!i){o=!o;continue}if(r===" "&&!i&&!o){t&&(e.push(t),t="");continue}t+=r}return t&&e.push(t),e}function O(n,e,t){const i=T(n),o=i[0];if(!o)throw new Error("Command string is empty");const r=i.slice(1);if(d()!=="win32")return{command:o,args:[...r,...e],cwd:t};const a=B(o);return a.kind==="cmd-shim"?{command:A(),args:["/d","/s","/c",`"${[a.path,...r,...e].map(S).join(" ")}"`],cwd:t}:$(a.path)?{command:W(),args:[a.path,...r,...e],cwd:t}:{command:a.path,args:[...r,...e],cwd:t}}function nn(n,e,t={}){const i=O(n,e,t.cwd),o=T(n),r=d()==="win32"&&o[0]?B(o[0]):null;return k(i.command,i.args,{...t,cwd:i.cwd,windowsHide:t.windowsHide??!0,env:{...t.env,PATH:j(t.env?.PATH??process.env.PATH,o[0])},...d()==="win32"&&r?.kind==="cmd-shim"?{windowsVerbatimArguments:!0}:{}})}function q(n,e="--version"){const t=D(n,[e],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:5e3});return t.status!==0?void 0:`${t.stdout??""}
|
|
2
|
+
${t.stderr??""}`.trim().match(/\d+\.\d+[\w.-]*/)?.[0]}function en(n,e){if(n!=="cursor")return q(e);const t=D(e,["about"],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:5e3});return t.status!==0?void 0:`${t.stdout??""}
|
|
3
|
+
${t.stderr??""}`.match(/^\s*CLI Version\s+(\d+\.\d+(?:\.\d+)?[\w.-]*)\s*$/im)?.[1]}export{O as buildCommandStringLaunchSpec,H as buildLaunchSpec,Q as commandExists,en as getBuiltinAgentVersion,q as getCommandVersion,d as getProcessPlatform,T as parseCommandString,K as resetCommandSpecCache,X as resolveBuiltinCommand,Y as spawnAgentCommand,nn as spawnCommandString,I as spawnResolvedCommand,D as spawnResolvedCommandSync,N as windowsPathToWsl};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ManagedAgentEnforcement } from '@shennian/wire';
|
|
2
|
+
import type { ManagedAgentRuntimeSecurityDeclaration } from '../security/managed-agent-runtime.js';
|
|
3
|
+
type JsonObject = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Evidence baseline: Cursor Agent 2026.08.31 exposes --sandbox, --mode=ask,
|
|
6
|
+
* CURSOR_CONFIG_DIR, sandbox.json network policy, and CLI permission tokens.
|
|
7
|
+
*
|
|
8
|
+
* The profile is deliberately partial. Real 2026.08.31 probes showed that
|
|
9
|
+
* Read can cross the workspace boundary, and that an allowed Shell can read
|
|
10
|
+
* outside files, write temporary files, and request network access despite a
|
|
11
|
+
* deny policy. Project MCP discovery also lacks a strict invocation-scoped
|
|
12
|
+
* disable switch. Shell is therefore disabled in managed mode.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CURSOR_MANAGED_RUNTIME_SECURITY: ManagedAgentRuntimeSecurityDeclaration;
|
|
15
|
+
export type CursorManagedPermissionConfig = {
|
|
16
|
+
args: string[];
|
|
17
|
+
cliConfig: JsonObject;
|
|
18
|
+
sandboxConfig: JsonObject;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Compiles one Cursor invocation policy. The returned JSON is written only to
|
|
22
|
+
* an isolated CURSOR_CONFIG_DIR created for the child process; neither the
|
|
23
|
+
* user's ~/.cursor directory nor the workspace is modified.
|
|
24
|
+
*/
|
|
25
|
+
export declare function compileCursorManagedPermissions(enforcement: ManagedAgentEnforcement, resolvedWorkDir: string): CursorManagedPermissionConfig;
|
|
26
|
+
export declare function createCursorManagedConfigDirectory(config: CursorManagedPermissionConfig): Promise<string>;
|
|
27
|
+
export declare function disposeCursorManagedConfigDirectory(configDir: string): Promise<void>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import l from"node:fs";import c from"node:fs/promises";import u from"node:os";import s from"node:path";import{resolveProtectedShennianDirectories as C}from"../security/managed-agent-credential-paths.js";const f="shennian-cursor-managed-",R=1024*1024,v=["Read(.env)","Read(.env.*)","Read(**/.env)","Read(**/.env.*)","Read(*.key)","Read(**/*.key)","Read(*.pem)","Read(**/*.pem)","Read(~/.shennian/**)","Read($HOME/.shennian/**)"],p={commandNetwork:{allow:!1,deny:!0},webSearch:{allow:!0,deny:!0},browser:{allow:!1,deny:!0},computerUse:{allow:!1,deny:!0},mcp:{allow:!1,deny:!1}},U={minimumRuntimeVersion:"2026.08.31",maximumRuntimeVersionExclusive:"2026.09.01",platforms:{darwin:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:p,knownGaps:["fileAccess.workspaceBoundaryUnenforced","fileAccess.outsideWorkspaceRead","sandbox.shellDisabled","externalCapabilities.webSearchToolGranularity","externalCapabilities.projectMcpStartup"]},linux:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:p,knownGaps:["fileAccess.workspaceBoundaryUnenforced","fileAccess.outsideWorkspaceRead","sandbox.shellDisabled","externalCapabilities.webSearchToolGranularity","externalCapabilities.projectMcpStartup"]},wsl:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:p,knownGaps:["fileAccess.workspaceBoundaryUnenforced","fileAccess.outsideWorkspaceRead","sandbox.shellDisabled","externalCapabilities.webSearchToolGranularity","externalCapabilities.projectMcpStartup"]},win32:{level:"partial",fileAccess:{"workspace-read":!1,"workspace-write":!1,"host-full":!0},externalCapabilities:{commandNetwork:{allow:!0,deny:!1},webSearch:{allow:!0,deny:!0},browser:{allow:!1,deny:!0},computerUse:{allow:!1,deny:!0},mcp:{allow:!0,deny:!0}},knownGaps:["sandbox.nativeWindowsUnverified","sandbox.shellDisabled","externalCapabilities.projectMcpStartup"]}}};function G(a,e){const n=j(e),o=a.effectivePolicy,t=o.fileAccess==="host-full",r=o.fileAccess==="workspace-read",w=E(),b=w.map(k=>k.replace(/^Read\(/,"Write("));if(o.externalCapabilities.commandNetwork)throw new Error("cursor_managed_command_network_unavailable");if(o.externalCapabilities.mcp)throw new Error("cursor_managed_mcp_isolation_unavailable");if(o.externalCapabilities.browser||o.externalCapabilities.computerUse)throw new Error("cursor_managed_native_interactive_tool_control_unavailable");if(a.platform==="win32"&&!t)throw new Error("cursor_managed_native_windows_sandbox_unavailable");t||S(n,r);const i=["Read(**)"];r||i.push("Write(**)"),o.externalCapabilities.webSearch&&i.push("WebFetch(*)"),o.externalCapabilities.mcp&&i.push("Mcp(*:*)");const h=[...w,...b,"Shell(*)",...r?["Write(**)"]:[],...o.externalCapabilities.webSearch?[]:["WebFetch(*)"],...o.externalCapabilities.mcp?[]:["Mcp(*:*)"]],d=o.externalCapabilities.commandNetwork,y={version:1,editor:{vimMode:!1},approvalMode:t?"unrestricted":"allowlist",sandbox:{mode:t?"disabled":"enabled",networkAccess:d?"allow_all":"user_config_only",readBoundary:t?"system":"workspace"},permissions:{allow:[...new Set(i)].sort(),deny:[...new Set(h)].sort()},notifications:!1,hints:!1,suggestNextPrompt:!1,attribution:{attributeCommitsToAgent:!1,attributePRsToAgent:!1}},x={type:t?"insecure_none":r?"workspace_readonly":"workspace_readwrite",additionalReadwritePaths:[],additionalReadonlyPaths:[],disableTmpWrite:!0,enableSharedBuildCache:!1,networkPolicy:d?{default:"allow",allow:[],deny:[]}:{default:"deny",allow:[],deny:["*"]},networkPolicyStrict:!d,readBoundary:t?"system":"workspace"};return{args:["--trust","--workspace",n,"--sandbox",t?"disabled":"enabled",...r?["--mode","ask"]:[],...o.externalCapabilities.mcp?["--approve-mcps"]:[],...t?["--force"]:[]],cliConfig:y,sandboxConfig:x}}async function T(a){const e=await c.mkdtemp(s.join(u.tmpdir(),f));try{return await c.chmod(e,448),await Promise.all([_(s.join(e,"cli-config.json"),a.cliConfig),_(s.join(e,"sandbox.json"),a.sandboxConfig)]),e}catch(n){throw await g(e).catch(()=>{}),new Error("cursor_managed_config_initialization_failed",{cause:n})}}async function g(a){const e=s.resolve(a),n=s.resolve(u.tmpdir());if(s.dirname(e)!==n||!s.basename(e).startsWith(f))throw new Error("cursor_managed_config_cleanup_refused");await c.rm(e,{recursive:!0,force:!0})}function S(a,e){const n=s.join(a,".cursor","sandbox.json");if(!l.existsSync(n))return;let o;try{const r=l.statSync(n);if(!r.isFile()||r.size>R)throw new Error("invalid_project_sandbox");o=l.readFileSync(n,"utf8")}catch(r){throw new Error("cursor_managed_project_sandbox_unverifiable",{cause:r})}let t;try{const r=JSON.parse(o);if(!r||typeof r!="object"||Array.isArray(r))throw new Error("invalid_project_sandbox");t=r}catch(r){throw new Error("cursor_managed_project_sandbox_unverifiable",{cause:r})}if(t.type==="insecure_none")throw new Error("cursor_managed_project_sandbox_broadens_access");if(e&&t.type==="workspace_readwrite")throw new Error("cursor_managed_project_sandbox_broadens_access");if(m(t.additionalReadwritePaths)||m(t.additionalReadonlyPaths))throw new Error("cursor_managed_project_sandbox_broadens_access")}function m(a){return a==null?!1:!Array.isArray(a)||a.length>0}function A(a){return a.replaceAll("\\","/")}function E(){const a=C().map(e=>A(s.normalize(e))).filter(e=>s.isAbsolute(e));return[...new Set([...v,...a.map(e=>`Read(${e}/**)`)])]}function j(a){const e=s.normalize(a.trim());if(!e||!s.isAbsolute(e))throw new Error("cursor_managed_workdir_invalid");let n;try{n=l.realpathSync(e)}catch{throw new Error("cursor_managed_workdir_unavailable")}if(s.normalize(n)!==e)throw new Error("cursor_managed_workdir_not_realpath");return e}async function _(a,e){await c.writeFile(a,`${JSON.stringify(e)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"})}export{U as CURSOR_MANAGED_RUNTIME_SECURITY,G as compileCursorManagedPermissions,T as createCursorManagedConfigDirectory,g as disposeCursorManagedConfigDirectory};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { AgentAdapter } from './adapter.js';
|
|
1
|
+
import { AgentAdapter, type AgentSendContext } from './adapter.js';
|
|
2
2
|
import type { ChatAttachmentMeta, ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
|
+
import { type CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
3
4
|
export declare class CursorAdapter extends AgentAdapter {
|
|
4
5
|
readonly type: "cursor";
|
|
5
6
|
private process;
|
|
@@ -11,16 +12,21 @@ export declare class CursorAdapter extends AgentAdapter {
|
|
|
11
12
|
private externalChannel;
|
|
12
13
|
private shennianSessionId;
|
|
13
14
|
private extraEnv;
|
|
15
|
+
private managedAgentPolicy;
|
|
16
|
+
private managedConfigDir;
|
|
14
17
|
configure(options: {
|
|
15
18
|
sessionId?: string;
|
|
16
19
|
externalChannel?: ExternalChannelSessionStatus | null;
|
|
17
20
|
env?: NodeJS.ProcessEnv;
|
|
21
|
+
managedAgentPolicy?: CompiledManagedAgentPolicy | null;
|
|
18
22
|
}): void;
|
|
19
23
|
start(_sessionId: string, workDir: string, agentSessionId?: string | null): Promise<void>;
|
|
20
|
-
send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null): Promise<void>;
|
|
24
|
+
send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
|
|
21
25
|
resume(agentSessionId: string): Promise<void>;
|
|
22
26
|
stop(): Promise<void>;
|
|
23
27
|
private spawnAndParse;
|
|
28
|
+
private prepareManagedLaunch;
|
|
29
|
+
private disposeManagedConfig;
|
|
24
30
|
private handleStreamEvent;
|
|
25
31
|
private emitEvent;
|
|
26
32
|
private killProcess;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createInterface as l}from"node:readline";import{randomUUID as
|
|
1
|
+
import{createInterface as l}from"node:readline";import{randomUUID as d}from"node:crypto";import{AgentAdapter as c,registerAgent as h}from"./adapter.js";import{resolveBuiltinCommand as u,spawnResolvedCommand as m}from"./command-spec.js";import{buildAgentProcessEnv as g,buildManagedAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as p}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as v}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as E}from"../security/managed-agent-runtime.js";import{compileCursorManagedPermissions as w,createCursorManagedConfigDirectory as k,CURSOR_MANAGED_RUNTIME_SECURITY as y,disposeCursorManagedConfigDirectory as C}from"./cursor-managed-permissions.js";E("cursor",y);class x extends c{type="cursor";process=null;chatId=null;workDir=null;seq=0;runId="";emittedText="";externalChannel=null;shennianSessionId=null;extraEnv={};managedAgentPolicy=null;managedConfigDir=null;configure(t){this.shennianSessionId=t.sessionId??null,this.externalChannel=t.externalChannel??null,this.extraEnv=t.env??{},"managedAgentPolicy"in t&&(this.managedAgentPolicy=t.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(t,s,e){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??s,this.seq=0,e&&(this.chatId=e)}async send(t,s,e,n,o,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=d(),this.resetRunState();const a=["-p",`${p(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,o)}${t}`,"--output-format","stream-json","--stream-partial-output"];s&&a.push("--model",s),this.chatId&&a.push("--continue"),await this.spawnAndParse(a)}async resume(t){await this.killProcess(),this.chatId=t,this.runId=d(),this.resetRunState(),await this.spawnAndParse(["--resume",t,"--output-format","stream-json","--stream-partial-output"])}async stop(){await this.killProcess()}async spawnAndParse(t){const s=u("cursor");if(!s){this.emit("error",new Error('Cursor Agent CLI not found. Expected "agent" or "cursor agent".'));return}const e=await this.prepareManagedLaunch();let n;try{n=m(s,[...t,...e.args],{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:e.env})}catch(i){throw await this.disposeManagedConfig(e.configDir),i}this.process=n,this.managedConfigDir=e.configDir,l({input:n.stdout}).on("line",i=>{if(i.trim())try{const a=JSON.parse(i);this.handleStreamEvent(a)}catch{this.emitEvent({state:"delta",text:i})}});let r="";n.stderr?.on("data",i=>{r+=i.toString()}),n.on("close",i=>{if(this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i!==0&&i!==null)){const a=r.trim()||`cursor agent exited with code ${i}`;this.emitEvent({state:"error",message:a})}}),n.on("error",i=>{this.process===n&&(this.process=null,this.disposeManagedConfig(e.configDir),i.code==="ENOENT"?this.emit("error",new Error('Command "cursor" not found. Is Cursor CLI installed?')):this.emit("error",i))})}async prepareManagedLaunch(){const t=this.managedAgentPolicy;if(!t)return{args:["--trust"],env:g(this.extraEnv),configDir:null};const s=w(v(t),t.adapterPolicy.workDir),e=await k(s),n=f({agentType:"cursor",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}});return n.CURSOR_CONFIG_DIR=e,{args:s.args,env:n,configDir:e}}async disposeManagedConfig(t){t&&(this.managedConfigDir===t&&(this.managedConfigDir=null),await C(t).catch(()=>{}))}handleStreamEvent(t){switch(t.session_id&&!this.chatId&&(this.chatId=t.session_id),t.chat_id&&!this.chatId&&(this.chatId=t.chat_id),t.type??t.event){case"system":break;case"assistant":{if(t.message?.content){if(!this.isLiveAssistantDelta(t))break;for(const e of t.message.content)e.type==="text"&&e.text?this.emitTextDelta(e.text):e.type==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.type==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});break}(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text});break}case"user":break;case"result":{if(t.subtype==="success"||!t.is_error){this.emitFinalResultDelta(t.result);const e=t.usage;this.emitEvent({state:"final",agentSessionId:this.chatId??void 0,usage:e?{inputTokens:e.inputTokens??e.input_tokens??0,outputTokens:e.outputTokens??e.output_tokens??0}:void 0})}else this.emitEvent({state:"error",message:t.result??t.error??"unknown error"});break}case"error":{this.emitEvent({state:"error",message:t.error??"unknown error"});break}case"text":case"delta":case"content":{const e=t.delta??t.text;e&&this.emitEvent({state:"delta",text:e});break}case"done":{this.emitEvent({state:"final",agentSessionId:this.chatId??void 0});break}default:t.done?this.emitEvent({state:"final",agentSessionId:this.chatId??void 0}):(t.delta??t.text)&&this.emitEvent({state:"delta",text:t.delta??t.text})}}emitEvent(t){const s={...t,runId:this.runId,seq:this.seq++};this.emit("agentEvent",s)}async killProcess(){const t=this.process;if(!t){await this.disposeManagedConfig(this.managedConfigDir);return}this.process=null,t.kill("SIGTERM"),await new Promise(s=>{t.on("close",s),setTimeout(()=>{t.kill("SIGKILL"),s()},3e3)}),await this.disposeManagedConfig(this.managedConfigDir)}resetRunState(){this.seq=0,this.emittedText=""}isLiveAssistantDelta(t){return t.timestamp_ms!=null&&!t.model_call_id}emitTextDelta(t){t&&(this.emittedText+=t,this.emitEvent({state:"delta",text:t}))}emitFinalResultDelta(t){if(!t)return;if(!this.emittedText){this.emitTextDelta(t);return}if(!t.startsWith(this.emittedText))return;const s=t.slice(this.emittedText.length);s&&this.emitTextDelta(s)}}h("cursor",()=>new x);export{x as CursorAdapter};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
2
|
+
import type { ManagedAgentRuntimePlatform, ManagedAgentRuntimeSecurityDeclaration } from '../security/managed-agent-runtime.js';
|
|
3
|
+
import type { CustomAgentEntry } from '../config/index.js';
|
|
4
|
+
import type { CustomAgentType } from '@shennian/wire';
|
|
5
|
+
export declare const CUSTOM_RUNTIME_CONFORMANCE_SUITE_VERSION = "1";
|
|
6
|
+
export declare const CUSTOM_RUNTIME_POLICY_ENV = "SHENNIAN_MANAGED_AGENT_POLICY_FILE";
|
|
7
|
+
export type CustomRuntimeSecurityAssessment = {
|
|
8
|
+
/** Structurally valid self-declaration; never a Shennian trust verdict. */
|
|
9
|
+
state: 'declared';
|
|
10
|
+
declaration: ManagedAgentRuntimeSecurityDeclaration;
|
|
11
|
+
executablePath: string;
|
|
12
|
+
executableSha256: string;
|
|
13
|
+
} | {
|
|
14
|
+
state: 'unverified';
|
|
15
|
+
reason: 'declaration_missing' | 'declaration_invalid' | 'runtime_version_mismatch' | 'platform_mismatch' | 'conformance_not_passed' | 'conformance_suite_unsupported' | 'executable_unavailable' | 'executable_not_bound_to_command' | 'executable_fingerprint_mismatch';
|
|
16
|
+
};
|
|
17
|
+
export type CustomManagedPolicyFile = {
|
|
18
|
+
directory: string;
|
|
19
|
+
policyPath: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Validates the owner-supplied Custom Runtime attestation against local facts.
|
|
23
|
+
* The result may be used for capability reporting, but the digest is checked
|
|
24
|
+
* again immediately before every managed process launch.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assessCustomRuntimeSecurity(entry: CustomAgentEntry, platform?: ManagedAgentRuntimePlatform): CustomRuntimeSecurityAssessment;
|
|
27
|
+
export declare function registerCustomRuntimeSecurity(agentType: CustomAgentType, entry: CustomAgentEntry): CustomRuntimeSecurityAssessment;
|
|
28
|
+
export declare function sha256File(filePath: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Produces a confirmation identity for every Custom Runtime. The digest binds
|
|
31
|
+
* the exact command string and every locally resolvable file participating in
|
|
32
|
+
* launch; it is never a digest of command text alone. The separate attestation
|
|
33
|
+
* decides whether the runtime is verified, not what identity the user accepts.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveCustomRuntimeExecutableFingerprint(entry: CustomAgentEntry): string | undefined;
|
|
36
|
+
export declare function createCustomManagedPolicyFile(compiled: CompiledManagedAgentPolicy): Promise<CustomManagedPolicyFile>;
|
|
37
|
+
export declare function disposeCustomManagedPolicyFile(directory: string): Promise<void>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import u from"node:crypto";import a from"node:fs";import s from"node:fs/promises";import f from"node:os";import o from"node:path";import{parseCommandString as l}from"./command-spec.js";import{unregisterManagedAgentRuntimeSecurity as d}from"../security/managed-agent-runtime.js";const h="1",O="SHENNIAN_MANAGED_AGENT_POLICY_FILE",m="shennian-custom-managed-",_=/^[a-f0-9]{64}$/;function y(t,r=E()){const e=t.caps.security;if(!e)return{state:"unverified",reason:"declaration_missing"};if(e.schemaVersion!==1||e.declarationVersion!==1||e.policyProtocol!=="shennian-managed-policy-file-v1"||typeof e.runtimeVersion!="string"||!e.runtimeVersion.trim()||typeof e.executablePath!="string"||!e.executablePath.trim()||typeof e.executableSha256!="string"||!e.runtimeSecurity||!e.runtimeSecurity.platforms||typeof e.runtimeSecurity.platforms!="object"||!e.conformance||typeof e.conformance.testedAt!="string"||Number.isNaN(Date.parse(e.conformance.testedAt)))return{state:"unverified",reason:"declaration_invalid"};if(!t.caps.version||t.caps.version.trim()!==e.runtimeVersion.trim())return{state:"unverified",reason:"runtime_version_mismatch"};if(e.runtimeSecurity.minimumRuntimeVersion!==e.runtimeVersion.trim())return{state:"unverified",reason:"runtime_version_mismatch"};if(e.conformance.platform!==r)return{state:"unverified",reason:"platform_mismatch"};if(e.conformance.status!=="passed")return{state:"unverified",reason:"conformance_not_passed"};if(e.conformance.suiteVersion!==h)return{state:"unverified",reason:"conformance_suite_unsupported"};if(!g(e.runtimeSecurity.platforms[r]))return{state:"unverified",reason:"declaration_invalid"};let n;try{n=b(e.executablePath)}catch{return{state:"unverified",reason:"executable_unavailable"}}if(!w(t.command,n))return{state:"unverified",reason:"executable_not_bound_to_command"};const i=e.executableSha256.trim().toLowerCase();if(!_.test(i))return{state:"unverified",reason:"declaration_invalid"};const c=x(n);return P(c,i)?{state:"declared",declaration:e.runtimeSecurity,executablePath:n,executableSha256:c}:{state:"unverified",reason:"executable_fingerprint_mismatch"}}function g(t){if(!t||t.level!=="hard"&&t.level!=="partial")return!1;const r=t.fileAccess;if(!r||typeof r["workspace-read"]!="boolean"||typeof r["workspace-write"]!="boolean"||typeof r["host-full"]!="boolean")return!1;const e=t.externalCapabilities;if(!e)return!1;for(const n of["commandNetwork","webSearch","browser","computerUse","mcp"]){const i=e[n];if(!i||typeof i.allow!="boolean"||typeof i.deny!="boolean")return!1}return!0}function I(t,r){const e=y(r);return d(t),e}function x(t){return u.createHash("sha256").update(a.readFileSync(t)).digest("hex")}function V(t){const r=l(t.command).map((n,i)=>i===0||!n.startsWith("-")?p(n):null).filter(n=>!!n);if(r.length===0)return;const e=u.createHash("sha256");e.update(t.command);for(const n of[...new Set(r)])e.update("\0"),e.update(n),e.update("\0"),e.update(a.readFileSync(n));return`sha256:${e.digest("hex")}`}async function D(t){const r=await s.mkdtemp(o.join(f.tmpdir(),m)),e=o.join(r,"managed-policy.json");try{return await s.chmod(r,448),await s.writeFile(e,`${JSON.stringify({schemaVersion:1,policyVersion:t.requestedPolicy.version,requestedPolicy:t.requestedPolicy,resolvedWorkDir:t.adapterPolicy.workDir,enforcement:t.enforcement,adapterPolicy:t.adapterPolicy,environmentPolicy:t.environmentPolicy})}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&await s.chmod(e,384),{directory:r,policyPath:e}}catch(n){throw await S(r).catch(()=>{}),new Error("custom_managed_policy_initialization_failed",{cause:n})}}async function S(t){const r=o.resolve(t),e=o.resolve(f.tmpdir());if(o.dirname(r)!==e||!o.basename(r).startsWith(m))throw new Error("custom_managed_policy_cleanup_refused");await s.rm(r,{recursive:!0,force:!0})}function b(t){if(!o.isAbsolute(t))throw new Error("custom_managed_executable_not_absolute");const r=a.realpathSync(o.normalize(t));if(!a.statSync(r).isFile())throw new Error("custom_managed_executable_not_file");return o.normalize(r)}function w(t,r){const e=l(t);if(e.length===0)return!1;for(const[n,i]of e.entries()){if(n>0&&i.startsWith("-"))continue;if(p(i)===r)return!0}return!1}function p(t){const r=o.isAbsolute(t)?[t]:t.includes(o.sep)||process.platform==="win32"&&t.includes("/")?[o.resolve(t)]:v(t);for(const e of r)try{const n=a.realpathSync(e);if(a.statSync(n).isFile())return o.normalize(n)}catch{}return null}function v(t){const r=(process.env.PATH??"").split(o.delimiter).filter(Boolean),e=process.platform==="win32"?(process.env.PATHEXT??".COM;.EXE;.BAT;.CMD").split(";"):[""];return r.flatMap(n=>e.map(i=>o.join(n,`${t}${i}`)))}function P(t,r){const e=Buffer.from(t,"hex"),n=Buffer.from(r,"hex");return e.length===n.length&&u.timingSafeEqual(e,n)}function E(){return process.platform==="linux"&&(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)?"wsl":process.platform==="darwin"||process.platform==="linux"||process.platform==="win32"?process.platform:"unsupported"}export{h as CUSTOM_RUNTIME_CONFORMANCE_SUITE_VERSION,O as CUSTOM_RUNTIME_POLICY_ENV,y as assessCustomRuntimeSecurity,D as createCustomManagedPolicyFile,S as disposeCustomManagedPolicyFile,I as registerCustomRuntimeSecurity,V as resolveCustomRuntimeExecutableFingerprint,x as sha256File};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { AgentAdapter } from './adapter.js';
|
|
1
|
+
import { AgentAdapter, type AgentSendContext } from './adapter.js';
|
|
2
2
|
import type { ChatAttachmentMeta, CustomAgentType, ExternalChannelSessionStatus } from '@shennian/wire';
|
|
3
3
|
import type { CustomAgentEntry } from '../config/index.js';
|
|
4
|
+
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
4
5
|
export declare class CustomAgentAdapter extends AgentAdapter {
|
|
5
6
|
readonly type: CustomAgentType;
|
|
6
7
|
private command;
|
|
7
8
|
private mode;
|
|
8
9
|
private proactive;
|
|
10
|
+
private entry;
|
|
9
11
|
private sessionId;
|
|
10
12
|
private agentSessionId;
|
|
11
13
|
private workDir;
|
|
@@ -14,6 +16,10 @@ export declare class CustomAgentAdapter extends AgentAdapter {
|
|
|
14
16
|
private externalChannel;
|
|
15
17
|
private shennianSessionId;
|
|
16
18
|
private extraEnv;
|
|
19
|
+
private managedAgentPolicy;
|
|
20
|
+
private managedConfigDir;
|
|
21
|
+
private configurationRestartRequired;
|
|
22
|
+
private stoppingStdioProcess;
|
|
17
23
|
private stdioProcess;
|
|
18
24
|
private spawnProcess;
|
|
19
25
|
private dispatchGuard;
|
|
@@ -24,8 +30,9 @@ export declare class CustomAgentAdapter extends AgentAdapter {
|
|
|
24
30
|
sessionId?: string;
|
|
25
31
|
externalChannel?: ExternalChannelSessionStatus | null;
|
|
26
32
|
env?: NodeJS.ProcessEnv;
|
|
33
|
+
managedAgentPolicy?: CompiledManagedAgentPolicy | null;
|
|
27
34
|
}): void;
|
|
28
|
-
send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null): Promise<void>;
|
|
35
|
+
send(text: string, modelId?: string, _reasoningEffort?: string, _attachments?: ChatAttachmentMeta[], agentSystemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
|
|
29
36
|
resume(agentSessionId: string): Promise<void>;
|
|
30
37
|
stop(): Promise<void>;
|
|
31
38
|
private runSpawn;
|
|
@@ -33,6 +40,9 @@ export declare class CustomAgentAdapter extends AgentAdapter {
|
|
|
33
40
|
private startStdioProcess;
|
|
34
41
|
private sendStdio;
|
|
35
42
|
private stopStdioProcess;
|
|
43
|
+
private prepareManagedLaunch;
|
|
44
|
+
private disposeManagedConfig;
|
|
45
|
+
private configurationFingerprint;
|
|
36
46
|
private attachOutputHandlers;
|
|
37
47
|
private handleProtocolEvent;
|
|
38
48
|
private startDispatchGuard;
|