shennian 0.4.10 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/publish-build-manifest.json +185 -55
- package/dist/src/agents/acp/adapter.d.ts +78 -0
- package/dist/src/agents/acp/adapter.js +3 -0
- package/dist/src/agents/acp/auth-state.d.ts +3 -0
- package/dist/src/agents/acp/auth-state.js +1 -0
- package/dist/src/agents/acp/auth.d.ts +7 -0
- package/dist/src/agents/acp/auth.js +1 -0
- package/dist/src/agents/acp/capability-cache.d.ts +14 -0
- package/dist/src/agents/acp/capability-cache.js +2 -0
- package/dist/src/agents/acp/catalog.d.ts +11 -0
- package/dist/src/agents/acp/catalog.js +2 -0
- package/dist/src/agents/acp/client-services.d.ts +32 -0
- package/dist/src/agents/acp/client-services.js +3 -0
- package/dist/src/agents/acp/discovery.d.ts +9 -0
- package/dist/src/agents/acp/discovery.js +1 -0
- package/dist/src/agents/acp/events.d.ts +4 -0
- package/dist/src/agents/acp/events.js +3 -0
- package/dist/src/agents/acp/manifests.d.ts +63 -0
- package/dist/src/agents/acp/manifests.js +2 -0
- package/dist/src/agents/acp/metrics.d.ts +24 -0
- package/dist/src/agents/acp/metrics.js +1 -0
- package/dist/src/agents/acp/package-commands.d.ts +4 -0
- package/dist/src/agents/acp/package-commands.js +1 -0
- package/dist/src/agents/acp/process-client.d.ts +39 -0
- package/dist/src/agents/acp/process-client.js +1 -0
- package/dist/src/agents/acp/process-tree.d.ts +2 -0
- package/dist/src/agents/acp/process-tree.js +1 -0
- package/dist/src/agents/acp/types.d.ts +31 -0
- package/dist/src/agents/acp/types.js +1 -0
- package/dist/src/agents/acp/watcher.d.ts +22 -0
- package/dist/src/agents/acp/watcher.js +1 -0
- package/dist/src/agents/adapter.d.ts +96 -2
- package/dist/src/agents/adapter.js +1 -1
- package/dist/src/agents/claude.d.ts +14 -0
- package/dist/src/agents/claude.js +5 -5
- package/dist/src/agents/codex.d.ts +6 -0
- package/dist/src/agents/codex.js +1 -1
- package/dist/src/agents/command-spec.d.ts +5 -0
- package/dist/src/agents/command-spec.js +3 -3
- package/dist/src/agents/cursor.d.ts +6 -0
- package/dist/src/agents/cursor.js +1 -1
- package/dist/src/agents/custom-recovery-conformance.d.ts +2 -0
- package/dist/src/agents/custom-recovery-conformance.js +2 -0
- package/dist/src/agents/custom.d.ts +9 -0
- package/dist/src/agents/custom.js +4 -4
- package/dist/src/agents/detect.d.ts +11 -2
- package/dist/src/agents/detect.js +1 -1
- package/dist/src/agents/gemini.d.ts +6 -0
- package/dist/src/agents/gemini.js +1 -1
- package/dist/src/agents/manager.d.ts +2 -0
- package/dist/src/agents/manager.js +5 -5
- package/dist/src/agents/model-registry/service.js +1 -1
- package/dist/src/agents/opencode.d.ts +6 -0
- package/dist/src/agents/opencode.js +1 -1
- package/dist/src/agents/pi-coding-agent.d.ts +6 -0
- package/dist/src/agents/pi-coding-agent.js +4 -4
- package/dist/src/agents/workbuddy.d.ts +6 -0
- package/dist/src/agents/workbuddy.js +2 -2
- package/dist/src/commands/agent.d.ts +2 -0
- package/dist/src/commands/agent.js +2 -0
- package/dist/src/commands/daemon.d.ts +6 -2
- package/dist/src/commands/daemon.js +19 -23
- package/dist/src/commands/pair.js +10 -10
- package/dist/src/commands/upgrade.d.ts +7 -1
- package/dist/src/commands/upgrade.js +1 -1
- package/dist/src/config/index.d.ts +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/daemon-ipc/control.d.ts +16 -0
- package/dist/src/daemon-ipc/control.js +1 -0
- package/dist/src/index.js +3 -3
- package/dist/src/log-reporter.d.ts +23 -0
- package/dist/src/log-reporter.js +1 -1
- 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 +2 -0
- package/dist/src/room/activation-service.js +5 -5
- package/dist/src/session/handlers/agents.d.ts +1 -0
- package/dist/src/session/handlers/agents.js +1 -1
- package/dist/src/session/handlers/chat.d.ts +3 -0
- package/dist/src/session/handlers/chat.js +2 -2
- package/dist/src/session/manager.d.ts +23 -2
- package/dist/src/session/manager.js +3 -1
- package/dist/src/session/queue.d.ts +7 -1
- package/dist/src/session/queue.js +8 -8
- package/dist/src/session/run-recovery/barrier.d.ts +12 -0
- package/dist/src/session/run-recovery/barrier.js +1 -0
- package/dist/src/session/run-recovery/coordinator.d.ts +32 -0
- package/dist/src/session/run-recovery/coordinator.js +1 -0
- package/dist/src/session/run-recovery/journal.d.ts +30 -0
- package/dist/src/session/run-recovery/journal.js +2 -0
- package/dist/src/session/run-recovery/schema.d.ts +43 -0
- package/dist/src/session/run-recovery/schema.js +1 -0
- package/dist/src/session/types.d.ts +10 -0
- package/dist/src/upgrade/engine.d.ts +6 -5
- package/dist/src/upgrade/engine.js +2 -2
- package/dist/src/upgrade/scheduler.d.ts +14 -0
- package/dist/src/upgrade/scheduler.js +1 -1
- package/dist/src/upgrade/updater-bootstrap.d.ts +12 -0
- package/dist/src/upgrade/updater-bootstrap.js +2 -0
- package/dist/src/upgrade/updater-launcher.d.ts +28 -0
- package/dist/src/upgrade/updater-launcher.js +3 -0
- package/dist/src/upgrade/updater-protocol.d.ts +37 -0
- package/dist/src/upgrade/updater-protocol.js +1 -0
- package/dist/src/upgrade/updater-worker.d.ts +11 -0
- package/dist/src/upgrade/updater-worker.js +2 -0
- package/node_modules/@shennian/wire/dist/events.d.ts +31 -0
- package/node_modules/@shennian/wire/dist/index.d.ts +1 -0
- package/node_modules/@shennian/wire/dist/index.js +1 -0
- package/node_modules/@shennian/wire/dist/machine.d.ts +49 -1
- package/node_modules/@shennian/wire/dist/message-payload.d.ts +1 -0
- package/node_modules/@shennian/wire/dist/message-payload.js +4 -1
- package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
- package/node_modules/@shennian/wire/dist/relay-connectivity.d.ts +30 -0
- package/node_modules/@shennian/wire/dist/relay-connectivity.js +29 -0
- package/node_modules/@shennian/wire/dist/session-sync.d.ts +2 -2
- package/node_modules/@shennian/wire/dist/session-sync.js +2 -0
- package/node_modules/@shennian/wire/dist/session.d.ts +86 -1
- package/node_modules/@shennian/wire/dist/session.js +5 -1
- package/package.json +5 -3
- package/dist/native/macos-background-client/Shennian.app/Contents/CodeResources +0 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Info.plist +0 -17
- package/dist/native/macos-background-client/Shennian.app/Contents/MacOS/ShennianClientLauncher +0 -0
- package/dist/native/macos-background-client/Shennian.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -2
- package/dist/native/macos-background-client/Shennian.app/Contents/Resources/zh-Hans.lproj/InfoPlist.strings +0 -2
- package/dist/native/macos-background-client/Shennian.app/Contents/_CodeSignature/CodeResources +0 -153
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
function n(t){if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return String(t)}}function r(t){return t.type==="text"?t.text:t.type==="resource_link"?t.name||t.uri:t.type==="resource"?"text"in t.resource?t.resource.text:`[${t.resource.mimeType??"resource"}]`:`[${t.type}: ${t.mimeType}]`}function o(t){return t?.length?t.map(e=>e.type==="content"?r(e.content):e.type==="diff"?`${e.path}
|
|
2
|
+
${e.newText}`:`[terminal: ${e.terminalId}]`).join(`
|
|
3
|
+
`):""}function l(t,e){switch(t.sessionUpdate){case"agent_message_chunk":return[e({state:"delta",text:r(t.content)})];case"agent_thought_chunk":return[e({state:"delta",text:r(t.content),thinking:!0})];case"tool_call":return[e({state:"tool-call",toolCallId:t.toolCallId,toolStatus:"running",name:t.title,args:typeof t.rawInput=="object"&&t.rawInput!==null?t.rawInput:t.rawInput===void 0?void 0:{input:t.rawInput}})];case"tool_call_update":{if(t.status!=="completed"&&t.status!=="failed")return[];const s=o(t.content)||(t.rawOutput===void 0?t.status:n(t.rawOutput));return[e({state:"tool-result",toolCallId:t.toolCallId,toolStatus:t.status,name:t.title??t.toolCallId,result:s})]}case"usage_update":return[e({state:"notify",title:"Context usage",text:`${t.used}/${t.size}`})];case"plan":case"plan_update":return[e({state:"notify",title:"Plan updated",text:n(t)})];case"session_info_update":return t.title?[e({state:"notify",title:"Session",text:t.title})]:[];default:return[]}}export{l as mapAcpSessionUpdate};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { AcpAgentManifest } from './types.js';
|
|
3
|
+
export declare const ACP_MANIFEST_SCHEMA_ID = "https://shennian.net/schemas/acp-agent-manifest-v1.json";
|
|
4
|
+
export declare const ACP_MANIFEST_DIRECTORY = "acp-agents.d";
|
|
5
|
+
declare const manifestSchema: z.ZodObject<{
|
|
6
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
7
|
+
id: z.ZodString;
|
|
8
|
+
name: z.ZodString;
|
|
9
|
+
protocol: z.ZodLiteral<"acp">;
|
|
10
|
+
command: z.ZodString;
|
|
11
|
+
args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
12
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
13
|
+
homepage: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
14
|
+
clientCapabilities: z.ZodOptional<z.ZodObject<{
|
|
15
|
+
readTextFile: z.ZodDefault<z.ZodBoolean>;
|
|
16
|
+
writeTextFile: z.ZodDefault<z.ZodBoolean>;
|
|
17
|
+
terminal: z.ZodDefault<z.ZodBoolean>;
|
|
18
|
+
}, "strict", z.ZodTypeAny, {
|
|
19
|
+
terminal: boolean;
|
|
20
|
+
readTextFile: boolean;
|
|
21
|
+
writeTextFile: boolean;
|
|
22
|
+
}, {
|
|
23
|
+
terminal?: boolean | undefined;
|
|
24
|
+
readTextFile?: boolean | undefined;
|
|
25
|
+
writeTextFile?: boolean | undefined;
|
|
26
|
+
}>>;
|
|
27
|
+
}, "strict", z.ZodTypeAny, {
|
|
28
|
+
id: string;
|
|
29
|
+
schemaVersion: 1;
|
|
30
|
+
name: string;
|
|
31
|
+
args: string[];
|
|
32
|
+
protocol: "acp";
|
|
33
|
+
command: string;
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
homepage?: string | undefined;
|
|
36
|
+
clientCapabilities?: {
|
|
37
|
+
terminal: boolean;
|
|
38
|
+
readTextFile: boolean;
|
|
39
|
+
writeTextFile: boolean;
|
|
40
|
+
} | undefined;
|
|
41
|
+
}, {
|
|
42
|
+
id: string;
|
|
43
|
+
schemaVersion: 1;
|
|
44
|
+
name: string;
|
|
45
|
+
protocol: "acp";
|
|
46
|
+
command: string;
|
|
47
|
+
args?: string[] | undefined;
|
|
48
|
+
enabled?: boolean | undefined;
|
|
49
|
+
homepage?: string | undefined;
|
|
50
|
+
clientCapabilities?: {
|
|
51
|
+
terminal?: boolean | undefined;
|
|
52
|
+
readTextFile?: boolean | undefined;
|
|
53
|
+
writeTextFile?: boolean | undefined;
|
|
54
|
+
} | undefined;
|
|
55
|
+
}>;
|
|
56
|
+
export type AcpManifestDocument = z.infer<typeof manifestSchema>;
|
|
57
|
+
export declare function getAcpManifestDirectory(): string;
|
|
58
|
+
export declare function validateAcpManifest(input: unknown): AcpManifestDocument;
|
|
59
|
+
export declare function readAcpManifests(directory?: string): AcpAgentManifest[];
|
|
60
|
+
export declare function writeAcpManifest(document: AcpManifestDocument, directory?: string): string;
|
|
61
|
+
export declare function removeAcpManifest(id: string, directory?: string): boolean;
|
|
62
|
+
export declare function validateAcpId(id: string): string;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import o from"node:fs";import c from"node:path";import{z as n}from"zod";import{resolveShennianPath as u}from"../../config/index.js";const w="https://shennian.net/schemas/acp-agent-manifest-v1.json",h="acp-agents.d",S=64*1024,f=n.object({schemaVersion:n.literal(1),id:n.string().regex(/^[a-z0-9][a-z0-9._-]{0,63}$/),name:n.string().trim().min(1).max(80),protocol:n.literal("acp"),command:n.string().trim().min(1).max(4096),args:n.array(n.string().max(4096)).max(64).default([]),enabled:n.boolean().default(!0),homepage:n.string().url().refine(t=>new URL(t).protocol==="https:").optional(),clientCapabilities:n.object({readTextFile:n.boolean().default(!1),writeTextFile:n.boolean().default(!1),terminal:n.boolean().default(!1)}).strict().optional()}).strict();function m(){return u(h)}function d(t){const e=f.parse(t);if(e.command.includes("\0")||e.args.some(i=>i.includes("\0")))throw new Error("ACP manifest command contains NUL");if(!c.isAbsolute(e.command)&&c.basename(e.command)!==e.command)throw new Error("ACP manifest command must be absolute or a plain command name");return e}function C(t=m()){let e;try{e=o.readdirSync(t).filter(s=>s.endsWith(".json")).sort()}catch{return[]}const i=[],r=new Set;for(const s of e){const l=c.join(t,s);try{const p=o.statSync(l);if(!p.isFile()||p.size>S)continue;const a=d(JSON.parse(o.readFileSync(l,"utf8")));if(!a.enabled||r.has(a.id))continue;r.add(a.id),i.push({id:a.id,name:a.name,homepage:a.homepage,source:"local",launch:{command:a.command,args:a.args},...a.clientCapabilities?{clientCapabilities:a.clientCapabilities}:{}})}catch{}}return i}function M(t,e=m()){const i=d(t);o.mkdirSync(e,{recursive:!0,mode:448}),process.platform!=="win32"&&o.chmodSync(e,448);const r=c.join(e,`${i.id}.json`),s=`${r}.tmp-${process.pid}`;try{o.writeFileSync(s,`${JSON.stringify(i,null,2)}
|
|
2
|
+
`,{mode:384,flag:"wx"}),o.renameSync(s,r),process.platform!=="win32"&&o.chmodSync(r,384)}finally{try{o.unlinkSync(s)}catch{}}return r}function j(t,e=m()){const i=g(t);try{return o.unlinkSync(c.join(e,`${i}.json`)),!0}catch{return!1}}function g(t){return f.shape.id.parse(t)}export{h as ACP_MANIFEST_DIRECTORY,w as ACP_MANIFEST_SCHEMA_ID,m as getAcpManifestDirectory,C as readAcpManifests,j as removeAcpManifest,g as validateAcpId,d as validateAcpManifest,M as writeAcpManifest};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function recordAcpScan(input: {
|
|
2
|
+
durationMs: number;
|
|
3
|
+
candidateCount: number;
|
|
4
|
+
installedCount: number;
|
|
5
|
+
cacheHits: number;
|
|
6
|
+
handshakeFailures: number;
|
|
7
|
+
handshakeFailuresByKind: Record<string, number>;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function recordAcpAuthFailure(): void;
|
|
10
|
+
export declare function recordAcpImportResult(ok: boolean): void;
|
|
11
|
+
export declare function getAcpLocalMetrics(): {
|
|
12
|
+
handshakeFailuresByKind: {
|
|
13
|
+
[x: string]: number;
|
|
14
|
+
};
|
|
15
|
+
scans: number;
|
|
16
|
+
lastScanDurationMs: number;
|
|
17
|
+
lastCandidateCount: number;
|
|
18
|
+
lastInstalledCount: number;
|
|
19
|
+
cacheHits: number;
|
|
20
|
+
handshakeFailures: number;
|
|
21
|
+
authFailures: number;
|
|
22
|
+
importSuccess: number;
|
|
23
|
+
importFailure: number;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a={scans:0,lastScanDurationMs:0,lastCandidateCount:0,lastInstalledCount:0,cacheHits:0,handshakeFailures:0,handshakeFailuresByKind:{},authFailures:0,importSuccess:0,importFailure:0};function n(e){a.scans+=1,a.lastScanDurationMs=Math.max(0,Math.round(e.durationMs)),a.lastCandidateCount=e.candidateCount,a.lastInstalledCount=e.installedCount,a.cacheHits+=e.cacheHits,a.handshakeFailures+=e.handshakeFailures;for(const[s,t]of Object.entries(e.handshakeFailuresByKind))a.handshakeFailuresByKind[s]=(a.handshakeFailuresByKind[s]??0)+t}function r(){a.authFailures+=1}function i(e){e?a.importSuccess+=1:a.importFailure+=1}function c(){return{...a,handshakeFailuresByKind:{...a.handshakeFailuresByKind}}}export{c as getAcpLocalMetrics,r as recordAcpAuthFailure,i as recordAcpImportResult,n as recordAcpScan};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import i from"node:fs";import d from"node:os";import r from"node:path";import{spawnSync as h}from"node:child_process";const p=/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/i,m=/^[a-zA-Z0-9._-]{1,128}$/;let u;function w(t){return[...new Set([...S(t?.npm),..._(t?.uv)])]}function S(t){if(!t||!p.test(t))return[];if(u??=j(),!u)return[];try{const o=r.join(u,...t.split("/"),"package.json"),s=i.statSync(o);if(!s.isFile()||s.size>256*1024)return[];const n=JSON.parse(i.readFileSync(o,"utf8")),e=t.split("/").at(-1)??"";return(typeof n.bin=="string"?[e]:Object.keys(n.bin??{})).filter(c=>m.test(c))}catch{return[]}}function j(){const t=h("npm",["root","--global"],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:3e3,windowsHide:!0}),o=t.status===0?t.stdout.trim():"";return o&&r.isAbsolute(o)?o:null}function _(t){if(!t||!p.test(t))return[];const o=t.toLowerCase().replace(/[-_.]+/g,"-"),s=[r.join(d.homedir(),".local","share","uv","tools",o),r.join(d.homedir(),".local","share","uv","tools",t)],n=[];for(const e of s)for(const l of b(e))try{const f=i.readFileSync(l,"utf8").match(/\[console_scripts\]([\s\S]*?)(?:\n\[|$)/)?.[1]??"";for(const y of f.split(/\r?\n/)){const a=y.split("=",1)[0]?.trim()??"";m.test(a)&&n.push(a)}}catch{}return n}function b(t){const o=[];let s=[];try{s=i.readdirSync(r.join(t,"lib")).filter(n=>n.startsWith("python")).slice(0,8)}catch{}for(const n of s){const e=r.join(t,"lib",n,"site-packages");let l=[];try{l=i.readdirSync(e).filter(c=>c.endsWith(".dist-info")).slice(0,32)}catch{}for(const c of l){const f=r.join(e,c,"entry_points.txt");try{i.statSync(f).isFile()&&i.statSync(f).size<=64*1024&&o.push(f)}catch{}}}return o}export{w as deriveInstalledPackageCommands};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as acp from '@agentclientprotocol/sdk';
|
|
2
|
+
import type { InitializeResponse, RequestPermissionRequest, RequestPermissionResponse, SessionNotification } from '@agentclientprotocol/sdk';
|
|
3
|
+
import type { AcpLaunchSpec } from './types.js';
|
|
4
|
+
export type AcpProcessClientHandlers = {
|
|
5
|
+
onSessionUpdate(params: SessionNotification): void | Promise<void>;
|
|
6
|
+
onPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse>;
|
|
7
|
+
};
|
|
8
|
+
export declare class AcpProcessClient {
|
|
9
|
+
private readonly launch;
|
|
10
|
+
private readonly handlers;
|
|
11
|
+
private readonly environment;
|
|
12
|
+
private readonly clientCapabilities;
|
|
13
|
+
private readonly timeouts;
|
|
14
|
+
private process;
|
|
15
|
+
private connection;
|
|
16
|
+
private initialization;
|
|
17
|
+
private stderr;
|
|
18
|
+
private clientServices;
|
|
19
|
+
constructor(launch: AcpLaunchSpec, handlers: AcpProcessClientHandlers, environment?: NodeJS.ProcessEnv, clientCapabilities?: {
|
|
20
|
+
readTextFile?: boolean;
|
|
21
|
+
writeTextFile?: boolean;
|
|
22
|
+
terminal?: boolean;
|
|
23
|
+
terminalAuth?: boolean;
|
|
24
|
+
}, timeouts?: {
|
|
25
|
+
initializeMs?: number;
|
|
26
|
+
sessionMs?: number;
|
|
27
|
+
authMs?: number;
|
|
28
|
+
});
|
|
29
|
+
get capabilities(): NonNullable<InitializeResponse['agentCapabilities']>;
|
|
30
|
+
get initializeResponse(): InitializeResponse | null;
|
|
31
|
+
listSessions(cwd?: string, cursor?: string | null): Promise<acp.ListSessionsResponse>;
|
|
32
|
+
authenticate(methodId: string): Promise<void>;
|
|
33
|
+
logout(): Promise<void>;
|
|
34
|
+
closeSession(sessionId: string): Promise<void>;
|
|
35
|
+
deleteSession(sessionId: string): Promise<void>;
|
|
36
|
+
get agent(): acp.ClientContext;
|
|
37
|
+
start(cwd: string): Promise<InitializeResponse>;
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{spawn as m}from"node:child_process";import{Readable as d,Transform as p,Writable as S}from"node:stream";import*as n from"@agentclientprotocol/sdk";import{AcpClientServices as f}from"./client-services.js";import{terminateProcessTree as h}from"./process-tree.js";class _{launch;handlers;environment;clientCapabilities;timeouts;process=null;connection=null;initialization=null;stderr="";clientServices=null;constructor(i,t,s=process.env,r={},o={}){this.launch=i,this.handlers=t,this.environment=s,this.clientCapabilities=r,this.timeouts=o}get capabilities(){return this.initialization?.agentCapabilities??{}}get initializeResponse(){return this.initialization}async listSessions(i,t){return a(this.agent.request(n.methods.agent.session.list,{cwd:i,cursor:t}),this.timeouts.sessionMs??8e3,"ACP session list timed out")}async authenticate(i){await a(this.agent.request(n.methods.agent.authenticate,{methodId:i}),this.timeouts.authMs??12e4,"ACP authentication timed out")}async logout(){await a(this.agent.request(n.methods.agent.logout,{}),8e3,"ACP logout timed out")}async closeSession(i){await a(this.agent.request(n.methods.agent.session.close,{sessionId:i}),8e3,"ACP session close timed out")}async deleteSession(i){await a(this.agent.request(n.methods.agent.session.delete,{sessionId:i}),8e3,"ACP session delete timed out")}get agent(){if(!this.connection)throw new Error("ACP process is not connected");return this.connection.agent}async start(i){if(this.connection)return this.initialization;const t=m(this.launch.command,this.launch.args??[],{cwd:i,env:{...this.environment,...this.launch.env},stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32",windowsHide:!0});this.process=t,t.stderr.on("data",e=>{this.stderr.length<16384&&(this.stderr+=e.toString())}),this.clientServices=Object.values(this.clientCapabilities).some(Boolean)?new f(i,this.clientCapabilities):null;let s=n.client({name:"Shennian"}).onRequest(n.methods.client.session.requestPermission,({params:e})=>this.handlers.onPermission(e)).onNotification(n.methods.client.session.update,({params:e})=>this.handlers.onSessionUpdate(e));this.clientServices?.enabled.readTextFile&&(s=s.onRequest(n.methods.client.fs.readTextFile,({params:e})=>this.clientServices.readTextFile(e))),this.clientServices?.enabled.writeTextFile&&(s=s.onRequest(n.methods.client.fs.writeTextFile,({params:e})=>this.clientServices.writeTextFile(e))),this.clientServices?.enabled.terminal&&(s=s.onRequest(n.methods.client.terminal.create,({params:e})=>this.clientServices.createTerminal(e)).onRequest(n.methods.client.terminal.output,({params:e})=>this.clientServices.terminalOutput(e)).onRequest(n.methods.client.terminal.waitForExit,({params:e})=>this.clientServices.waitForTerminalExit(e)).onRequest(n.methods.client.terminal.kill,({params:e})=>this.clientServices.killTerminal(e)).onRequest(n.methods.client.terminal.release,({params:e})=>this.clientServices.releaseTerminal(e)));const r=new p({transform:C(()=>h(t,!0))});t.stdout.pipe(r);const o=n.ndJsonStream(S.toWeb(t.stdin),d.toWeb(r)),l=s.connect(o);this.connection=l,t.once("error",e=>l.close(e)),t.once("exit",(e,u)=>{l.close(new Error(`ACP agent exited (${e??u??"unknown"})`))});try{const e=await a(l.agent.request(n.methods.agent.initialize,{protocolVersion:n.PROTOCOL_VERSION,clientCapabilities:{...this.clientServices?.capabilities??{},...this.clientCapabilities.terminalAuth?{auth:{terminal:!0}}:{}},clientInfo:{name:"Shennian",version:"0.4.13"}}),this.timeouts.initializeMs??8e3,"ACP initialize timed out");if(e.protocolVersion!==n.PROTOCOL_VERSION)throw new Error(`Unsupported ACP protocol version ${e.protocolVersion}`);return this.initialization=e,e}catch(e){throw await this.close(),e}}async close(){const i=this.connection,t=this.process;this.connection=null,this.process=null,this.initialization=null,this.clientServices?.close(),this.clientServices=null,i?.close(),t&&t.exitCode===null&&t.signalCode===null&&(h(t),setTimeout(()=>h(t,!0),2e3).unref?.())}}const g=4*1024*1024,w=64*1024*1024;function C(c){let i=0,t=0;return function(s,r,o){i+=s.length;for(const l of s)t=l===10?0:t+1;if(i>w||t>g){c(),o(new Error("ACP output exceeded safety limit"));return}o(null,s)}}async function a(c,i,t){let s;try{return await Promise.race([c,new Promise((r,o)=>{s=setTimeout(()=>o(new Error(t)),i)})])}finally{s&&clearTimeout(s)}}export{_ as AcpProcessClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{spawn as i}from"node:child_process";function s(e,r=!1){if(!(!e.pid||e.exitCode!==null||e.signalCode!==null)){if(process.platform==="win32"){i("taskkill",["/PID",String(e.pid),"/T",...r?["/F"]:[]],{stdio:"ignore",windowsHide:!0}).unref();return}try{process.kill(-e.pid,r?"SIGKILL":"SIGTERM")}catch{e.kill(r?"SIGKILL":"SIGTERM")}}}export{s as terminateProcessTree};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AcpAgentType } from '@shennian/wire';
|
|
2
|
+
import type { AgentCapabilities, AuthMethod, Implementation } from '@agentclientprotocol/sdk';
|
|
3
|
+
export type AcpLaunchSpec = {
|
|
4
|
+
command: string;
|
|
5
|
+
args?: string[];
|
|
6
|
+
env?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
export type AcpAgentManifest = {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
launch: AcpLaunchSpec;
|
|
12
|
+
/** Original command before platform shim wrapping; used to append terminal-auth args safely. */
|
|
13
|
+
authLaunch?: AcpLaunchSpec;
|
|
14
|
+
homepage?: string;
|
|
15
|
+
source?: 'registry' | 'local';
|
|
16
|
+
packageHints?: {
|
|
17
|
+
npm?: string;
|
|
18
|
+
uv?: string;
|
|
19
|
+
};
|
|
20
|
+
clientCapabilities?: {
|
|
21
|
+
readTextFile?: boolean;
|
|
22
|
+
writeTextFile?: boolean;
|
|
23
|
+
terminal?: boolean;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
export type AcpInitialization = {
|
|
27
|
+
agentInfo?: Implementation | null;
|
|
28
|
+
authMethods: AuthMethod[];
|
|
29
|
+
capabilities: AgentCapabilities;
|
|
30
|
+
};
|
|
31
|
+
export declare function acpAgentType(id: string): AcpAgentType;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function n(e){return`acp:${e}`}export{n as acpAgentType};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare class AcpDiscoveryWatcher {
|
|
2
|
+
private readonly refresh;
|
|
3
|
+
private readonly options;
|
|
4
|
+
private watcher;
|
|
5
|
+
private debounceTimer;
|
|
6
|
+
private intervalTimer;
|
|
7
|
+
private catalogTimer;
|
|
8
|
+
private running;
|
|
9
|
+
private rerun;
|
|
10
|
+
constructor(refresh: () => Promise<void>, options?: {
|
|
11
|
+
directory?: string;
|
|
12
|
+
intervalMs?: number;
|
|
13
|
+
catalogIntervalMs?: number;
|
|
14
|
+
debounceMs?: number;
|
|
15
|
+
});
|
|
16
|
+
start(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
/** Public so platform watcher adapters and deterministic tests share one debounce path. */
|
|
19
|
+
notifyManifestChanged(): void;
|
|
20
|
+
private schedule;
|
|
21
|
+
private trigger;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:fs";import{getAcpManifestDirectory as r}from"./manifests.js";const s=720*60*1e3,n=360*60*1e3,h=500;class a{refresh;options;watcher=null;debounceTimer=null;intervalTimer=null;catalogTimer=null;running=null;rerun=!1;constructor(e,i={}){this.refresh=e,this.options=i}start(){if(this.watcher||this.intervalTimer)return;const e=this.options.directory??r();t.mkdirSync(e,{recursive:!0,mode:448}),process.platform!=="win32"&&t.chmodSync(e,448),this.watcher=t.watch(e,{persistent:!1},()=>this.notifyManifestChanged()),this.watcher.on("error",()=>{this.watcher?.close(),this.watcher=null}),this.intervalTimer=setInterval(()=>this.trigger(),this.options.intervalMs??s),this.intervalTimer.unref?.(),this.catalogTimer=setInterval(()=>this.trigger(),this.options.catalogIntervalMs??n),this.catalogTimer.unref?.()}stop(){this.watcher?.close(),this.watcher=null,this.debounceTimer&&clearTimeout(this.debounceTimer),this.intervalTimer&&clearInterval(this.intervalTimer),this.catalogTimer&&clearInterval(this.catalogTimer),this.debounceTimer=null,this.intervalTimer=null,this.catalogTimer=null}notifyManifestChanged(){this.schedule()}schedule(){this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.trigger()},this.options.debounceMs??h),this.debounceTimer.unref?.()}trigger(){if(this.running){this.rerun=!0;return}this.running=this.refresh().catch(()=>{}).finally(()=>{this.running=null,this.rerun&&(this.rerun=!1,this.trigger())})}}export{a as AcpDiscoveryWatcher};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import type { AgentType, ChatAttachmentMeta, ExternalChannelSessionStatus, SessionRunPhase } from '@shennian/wire';
|
|
2
|
+
import type { AgentType, ChatAttachmentMeta, ExternalChannelSessionStatus, SessionRunPhase, ModelInfo } from '@shennian/wire';
|
|
3
3
|
import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
|
|
4
4
|
import type { ManagedRoomExecutor } from '../room/managed-room-ipc.js';
|
|
5
5
|
export type AgentEvent = {
|
|
@@ -10,16 +10,31 @@ export type AgentEvent = {
|
|
|
10
10
|
thinking?: boolean;
|
|
11
11
|
runPhase?: SessionRunPhase;
|
|
12
12
|
name?: string;
|
|
13
|
+
toolCallId?: string;
|
|
14
|
+
toolStatus?: 'running' | 'completed' | 'failed';
|
|
13
15
|
args?: Record<string, unknown>;
|
|
14
16
|
result?: string;
|
|
15
17
|
durationMs?: number;
|
|
16
18
|
message?: string;
|
|
19
|
+
errorCode?: 'acp_auth_required';
|
|
17
20
|
approval?: {
|
|
18
21
|
title?: string;
|
|
19
22
|
content?: string;
|
|
20
23
|
source?: string;
|
|
21
24
|
actionHint?: string;
|
|
22
25
|
};
|
|
26
|
+
permission?: {
|
|
27
|
+
id: string;
|
|
28
|
+
sessionId?: string;
|
|
29
|
+
agentSessionId?: string;
|
|
30
|
+
runId?: string;
|
|
31
|
+
toolCallId: string;
|
|
32
|
+
options: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
kind: 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always';
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
23
38
|
usage?: {
|
|
24
39
|
inputTokens: number;
|
|
25
40
|
outputTokens: number;
|
|
@@ -33,7 +48,23 @@ export type AgentSendResult = {
|
|
|
33
48
|
queuedSubmissionId?: string;
|
|
34
49
|
};
|
|
35
50
|
export type AgentSendContext = {
|
|
36
|
-
source: 'room_activation';
|
|
51
|
+
source: 'room_activation' | 'upgrade_recovery';
|
|
52
|
+
/** Marks internal recovery input; it must never be persisted as user-authored text. */
|
|
53
|
+
recovery?: 'upgrade';
|
|
54
|
+
};
|
|
55
|
+
export type ImportedAgentMessage = {
|
|
56
|
+
/** Stable within repeated imports of the same provider session. */
|
|
57
|
+
sourceId: string;
|
|
58
|
+
role: 'user' | 'agent';
|
|
59
|
+
payload: string;
|
|
60
|
+
};
|
|
61
|
+
export type AgentSessionOptions = {
|
|
62
|
+
models: ModelInfo[];
|
|
63
|
+
modes: Array<{
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
description?: string;
|
|
67
|
+
}>;
|
|
37
68
|
};
|
|
38
69
|
export type AgentStartOptions = {
|
|
39
70
|
/** Test-only callers can disable provider-native history persistence. */
|
|
@@ -41,12 +72,56 @@ export type AgentStartOptions = {
|
|
|
41
72
|
/** Keep an internal orchestration session out of the provider's user-facing task list. */
|
|
42
73
|
nativeSessionVisibility?: 'user' | 'internal';
|
|
43
74
|
};
|
|
75
|
+
export type AgentRecoveryCapabilities = {
|
|
76
|
+
inspectTerminal: boolean;
|
|
77
|
+
reattachRun: boolean;
|
|
78
|
+
resumeRun: boolean;
|
|
79
|
+
resumeSession: boolean;
|
|
80
|
+
};
|
|
81
|
+
export type AgentRecoveryInput = {
|
|
82
|
+
sessionId: string;
|
|
83
|
+
shennianRunId: string;
|
|
84
|
+
workDir: string;
|
|
85
|
+
agentSessionId: string | null;
|
|
86
|
+
modelId?: string | null;
|
|
87
|
+
reasoningEffort?: string | null;
|
|
88
|
+
providerRunId: string | null;
|
|
89
|
+
lastObservedEventSeq: number;
|
|
90
|
+
continueDispatchState: 'none' | 'intent' | 'accepted';
|
|
91
|
+
systemPrompt?: string | null;
|
|
92
|
+
sendContext?: AgentSendContext;
|
|
93
|
+
/** Durable provider-level de-duplication fence, invoked immediately before Continue. */
|
|
94
|
+
beforeContinueDispatch?: () => void;
|
|
95
|
+
};
|
|
96
|
+
export type AgentRecoveryInspection = {
|
|
97
|
+
state: 'terminal' | 'active' | 'interrupted' | 'unknown';
|
|
98
|
+
providerRunId?: string | null;
|
|
99
|
+
runPhase?: SessionRunPhase | null;
|
|
100
|
+
};
|
|
101
|
+
export type AgentRecoveryResult = {
|
|
102
|
+
result: 'terminal_reconciled';
|
|
103
|
+
} | {
|
|
104
|
+
result: 'reattached';
|
|
105
|
+
} | {
|
|
106
|
+
result: 'resumed';
|
|
107
|
+
} | {
|
|
108
|
+
result: 'continued';
|
|
109
|
+
} | {
|
|
110
|
+
result: 'interrupted_by_upgrade';
|
|
111
|
+
reason: 'resume_unsupported' | 'missing_agent_session';
|
|
112
|
+
} | {
|
|
113
|
+
result: 'recovery_failed';
|
|
114
|
+
reason: 'continue_dispatch_outcome_unknown' | 'provider_session_missing' | 'provider_identity_mismatch' | 'authentication_required' | 'permission_context_unavailable' | 'provider_error';
|
|
115
|
+
message?: string;
|
|
116
|
+
};
|
|
44
117
|
export interface AgentAdapterEvents {
|
|
45
118
|
agentEvent: [event: AgentEvent];
|
|
46
119
|
error: [error: Error];
|
|
47
120
|
}
|
|
48
121
|
export declare abstract class AgentAdapter extends EventEmitter<AgentAdapterEvents> {
|
|
49
122
|
abstract readonly type: AgentType;
|
|
123
|
+
/** Explicit fail-closed default; resumable adapters must opt in. */
|
|
124
|
+
get recoveryCapabilities(): AgentRecoveryCapabilities;
|
|
50
125
|
configure?(options: {
|
|
51
126
|
sessionId?: string;
|
|
52
127
|
externalChannel?: ExternalChannelSessionStatus | null;
|
|
@@ -62,11 +137,30 @@ export declare abstract class AgentAdapter extends EventEmitter<AgentAdapterEven
|
|
|
62
137
|
canStop?: boolean;
|
|
63
138
|
}>;
|
|
64
139
|
setTitle?(agentSessionId: string, title: string, workDir?: string): Promise<void>;
|
|
140
|
+
respondToPermission?(permissionId: string, optionId: string): Promise<void>;
|
|
141
|
+
authenticate?(methodId: string): Promise<void>;
|
|
142
|
+
hasPendingAuthenticationRetry?(): boolean;
|
|
143
|
+
resumeAfterExternalAuthentication?(): Promise<void>;
|
|
144
|
+
getSessionOptions?(): AgentSessionOptions;
|
|
145
|
+
logout?(): Promise<void>;
|
|
146
|
+
listExternalSessions?(options?: {
|
|
147
|
+
cwd?: string;
|
|
148
|
+
}): Promise<import('@shennian/wire').AcpExternalSessionInfo[]>;
|
|
149
|
+
loadExternalSession?(agentSessionId: string): Promise<ImportedAgentMessage[]>;
|
|
150
|
+
closeExternalSession?(agentSessionId: string): Promise<void>;
|
|
151
|
+
deleteExternalSession?(agentSessionId: string): Promise<void>;
|
|
65
152
|
abstract start(sessionId: string, workDir: string, agentSessionId?: string | null, options?: AgentStartOptions): Promise<void>;
|
|
66
153
|
prepareSend?(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], systemPrompt?: string | null, context?: AgentSendContext): Promise<void>;
|
|
67
154
|
abstract send(text: string, modelId?: string, reasoningEffort?: string, attachments?: ChatAttachmentMeta[], systemPrompt?: string | null, context?: AgentSendContext): Promise<void | AgentSendResult>;
|
|
68
155
|
abstract resume(agentSessionId: string): Promise<void>;
|
|
69
156
|
abstract stop(): Promise<void>;
|
|
157
|
+
inspectInterruptedRun(input: AgentRecoveryInput): Promise<AgentRecoveryInspection>;
|
|
158
|
+
/**
|
|
159
|
+
* The coordinator starts this adapter with the exact persisted native
|
|
160
|
+
* Session before calling recoverInterruptedRun. No original prompt/history
|
|
161
|
+
* is accepted by this API, so a recovery cannot accidentally replay it.
|
|
162
|
+
*/
|
|
163
|
+
recoverInterruptedRun(input: AgentRecoveryInput): Promise<AgentRecoveryResult>;
|
|
70
164
|
}
|
|
71
165
|
export declare function registerAgent(type: AgentType, factory: () => AgentAdapter): void;
|
|
72
166
|
export declare function unregisterAgent(type: AgentType): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EventEmitter as
|
|
1
|
+
import{EventEmitter as o}from"node:events";import{RECOVERY_CONTINUE_TEXT as u}from"../session/run-recovery/schema.js";class l extends o{get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!1}}async inspectInterruptedRun(e){if(!this.getStatus)return{state:"interrupted",providerRunId:e.providerRunId};const r=await this.getStatus().catch(()=>null);return r?.active?{state:"active",providerRunId:r.runId??e.providerRunId,runPhase:r.runPhase}:{state:"interrupted",providerRunId:e.providerRunId}}async recoverInterruptedRun(e){const r=await this.inspectInterruptedRun(e);if(r.state==="terminal"&&this.recoveryCapabilities.inspectTerminal)return{result:"terminal_reconciled"};if(r.state==="active"&&this.recoveryCapabilities.reattachRun)return{result:"reattached"};if(!e.agentSessionId)return{result:"interrupted_by_upgrade",reason:"missing_agent_session"};if(!this.recoveryCapabilities.resumeSession)return{result:"interrupted_by_upgrade",reason:"resume_unsupported"};if(e.continueDispatchState!=="none")return{result:"recovery_failed",reason:"continue_dispatch_outcome_unknown"};try{return e.beforeContinueDispatch?.(),await this.send(u,e.modelId??void 0,e.reasoningEffort??void 0,void 0,e.systemPrompt,e.sendContext),{result:"continued"}}catch(i){const a=i instanceof Error?i.message:String(i),n=a.toLowerCase();return{result:"recovery_failed",reason:n.includes("auth")||n.includes("login")?"authentication_required":n.includes("permission")||n.includes("capability")?"permission_context_unavailable":n.includes("session")&&n.includes("not")?"provider_session_missing":"provider_error",message:a.slice(0,500)}}}}const s=new Map;function p(t,e){s.set(t,e)}function f(t){s.delete(t)}function g(t){const e=s.get(t);return e?e():null}function m(){return[...s.keys()]}export{l as AgentAdapter,g as createAgent,m as getRegisteredAgents,p as registerAgent,f as unregisterAgent};
|
|
@@ -9,6 +9,12 @@ export declare function supportsClaudeReasoningEffort(): boolean;
|
|
|
9
9
|
export declare class ClaudeAdapter extends AgentAdapter {
|
|
10
10
|
private readonly options;
|
|
11
11
|
readonly type: "claude";
|
|
12
|
+
get recoveryCapabilities(): {
|
|
13
|
+
inspectTerminal: boolean;
|
|
14
|
+
reattachRun: boolean;
|
|
15
|
+
resumeRun: boolean;
|
|
16
|
+
resumeSession: boolean;
|
|
17
|
+
};
|
|
12
18
|
private process;
|
|
13
19
|
private agentSessionId;
|
|
14
20
|
private sessionId;
|
|
@@ -51,3 +57,11 @@ export declare function resolveClaudeManagedInternalAccess(): {
|
|
|
51
57
|
readPaths: string[];
|
|
52
58
|
unixSockets: string[];
|
|
53
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the smallest useful read boundary for the installed Shennian CLI.
|
|
62
|
+
* npm's bin entry is a symlink into dist/bin, while runtime dependencies such
|
|
63
|
+
* as commander live under the package-root node_modules directory. Allowing
|
|
64
|
+
* only the entry point or dist therefore makes every Room CLI call fail inside
|
|
65
|
+
* Claude's native sandbox.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveClaudeShennianCliReadPaths(commandPath: string): string[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
${t.stderr??""}`,a=/(?:^|\s)--effort(?:\s|,|<)/.test(s);return
|
|
3
|
-
`)){const
|
|
4
|
-
`})}}),s.on("close",r=>{this.process===s&&(this.process=null,this.handleProcessClose(r,
|
|
1
|
+
import c from"node:fs";import o from"node:path";import{createInterface as y}from"node:readline";import{randomUUID as p}from"node:crypto";import{AgentAdapter as S,registerAgent as E}from"./adapter.js";import{resolveBuiltinCommand as m,spawnAgentCommand as x,spawnResolvedCommandSync as P}from"./command-spec.js";import{buildAgentProcessEnv as f,buildManagedAgentProcessEnv as v}from"../agent-env.js";import{buildPlatformInstructions as g}from"./platform-instructions.js";import{resolveManagedAgentAdapterEnforcement as I}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as C}from"../security/managed-agent-runtime.js";import{resolveManagedRoomIpcPaths as k}from"../daemon-ipc/paths.js";import{CLAUDE_MANAGED_RUNTIME_SECURITY as w,compileClaudeManagedPermissions as A}from"./claude-managed-permissions.js";C("claude",w);function _(n){return n?.trim()||"default"}const R=new Set(["low","medium","high","xhigh","max"]),h=new Map;function T(n){const e=n?.trim();if(e){if(R.has(e))return e;throw new Error(`Unsupported Claude reasoning effort "${e}". Supported values: low, medium, high, xhigh, max.`)}}function Q(){h.clear()}function M(n){const e=n.trim();return e?!e.includes("claude.ai connectors are disabled because"):!1}function O(){const n=m("claude");if(!n)return!1;const e=`${n.kind}:${n.path}:${n.command}:${n.args.join("\0")}`;if(h.has(e))return h.get(e)??!1;try{const t=P(n,["--help"],{encoding:"utf-8",stdio:["ignore","pipe","pipe"],timeout:3e3,env:f()}),s=`${t.stdout??""}
|
|
2
|
+
${t.stderr??""}`,a=/(?:^|\s)--effort(?:\s|,|<)/.test(s);return h.set(e,a),a}catch{return h.set(e,!1),!1}}class N extends S{options;type="claude";get recoveryCapabilities(){return{inspectTerminal:!1,reattachRun:!1,resumeRun:!1,resumeSession:!0}}process=null;agentSessionId=null;sessionId=null;workDir=null;seq=0;runId="";hasEmittedText=!1;terminalState="open";persistNativeSession=!0;managedAgentPolicy=null;constructor(e={}){super(),this.options=e}externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{},"managedAgentPolicy"in e&&(this.managedAgentPolicy=e.managedAgentPolicy??null),this.managedAgentPolicy&&(this.workDir=this.managedAgentPolicy.adapterPolicy.workDir)}async start(e,t,s,a){this.sessionId=e,this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=a?.persistNativeSession!==!1,s&&(this.agentSessionId=s)}async send(e,t,s,a,l,r){if(r?.source==="room_activation"&&!this.managedAgentPolicy)throw new Error("managed_agent_policy_missing");await this.killProcess(),this.runId=p(),this.resetRunState();const i=["-p",e,"--output-format","stream-json","--verbose",...this.buildManagedPermissionConfig()?.args??[]];this.persistNativeSession||i.push("--no-session-persistence");const u=this.options.systemPrompt??g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,l);u&&i.push("--append-system-prompt",u),this.options.hidden&&i.push("--name","Shennian Manager Substrate"),!this.managedAgentPolicy&&process.getuid?.()!==0&&i.push("--dangerously-skip-permissions"),i.push("--model",_(t));const d=T(s);d&&O()&&i.push("--effort",d),this.agentSessionId&&i.push("--resume",this.agentSessionId),this.spawnAndParse(i)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=p(),this.resetRunState();const t=["--resume",e,"--output-format","stream-json","--verbose",...this.buildManagedPermissionConfig()?.args??[]],s=this.options.systemPrompt??g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0);s&&t.push("--append-system-prompt",s),this.options.hidden&&t.push("--name","Shennian Manager Substrate"),!this.managedAgentPolicy&&process.getuid?.()!==0&&t.push("--dangerously-skip-permissions"),this.spawnAndParse(t)}async stop(){await this.killProcess()}spawnAndParse(e){const t=m("claude");if(!t){this.emit("error",new Error('Command "claude" not found. Is Claude Code CLI installed?'));return}const s=x(t,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv()});this.process=s,y({input:s.stdout}).on("line",r=>{if(r.trim())try{const i=JSON.parse(r);this.handleStreamEvent(i)}catch{}});let l="";s.stderr?.on("data",r=>{const i=r.toString();if(l+=i,this.terminalState==="open")for(const u of i.split(`
|
|
3
|
+
`)){const d=u.trim();M(d)&&this.emitEvent({state:"delta",text:d+`
|
|
4
|
+
`})}}),s.on("close",r=>{this.process===s&&(this.process=null,this.handleProcessClose(r,l))}),s.on("error",r=>{this.process===s&&(this.process=null,r.code==="ENOENT"?this.emit("error",new Error('Command "claude" not found. Is Claude Code CLI installed?')):this.emit("error",r))})}buildManagedPermissionConfig(){const e=this.managedAgentPolicy;if(!e)return null;const t=D();return A(I(e),e.adapterPolicy.workDir,{internalReadPaths:t.readPaths,internalUnixSockets:t.unixSockets,shellSubprocessPolicy:e.environmentPolicy.shellSubprocess})}buildProcessEnv(){return this.managedAgentPolicy?v({agentType:"claude",providerEnv:this.extraEnv,sessionEnv:this.extraEnv,runtimeEnv:{NO_COLOR:"1"}}):f(this.extraEnv)}handleStreamEvent(e){if(e.type==="system"&&e.subtype==="init"&&e.session_id){this.agentSessionId=e.session_id,this.emitEvent({state:"start",agentSessionId:this.agentSessionId});return}if(e.type==="system"&&e.subtype==="api_retry"&&e.error==="authentication_failed"){this.emitErrorIfOpen({state:"error",message:'Claude authentication failed. Run "claude auth login" and retry.'});return}if(e.type==="assistant"){if(e.message?.content){for(const t of e.message.content)if(t.type==="text"&&t.text){const s=this.hasEmittedText?`
|
|
5
5
|
|
|
6
6
|
`:"";this.emitEvent({state:"delta",text:s+t.text}),this.hasEmittedText=!0}else t.type==="thinking"&&t.thinking?this.emitEvent({state:"delta",text:t.thinking,thinking:!0}):t.type==="tool_use"&&this.emitEvent({state:"tool-call",name:t.name,args:t.input});return}if(e.subtype==="text"&&e.text){const t=this.hasEmittedText?`
|
|
7
7
|
|
|
8
|
-
`:"";this.emitEvent({state:"delta",text:t+e.text}),this.hasEmittedText=!0}else e.subtype==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.subtype==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});return}if(e.type==="result")if(e.subtype==="tool_result"){const t=typeof e.content=="string"?e.content:JSON.stringify(e.content);this.emitEvent({state:"tool-result",name:e.name,result:t})}else if(e.subtype==="success"){e.session_id&&(this.agentSessionId=e.session_id);const t=e.usage??e.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens}:void 0})}else e.subtype==="error"&&this.emitErrorIfOpen({state:"error",message:e.error??e.result??"unknown error"})}handleProcessClose(e,t){if(e!==0&&e!==null){const s=t.trim()||`claude exited with code ${e}`;this.emitErrorIfOpen({state:"error",message:s});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent(e))}resetRunState(){this.seq=0,this.hasEmittedText=!1,this.terminalState="open"}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}async killProcess(){const e=this.process;e&&(this.process=null,e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}))}}function
|
|
8
|
+
`:"";this.emitEvent({state:"delta",text:t+e.text}),this.hasEmittedText=!0}else e.subtype==="thinking"&&e.thinking?this.emitEvent({state:"delta",text:e.thinking,thinking:!0}):e.subtype==="tool_use"&&this.emitEvent({state:"tool-call",name:e.name,args:e.input});return}if(e.type==="result")if(e.subtype==="tool_result"){const t=typeof e.content=="string"?e.content:JSON.stringify(e.content);this.emitEvent({state:"tool-result",name:e.name,result:t})}else if(e.subtype==="success"){e.session_id&&(this.agentSessionId=e.session_id);const t=e.usage??e.message?.usage;this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0,usage:t?{inputTokens:t.input_tokens,outputTokens:t.output_tokens}:void 0})}else e.subtype==="error"&&this.emitErrorIfOpen({state:"error",message:e.error??e.result??"unknown error"})}handleProcessClose(e,t){if(e!==0&&e!==null){const s=t.trim()||`claude exited with code ${e}`;this.emitErrorIfOpen({state:"error",message:s});return}this.emitFinalIfOpen({state:"final",agentSessionId:this.agentSessionId??void 0})}emitFinalIfOpen(e){this.terminalState==="open"&&(this.terminalState="final",this.emitEvent(e))}emitErrorIfOpen(e){this.terminalState==="open"&&(this.terminalState="error",this.emitEvent(e))}resetRunState(){this.seq=0,this.hasEmittedText=!1,this.terminalState="open"}emitEvent(e){const t={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",t)}async killProcess(){const e=this.process;e&&(this.process=null,e.kill("SIGTERM"),await new Promise(t=>{e.on("close",t),setTimeout(()=>{e.kill("SIGKILL"),t()},3e3)}))}}function D(){const n=k(),e=[n.secretFile],t=n.transport==="unix_socket"?[n.endpoint]:[];n.transport==="unix_socket"&&e.push(n.endpoint);const s=m("pi");return s?.path&&e.push(...F(s.path)),{readPaths:[...new Set(e)].sort(),unixSockets:[...new Set(t)].sort()}}function F(n){const e=o.normalize(n.trim());if(!e||!o.isAbsolute(e))return[];const t=[e];try{const s=c.realpathSync(e);t.push(s,o.dirname(s),o.dirname(o.dirname(s)));let a=o.dirname(s);for(let l=0;l<8;l+=1){const r=o.join(a,"package.json");try{if(JSON.parse(c.readFileSync(r,"utf8")).name==="shennian"){t.push(a);break}}catch{}const i=o.dirname(a);if(i===a)break;a=i}}catch{}return[...new Set(t)].sort()}E("claude",()=>new N);export{N as ClaudeAdapter,_ as normalizeClaudeModelId,T as normalizeClaudeReasoningEffort,Q as resetClaudeCapabilityCacheForTests,D as resolveClaudeManagedInternalAccess,F as resolveClaudeShennianCliReadPaths,M as shouldForwardClaudeStderrLine,O as supportsClaudeReasoningEffort};
|
|
@@ -20,6 +20,12 @@ export declare function buildCodexQueueArgs(threadId: string, text: string, atta
|
|
|
20
20
|
export declare class CodexAdapter extends AgentAdapter {
|
|
21
21
|
private readonly options;
|
|
22
22
|
readonly type: "codex";
|
|
23
|
+
get recoveryCapabilities(): {
|
|
24
|
+
inspectTerminal: boolean;
|
|
25
|
+
reattachRun: boolean;
|
|
26
|
+
resumeRun: boolean;
|
|
27
|
+
resumeSession: boolean;
|
|
28
|
+
};
|
|
23
29
|
private process;
|
|
24
30
|
private workDir;
|
|
25
31
|
private seq;
|
package/dist/src/agents/codex.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import{spawnSync as b}from"node:child_process";import{createInterface as S}from"node:readline";import{createHash as w,randomUUID as O}from"node:crypto";import g from"node:path";import{AgentAdapter as q,registerAgent as N}from"./adapter.js";import{resolveBuiltinCommand as T,spawnAgentCommand as x}from"./command-spec.js";import{buildAgentProcessEnv as D,buildManagedAgentProcessEnv as L}from"../agent-env.js";import{ensurePlatformInstructionsFile as F}from"./platform-instructions.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as C}from"@shennian/wire";import{isCodexThreadTailInterrupted as $}from"../native-fusion/codex-files.js";import{resolveManagedAgentAdapterEnforcement as U}from"../security/managed-agent-policy.js";import{registerManagedAgentRuntimeSecurity as W}from"../security/managed-agent-runtime.js";import{CODEX_MANAGED_ROOM_DYNAMIC_TOOLS as z,CodexManagedRoomToolBinding as B}from"./codex-managed-room-tools.js";import{assertCodexManagedPermissionProfile as R,CODEX_MANAGED_RUNTIME_SECURITY as G,compileCodexManagedPermissions as V,extractCodexMcpServerNames as J}from"./codex-managed-permissions.js";import{CODEX_APP_SERVER_CLIENT_INFO as H,extractAppServerErrorMessage as E,formatCodexErrorMessage as X,formatTransientCodexStatus as K,isCodexCollabAgentItem as Q,isCodexCollabAgentToolName as v,isCodexLegacyCollabAgentItem as Y,isCodexUnsupportedEffortError as j,isMissingCodexRolloutError as y,isTransientCodexErrorMessage as Z,looksLikeCodexInteractiveAuthPrompt as ee,looksLikeFatalCodexStderr as te,normalizeCodexModelId as I,normalizeCodexReasoningEffort as re,normalizeCodexStderr as _,normalizeTerminalText as se,safeStringify as k,stripGitDirectiveArtifacts as ne}from"./codex-utils.js";import{isCodexUnsupportedEffortError as Oe,isMissingCodexRolloutError as qe,normalizeCodexModelId as Ne,normalizeCodexReasoningEffort as De}from"./codex-utils.js";W("codex",G);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 ie(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 oe(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 ae(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 de(o,e){const t=g.resolve(e);let r;for(const s of o)if(s.id)for(const i of s.roots??[]){if(!i.path)continue;const n=g.resolve(i.path),a=g.relative(n,t);!(a===""||!a.startsWith("..")&&!g.isAbsolute(a))||n.length<=(r?.rootLength??-1)||(r={id:s.id,rootLength:n.length})}return r?.id}function le(o,e){const t=e?.flatMap(s=>ce(s)).filter(s=>!/^https?:\/\//i.test(s.path)).filter(s=>s.path);return{type:"text",text:he(o,e),text_elements:[],...t?.length?{local_images:t}:{}}}function ce(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=ue(o.path);return e?[{path:e}]:[]}return[]}function ue(o){if(!o||/^https?:\/\//i.test(o))return null;const e=g.extname(o),r=`${e?o.slice(0,-e.length):o}.preview.png`;return b("ffmpeg",["-y","-ss","0","-i",o,"-frames:v","1","-vf","scale=320:-1",r],{stdio:"ignore",timeout:15e3}).status===0?r:null}function he(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 s=t.map((i,n)=>{const a=i.name||`attachment-${n+1}`,d=i.mimeType?` (${i.mimeType})`:"";return`[${A(a)}${d}](<${i.path}>)`});return[o,"",...s].join(`
|
|
2
2
|
`)}function pe(o,e){const t=e?.filter(s=>s.path&&!/^https?:\/\//i.test(s.path)&&!o.includes(s.path));if(!t?.length)return o;const r=t.map((s,i)=>{const n=A(s.name||`attachment-${i+1}`);return s.kind==="image"?``:`[${n}](<${s.path}>)`});return[o,"",...r].join(`
|
|
3
|
-
`)}function me(o,e,t){return["queue","--thread",o,"--message",pe(e,t)]}function A(o){return o.replace(/[[\]\\]/g,"\\$&")}class ge extends q{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 B;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;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 w("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return D({...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 L({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r,s){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=s?.persistNativeSession!==!1,this.nativeSessionVisibility=s?.nativeSessionVisibility??"user",r&&(this.agentSessionId=r)}async prepareSend(e,t,r,s,i,n){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(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:d});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:d});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,s,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=I(t),d=re(r),c=i?.trim()??"";if(c!==this.agentSystemPrompt&&(this.agentSystemPrompt=c,this.process&&await this.killProcess(!0)),n?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=O(),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,s)};try{await this.ensureAppServer(a)}catch(h){if(!this.agentSessionId||!P(h))throw h;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:h});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:h});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,s)}}const l=this.agentSessionId;if(!l)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:l}),this.managedTurnReplay=this.managedPermissionConfig?{text:e,modelId:a,reasoningEffort:d,attachments:s,recoveryCount:0}:null;const p=await this.startTurnWithRecovery(l,e,a,d,s),m=this.agentSessionId??l;if(this.activeTurnId=p.turn?.id??null,this.managedRoomToolThreadId===m&&this.activeTurnId&&!this.managedRoomTools.bindTurn(m,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}isThreadTailInterrupted(e){return this.options.isThreadTailInterrupted?.(e)??$(e)}async enqueueForExternalOwner(e,t,r){const s=T("codex");if(!s)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=me(e,t,r),n=x(s,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let c="",l="",p=!1;const m=(u,f)=>{p||(p=!0,clearTimeout(h),u?d(u):a(f))},h=setTimeout(()=>{n.kill("SIGTERM"),m(new Error("Codex Desktop message queue timed out"))},6e4);h.unref?.(),n.stdout?.on("data",u=>{c+=u.toString()}),n.stderr?.on("data",u=>{l+=u.toString()}),n.once("error",u=>m(u)),n.once("close",u=>{const f=`${c}
|
|
3
|
+
`)}function me(o,e,t){return["queue","--thread",o,"--message",pe(e,t)]}function A(o){return o.replace(/[[\]\\]/g,"\\$&")}class ge extends q{options;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 B;configuredManagedRoomContextToken=null;configuredManagedRoomExecutor=null;managedRoomToolThreadId=null;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 w("sha256").update(JSON.stringify({sessionId:this.shennianSessionId,externalChannel:this.externalChannel,env:e,managedAgentPolicy:this.managedAgentPolicy})).digest("hex")}buildProcessEnv(e={}){if(!this.managedAgentPolicy)return D({...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 L({agentType:"codex",providerEnv:t,sessionEnv:t,runtimeEnv:e})}async start(e,t,r,s){this.workDir=this.managedAgentPolicy?.adapterPolicy.workDir??t,this.seq=0,this.persistNativeSession=s?.persistNativeSession!==!1,this.nativeSessionVisibility=s?.nativeSessionVisibility??"user",r&&(this.agentSessionId=r)}async prepareSend(e,t,r,s,i,n){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(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:d});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:d});this.preparedExternalOwnerThreadId=this.agentSessionId}}}async send(e,t,r,s,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=I(t),d=re(r),c=i?.trim()??"";if(c!==this.agentSystemPrompt&&(this.agentSystemPrompt=c,this.process&&await this.killProcess(!0)),n?.source==="room_activation"&&this.managedRoomTools.configure(this.configuredManagedRoomContextToken,this.configuredManagedRoomExecutor),this.runId=O(),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,s)};try{await this.ensureAppServer(a)}catch(h){if(!this.agentSessionId||!P(h))throw h;if(n?.source==="room_activation")throw new Error("codex_managed_thread_owned_externally",{cause:h});if(this.isThreadTailInterrupted(this.agentSessionId))throw new Error(C,{cause:h});return{deliveryMode:"external_owner_queue",queuedSubmissionId:await this.enqueueForExternalOwner(this.agentSessionId,e,s)}}const l=this.agentSessionId;if(!l)throw new Error("codex app-server did not create a thread");this.emitEvent({state:"start",agentSessionId:l}),this.managedTurnReplay=this.managedPermissionConfig?{text:e,modelId:a,reasoningEffort:d,attachments:s,recoveryCount:0}:null;const p=await this.startTurnWithRecovery(l,e,a,d,s),m=this.agentSessionId??l;if(this.activeTurnId=p.turn?.id??null,this.managedRoomToolThreadId===m&&this.activeTurnId&&!this.managedRoomTools.bindTurn(m,this.activeTurnId))throw await this.killProcess(),new Error("codex_managed_room_turn_binding_failed");return{deliveryMode:"direct"}}isThreadTailInterrupted(e){return this.options.isThreadTailInterrupted?.(e)??$(e)}async enqueueForExternalOwner(e,t,r){const s=T("codex");if(!s)throw new Error('Command "codex" not found. Is OpenAI Codex CLI installed?');const i=me(e,t,r),n=x(s,i,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});return await new Promise((a,d)=>{let c="",l="",p=!1;const m=(u,f)=>{p||(p=!0,clearTimeout(h),u?d(u):a(f))},h=setTimeout(()=>{n.kill("SIGTERM"),m(new Error("Codex Desktop message queue timed out"))},6e4);h.unref?.(),n.stdout?.on("data",u=>{c+=u.toString()}),n.stderr?.on("data",u=>{l+=u.toString()}),n.once("error",u=>m(u)),n.once("close",u=>{const f=`${c}
|
|
4
4
|
${l}`.trim();if(u!==0){m(new Error(f||`codex queue exited with code ${u??"unknown"}`));return}const M=f.match(/Queued message\s+([^\s]+)\s+for thread/i)?.[1];m(void 0,M)})})}async resume(e){await this.killProcess(),this.agentSessionId=e,this.resetTextState()}async setTitle(e,t,r){const s=e.trim(),i=t.replace(/\s+/g," ").trim();!s||!i||(this.agentSessionId=s,r?.trim()&&(this.workDir=r),await this.ensureAppServer(),await this.sendRpc("thread/name/set",{threadId:s,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 s=r?.type==="active"||!!t;return{active:s,runId:t,runPhase:s?this.mapThreadStatusToRunPhase(r):null,canStop:!!(s&&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,S({input:r.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="";r.stderr?.on("data",n=>{i+=n.toString()}),r.on("close",n=>{this.process===r&&(this.process=null,this.clearForceCloseTimer(),this.handleProcessClose(n,i))}),r.on("error",n=>{this.process===r&&(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=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??F(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,this.agentSystemPrompt);r&&t.push("-c",`model_instructions_file=${JSON.stringify(r)}`);const s=x(e,t,{cwd:this.workDir??void 0,stdio:["pipe","pipe","pipe"],env:this.buildProcessEnv({NO_COLOR:"1"})});this.process=s,this.stderrBuf="",this.appServerStartupError=null,S({input:s.stdout}).on("line",n=>{if(!n.trim())return;let a;try{a=JSON.parse(n)}catch{this.handleAppServerNonJsonStdout(n);return}this.handleAppServerMessage(a)}),s.stderr?.on("data",n=>{this.stderrBuf+=n.toString()}),s.on("close",n=>{if(this.process!==s)return;const a=this.activeTurnId!=null;this.process=null,this.appServerReady=null,this.activeTurnId=null,this.managedRoomTools.reset(),this.clearConfiguredManagedRoomContext(),this.rejectAllPending(new Error(_(this.stderrBuf)||`codex app-server exited with code ${n}`)),this.terminalState==="open"&&a&&this.handleProcessClose(n,this.stderrBuf)}),s.on("error",n=>{this.process===s&&(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=I(e);await this.sendRpc("initialize",{clientInfo:H,capabilities:{experimentalApi:!0}}),this.managedPermissionConfig=this.managedAgentPolicy?await this.buildManagedPermissionConfig():null;const r=!!(this.configuredManagedRoomContextToken||this.configuredManagedRoomExecutor);if(this.managedPermissionConfig&&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.managedPermissionConfig&&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 c={threadId:this.agentSessionId,cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},persistExtendedHistory:!0,...this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession?{threadSource:"subagent"}:{},...t?{model:t}:{}};let l;try{l=await this.sendRpc("thread/resume",{...c,excludeTurns:!0})}catch(p){if(!oe(p))throw p;l=await this.sendRpc("thread/resume",c)}this.agentSessionId=l.thread?.id??this.agentSessionId,this.managedPermissionConfig&&(R(l,this.managedPermissionConfig.profileId),r&&(this.managedRoomTools.bindThread(this.agentSessionId),this.managedRoomToolThreadId=this.agentSessionId));return}const s=this.options.hidden||this.nativeSessionVisibility==="internal"||!this.persistNativeSession,i=s?void 0:await this.resolveCodexProjectId(),n={cwd:this.workDir,approvalPolicy:"never",...this.managedPermissionConfig?{config:this.managedPermissionConfig.threadConfig}:{sandbox:"danger-full-access"},serviceName:"Shennian",threadSource:s?"subagent":"user",experimentalRawEvents:!1,persistExtendedHistory:!0,...this.managedPermissionConfig&&r?{dynamicTools:z}:{},...i?{projectId:i}:{},...t?{model:t}:{}};let a;if(s&&!this.managedPermissionConfig)a=await this.sendRpc("thread/start",n);else try{a=await this.sendRpc("thread/start",{...n,historyMode:"paginated"})}catch(c){throw ie(c)?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:c}):c}const d=a.thread?.id;if(!d)throw new Error("codex app-server thread/start returned no thread id");this.managedPermissionConfig&&(R(a,this.managedPermissionConfig.profileId),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?[]:J(await this.sendRpc("config/read",{cwd:e.adapterPolicy.workDir}));return V(U(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 c=await this.sendRpc("project/list",{limit:100,...r?{cursor:r}:{}});t.push(...c.data??[]),r=c.nextCursor}while(r);const s=de(t,e);if(s)return s;const i=g.resolve(e),n=`shennian-project-${w("sha256").update(i).digest("hex")}`,d=(await this.sendRpc("project/create",{idempotencyKey:n,name:g.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 ae(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,s,i){try{return await this.startTurn(e,t,r,s,i)}catch(n){if(!y(n))throw n;if(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:n});return this.logManagedThreadLifecycle("thread.recreated",{previousThreadId:e,threadId:a}),this.emitEvent({state:"start",agentSessionId:a}),await this.startFreshManagedTurnWithReadinessRetry(a,t,r,s,i)}return await this.killProcess(),await this.ensureAppServer(r),await this.startTurn(e,t,r,s,i)}}async startFreshManagedTurnWithReadinessRetry(e,t,r,s,i){const n=[0,100,300,1e3];let a;for(let d=0;d<n.length;d+=1){const c=n[d];c>0&&await new Promise(l=>setTimeout(l,c));try{const l=await this.startTurn(e,t,r,s,i);return d>0&&this.logManagedThreadLifecycle("rollout.ready",{threadId:e,attempt:d+1}),l}catch(l){if(a=l,!y(l)||d===n.length-1)throw l;this.logManagedThreadLifecycle("rollout.retry",{threadId:e,attempt:d+2,delayMs:n[d+1]})}}throw a}logManagedThreadLifecycle(e,t){console.error(`[codex.managed] ${JSON.stringify({event:e,...t})}`)}async startTurn(e,t,r,s,i){try{return await this.sendRpc("turn/start",{threadId:e,input:[le(t,i)],approvalPolicy:"never",...this.managedPermissionConfig?{}:{sandboxPolicy:{type:"dangerFullAccess"}},...r?{model:r}:{},...s?{effort:s}:{}})}catch(n){throw s&&j(n)?new Error(`Codex app-server does not accept reasoning effort "${s}" 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,r=6e4){if(this.appServerStartupError)return Promise.reject(this.appServerStartupError);const s=this.process;if(!s?.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 c=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:c}),s.stdin.write(`${n}
|
|
5
5
|
`,l=>{l&&(clearTimeout(c),this.pendingRequests.delete(i),d(l))})})}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,s=typeof r?.id=="string"?r.id:null,i=typeof t.threadId=="string"?t.threadId:"";s&&(this.activeTurnId=s,this.managedRoomToolThreadId===i&&!this.managedRoomTools.bindTurn(i,s)&&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 s=typeof t.status=="object"&&t.status!==null?t.status:null;s?.type==="active"&&this.emitEvent({state:"heartbeat",runPhase:this.mapThreadStatusToRunPhase(s)});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,s=typeof r?.id=="string"?r.id:null;(!this.activeTurnId||s===this.activeTurnId)&&(this.activeTurnId=null,this.managedRoomTools.deactivateTurn(),this.emitFinalIfOpen());break}case"turn/failed":{const r=E(t);if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r||"turn failed");break}case"error":{const r=E(t);if(Z(r)){this.emitTransientStatus(r);return}if(this.scheduleManagedAsyncRolloutRecovery(r))return;this.emitErrorIfOpen(r);break}}}scheduleManagedAsyncRolloutRecovery(e){if(!this.managedPermissionConfig||!this.managedTurnReplay||!y(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 s=y(r)?"no rollout found after managed thread recovery":r instanceof Error?r.message:"managed thread recovery failed";this.emitErrorIfOpen(s)}).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 s=await this.startFreshManagedTurnWithReadinessRetry(r,e.text,e.modelId,e.reasoningEffort,e.attachments);if(this.activeTurnId=s.turn?.id??null,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 s=typeof t.arguments=="object"&&t.arguments!==null?t.arguments:null,i=typeof s?.text=="string"?s.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=se(e);ee(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")&&!Q(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:"",s=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:[s,t.exitCode!=null?`
|
|
@@ -19,6 +19,11 @@ export declare function resetCommandSpecCache(): void;
|
|
|
19
19
|
export declare function getProcessPlatform(): NodeJS.Platform;
|
|
20
20
|
export declare function windowsPathToWsl(input: string): string | undefined;
|
|
21
21
|
export declare function commandExists(command: string): boolean;
|
|
22
|
+
/** Stable, non-secret identity used to invalidate local capability caches. */
|
|
23
|
+
export declare function resolveCommandFileIdentity(command: string): {
|
|
24
|
+
pathHash: string;
|
|
25
|
+
fingerprint: string;
|
|
26
|
+
} | null;
|
|
22
27
|
export declare function resolveBuiltinCommand(type: BuiltinAgentType): ResolvedCommandSpec | null;
|
|
23
28
|
export declare function buildLaunchSpec(spec: ResolvedCommandSpec, runtimeArgs: string[], cwd?: string): LaunchSpec;
|
|
24
29
|
export declare function spawnResolvedCommand(spec: ResolvedCommandSpec, runtimeArgs: string[], options?: Omit<SpawnOptions, 'cwd'> & {
|