@code-yeongyu/senpi 2026.9.28-6 → 2026.9.28-7
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/CHANGELOG.md +18 -0
- package/dist/bundle/chunks/{app-server-command-5WCSZ4A5.js → app-server-command-OUTRKHMK.js} +8 -8
- package/dist/bundle/chunks/{chunk-AWPLVQ4K.js → chunk-56COAYOU.js} +5 -5
- package/dist/bundle/chunks/{chunk-4BDFNRBE.js → chunk-B32PXL7U.js} +1 -1
- package/dist/bundle/chunks/chunk-CEMOA3TF.js +2 -0
- package/dist/bundle/chunks/{chunk-WXN35RD7.js → chunk-DB2X7SOZ.js} +2 -2
- package/dist/bundle/chunks/{chunk-DKP4BUY5.js → chunk-F2YU5BZG.js} +1 -1
- package/dist/bundle/chunks/{chunk-VGJESQOS.js → chunk-JASECXGC.js} +1 -1
- package/dist/bundle/chunks/{chunk-KDZAVHA7.js → chunk-L4OOEWAT.js} +2 -2
- package/dist/bundle/chunks/{chunk-DOEV7ITR.js → chunk-LM2UMEX6.js} +1 -1
- package/dist/bundle/chunks/{chunk-HS2LJYBI.js → chunk-Y4CH7LYI.js} +1 -1
- package/dist/bundle/chunks/chunk-YJUO37UX.js +2 -0
- package/dist/bundle/chunks/{cli-main-IBG7ONGV.js → cli-main-26P4V2BK.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-MWSJXPH6.js → help-fast-path-BOMUI7U3.js} +1 -1
- package/dist/bundle/chunks/{host-command-MTK5WOPH.js → host-command-ZVXNTXMN.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-7IJCAHHL.js → host-lifecycle-MPLSJ5NB.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +1 -1
- package/dist/bundle/chunks/{interactive-host-runtime-FWMZWK6G.js → interactive-host-runtime-Q5EORM6B.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-XUJM4MCN.js → interactive-mode-OVSKUTVC.js} +1 -1
- package/dist/bundle/chunks/{list-tips-S4U7TKER.js → list-tips-ARO4HRVK.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-7KCH3HUA.js → multi-session-host-LJNXZRFA.js} +7 -7
- package/dist/bundle/chunks/{package-manager-cli-SNVT3VKW.js → package-manager-cli-MJCRY2JU.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-VYB2Q43O.js → rpc-mode-LY4HAZ5G.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-JQ65HF2K.js → schedule-command-3ANAEUY6.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +30 -30
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/main.d.ts +2 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/app-server/cli-args.d.ts +3 -0
- package/dist/modes/app-server/cli-args.d.ts.map +1 -1
- package/dist/modes/app-server/cli-args.js +25 -4
- package/dist/modes/app-server/cli-args.js.map +1 -1
- package/dist/modes/app-server/daemon/probe.d.ts +1 -0
- package/dist/modes/app-server/daemon/probe.d.ts.map +1 -1
- package/dist/modes/app-server/daemon/probe.js +4 -1
- package/dist/modes/app-server/daemon/probe.js.map +1 -1
- package/dist/modes/app-server/daemon/spawn.d.ts +26 -0
- package/dist/modes/app-server/daemon/spawn.d.ts.map +1 -0
- package/dist/modes/app-server/daemon/spawn.js +112 -0
- package/dist/modes/app-server/daemon/spawn.js.map +1 -0
- package/dist/modes/app-server/daemon.d.ts.map +1 -1
- package/dist/modes/app-server/daemon.js +14 -112
- package/dist/modes/app-server/daemon.js.map +1 -1
- package/dist/modes/app-server/extension-paths.d.ts +7 -0
- package/dist/modes/app-server/extension-paths.d.ts.map +1 -0
- package/dist/modes/app-server/extension-paths.js +10 -0
- package/dist/modes/app-server/extension-paths.js.map +1 -0
- package/dist/modes/app-server/index.d.ts.map +1 -1
- package/dist/modes/app-server/index.js +4 -1
- package/dist/modes/app-server/index.js.map +1 -1
- package/dist/modes/app-server/runtime.d.ts +5 -1
- package/dist/modes/app-server/runtime.d.ts.map +1 -1
- package/dist/modes/app-server/runtime.js +19 -3
- package/dist/modes/app-server/runtime.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/computer-tips.js +1 -1
- package/dist/modes/interactive/tips/catalog/computer-tips.js.map +1 -1
- package/dist/modes/rpc/custom-capability.d.ts +5 -0
- package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
- package/dist/modes/rpc/custom-capability.js +5 -0
- package/dist/modes/rpc/custom-capability.js.map +1 -1
- package/dist/modes/rpc/host-observe-request.d.ts +2 -0
- package/dist/modes/rpc/host-observe-request.d.ts.map +1 -1
- package/dist/modes/rpc/host-observe-request.js +4 -0
- package/dist/modes/rpc/host-observe-request.js.map +1 -1
- package/dist/modes/rpc/host-warm.d.ts +57 -0
- package/dist/modes/rpc/host-warm.d.ts.map +1 -0
- package/dist/modes/rpc/host-warm.js +125 -0
- package/dist/modes/rpc/host-warm.js.map +1 -0
- package/dist/modes/rpc/rpc-types.d.ts +24 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +2 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +1 -1
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +12 -1
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +6 -2
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +6 -0
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/docs/app-server-daemon.md +1 -1
- package/docs/app-server.md +3 -0
- package/docs/rpc.md +48 -2
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
- package/dist/bundle/chunks/chunk-A4UMPXQF.js +0 -2
- package/dist/bundle/chunks/chunk-BTMM2C5X.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,24 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.9.28-7] - 2026-09-28
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `senpi app-server` and `senpi app-server daemon start|restart` accept `--extension <path>` (repeatable) and load those extensions into every thread, so a launcher that ships its plugin beside the engine gets the plugin's tools and `extension_event`s in app-server sessions. The daemon records the list and `restart` keeps it. ([code-yeongyu/oh-my-openagent#9117](https://github.com/code-yeongyu/oh-my-openagent/issues/9117))
|
|
22
|
+
|
|
23
|
+
- A multi-session RPC host answers a new `warm` command: it loads what the next `open_session` for a cwd, kind and context needs (the extension graph and the runtimes those extensions load) without opening a session. Nothing is listed, the connection never counts as an attachment, and the host still idles out on schedule, so a client that starts one host per session no longer pays the first-session cost on every session. Hosts advertise it as the `warm` capability. ([#2314](https://github.com/code-yeongyu/senpi/issues/2314))
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- The `/computer` introduction tip now says computer use is experimental. The tip ids, their `/computer` gating and the other tips are unchanged. ([#2315](https://github.com/code-yeongyu/senpi/issues/2315))
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
15
33
|
## [2026.9.28-6] - 2026-09-28
|
|
16
34
|
|
|
17
35
|
### Breaking Changes
|
package/dist/bundle/chunks/{app-server-command-5WCSZ4A5.js → app-server-command-OUTRKHMK.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{import_websocket_server,wrapper_default}from"./chunk-YBZDAABT.js";import{AuthStorage,DefaultResourceLoader,GOAL_STORE_CHANGED_EVENT,ModelRegistry,clearGoal,createAgentSession,createGoal,createPendingQuestion,defaultModelPerProvider,formatProviderNativeBody,formatProviderNativeSummary,getMcpService,goalStoreRef,loadMcpConfig,loadSkills,readGoal,resolveAuthMode,updateGoal}from"./chunk-
|
|
3
|
-
`)}function parseListen(value){if(value==="stdio://")return{kind:"stdio",url:"stdio://"};if(value==="unix://")return{kind:"unix",url:"unix://"};if(value.startsWith("unix:///")){let path=value.slice(7);return path.startsWith("/")?{kind:"unix",url:value,path}:void 0}if(!value.startsWith("ws://"))return;let parsed;try{parsed=new URL(value)}catch(error){if(error instanceof TypeError)return;throw error}let port=Number(parsed.port);if(!(parsed.protocol!=="ws:"||parsed.username!==""||parsed.password!==""||parsed.pathname!=="/"||parsed.search!==""||parsed.hash!==""||parsed.port===""||!Number.isInteger(port)||port<1||port>65535||isIP(parsed.hostname)===0))return{kind:"ws",url:value,host:parsed.hostname,port}}function parseDaemonVerb(value){switch(value){case"start":case"stop":case"status":case"restart":return value;default:return}}function parseWsAuth(value){return value==="off"?{kind:"off"}:{kind:"token-file",path:value}}function parseServerArgs(args){let listen3={kind:"stdio",url:"stdio://"},wsAuth,jsonLogs=!1;for(let index=0;index<args.length;index++){let arg=args[index];if(arg==="--listen"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};let parsed=parseListen(value);if(parsed===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};listen3=parsed,index++;continue}if(arg==="--ws-auth"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:"--ws-auth requires <token-file|off>."};wsAuth=parseWsAuth(value),index++;continue}if(arg==="--json-logs"){jsonLogs=!0;continue}return{kind:"usage-error",message:`Unexpected app-server argument: ${arg}`}}return{kind:"server",listen:listen3,wsAuth,jsonLogs}}function parseDaemonArgs(args){let verb=parseDaemonVerb(args[0]);if(verb===void 0)return{kind:"usage-error",message:"Usage: app-server daemon <start|stop|status|restart>."};let listen3={kind:"ws",url:"ws://127.0.0.1:18800",host:"127.0.0.1",port:18800};for(let index=1;index<args.length;index++){let arg=args[index];if(arg==="--listen"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};let parsed=parseListen(value);if(parsed===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};listen3=parsed,index++;continue}return{kind:"usage-error",message:`Unexpected app-server daemon argument: ${arg}`}}return{kind:"daemon",verb,listen:listen3}}function parseAppServerCliArgs(args){return args[0]==="daemon"?parseDaemonArgs(args.slice(1)):parseServerArgs(args)}var RpcHandlerError=class extends Error{constructor(error){super(error.message),this.name="RpcHandlerError",this.rpcError=error}};function invalidRequestError(){return{code:-32600,message:"Invalid request"}}function methodNotFoundError(method){return{code:-32601,message:`Method not found: ${method}`}}function invalidParamsError(){return{code:-32602,message:"Invalid params"}}function internalError(message="Internal error"){return{code:-32603,message}}function notInitializedError(){return{code:-32600,message:"Not initialized"}}function alreadyInitializedError(){return{code:-32600,message:"Already initialized"}}function experimentalCapabilityError(method){return{code:-32600,message:`${method} requires experimentalApi capability`}}function registerExtensionRequestMethod(registry,getThread){registry.register("extension_request",{scope:"thread",handler:async({request})=>{let params=request.params;if(typeof params!="object"||params===null||Array.isArray(params))throw new RpcHandlerError({code:-32602,message:"Invalid params"});let threadId=Reflect.get(params,"threadId"),name=Reflect.get(params,"name");if(typeof threadId!="string"||threadId.length===0||typeof name!="string"||name.length===0)throw new RpcHandlerError({code:-32602,message:"Invalid params"});try{return await getThread(threadId).extensionRunner.requestRpc(name,Reflect.get(params,"data"))}catch(error){throw new RpcHandlerError(internalError(error instanceof Error?error.message:String(error)))}}})}function createRegistry(){return new InMemoryMethodRegistry}var InMemoryMethodRegistry=class{constructor(){this.methods=new Map}register(method,registration){this.methods.set(method,registration)}async dispatch(connection,request){if(!connection.initialized){let registration2=this.methods.get(request.method);if(request.method!=="initialize"||registration2?.requiresInit!==!1)return{id:request.id,error:notInitializedError()}}if(connection.initialized&&request.method==="initialize")return{id:request.id,error:alreadyInitializedError()};let registration=this.methods.get(request.method);if(!registration)return{id:request.id,error:methodNotFoundError(request.method)};if(registration.experimental===!0&&connection.capabilities?.experimentalApi!==!0)return{id:request.id,error:experimentalCapabilityError(request.method)};try{let result=await registration.handler({connection,request});return{id:request.id,result}}catch(error){return error instanceof RpcHandlerError?{id:request.id,error:error.rpcError}:{id:request.id,error:internalError(error instanceof Error?error.message:String(error))}}}};function registerFuzzyFileSearchMethods(registry,service){registry.register("fuzzyFileSearch",{scope:"global",handler:({request})=>service.search(parseOneShotParams(request.params))}),registry.register("fuzzyFileSearch/sessionStart",{experimental:!0,scope:"global",handler:({request})=>service.startSession(parseSessionStartParams(request.params))}),registry.register("fuzzyFileSearch/sessionUpdate",{experimental:!0,scope:"global",handler:({request})=>service.updateSession(parseSessionUpdateParams(request.params))}),registry.register("fuzzyFileSearch/sessionStop",{experimental:!0,scope:"global",handler:({request})=>service.stopSession(parseSessionStopParams(request.params))})}function parseOneShotParams(value){let params=objectParams(value,"fuzzyFileSearch");return{query:requiredString(params.query,"fuzzyFileSearch query"),roots:requiredStringArray(params.roots,"fuzzyFileSearch roots"),cancellationToken:optionalString(params.cancellationToken,"fuzzyFileSearch cancellationToken")}}function parseSessionStartParams(value){let params=objectParams(value,"fuzzyFileSearch/sessionStart");return{sessionId:requiredString(params.sessionId,"fuzzyFileSearch/sessionStart sessionId"),roots:requiredStringArray(params.roots,"fuzzyFileSearch/sessionStart roots")}}function parseSessionUpdateParams(value){let params=objectParams(value,"fuzzyFileSearch/sessionUpdate");return{sessionId:requiredString(params.sessionId,"fuzzyFileSearch/sessionUpdate sessionId"),query:requiredString(params.query,"fuzzyFileSearch/sessionUpdate query")}}function parseSessionStopParams(value){let params=objectParams(value,"fuzzyFileSearch/sessionStop");return{sessionId:requiredString(params.sessionId,"fuzzyFileSearch/sessionStop sessionId")}}function objectParams(value,method){if(!isRecord(value))throw invalidRequest(`${method} params must be an object`);return value}function requiredString(value,field){if(typeof value!="string")throw invalidRequest(`${field} must be a string`);return value}function requiredStringArray(value,field){if(!Array.isArray(value)||value.some(entry=>typeof entry!="string"))throw invalidRequest(`${field} must be an array of strings`);return value}function optionalString(value,field){if(value==null)return null;if(typeof value!="string")throw invalidRequest(`${field} must be a string or null`);return value}function invalidRequest(message){return new RpcHandlerError({code:-32600,message})}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}import{lstat,readdir}from"node:fs/promises";import{basename,join as join2,relative as relative2,resolve,sep as sep2}from"node:path";var import_ignore=__toESM(require_ignore(),1);import{readFile}from"node:fs/promises";import{join,relative,sep}from"node:path";async function addGitignoreScope(matchers,directory){let gitignore=await readableText(join(directory,".gitignore"));return gitignore===void 0?matchers:[...matchers,{directory,matcher:(0,import_ignore.default)().add(gitignore)}]}function isIgnored(matchers,path,isDirectory){let ignored=!1;for(let scope of matchers){let relativePath=relative(scope.directory,path).split(sep).join("/"),result=scope.matcher.test(isDirectory?`${relativePath}/`:relativePath);result.ignored&&(ignored=!0),result.unignored&&(ignored=!1)}return ignored}async function readableText(path){try{return await readFile(path,"utf8")}catch(error){if(error instanceof Error)return;throw error}}var FUZZY_SEARCH_MAX_DEPTH=12,FUZZY_SEARCH_MAX_VISITED_ENTRIES=5e4,FUZZY_SEARCH_RESULT_LIMIT=50,FILENAME_MATCH_BONUS=1e6,CHARACTER_MATCH_SCORE=10,CONTIGUOUS_MATCH_BONUS=20,BOUNDARY_MATCH_BONUS=5,CANCELLATION_YIELD_INTERVAL=256;async function collectFuzzyFileEntries(roots,options={}){let entries=[],state={visitedEntries:0,stopped:!1},maxDepth=options.maxDepth??FUZZY_SEARCH_MAX_DEPTH,maxVisitedEntries=options.maxVisitedEntries??FUZZY_SEARCH_MAX_VISITED_ENTRIES;for(let root of roots){if(options.signal?.aborted||state.stopped)break;let absoluteRoot=resolve(root),rootStat=await readableStat(absoluteRoot);!rootStat?.isDirectory()||rootStat.isSymbolicLink()||await walkDirectory({wireRoot:root,absoluteRoot,absoluteDirectory:absoluteRoot,depth:0,matchers:[],entries,state,signal:options.signal,maxDepth,maxVisitedEntries})}return entries}function rankFuzzyFileEntries(query,entries){if(query.length===0)return[];let results=[];for(let entry of entries){let filenameMatch=scoreSubsequence(entry.fileName,query),pathMatch=filenameMatch??scoreSubsequence(entry.path,query);if(!pathMatch)continue;let filenameOffset=Array.from(entry.path).length-Array.from(entry.fileName).length,indices=filenameMatch?filenameMatch.indices.map(index=>index+filenameOffset):pathMatch.indices;results.push({root:entry.root,path:entry.path,match_type:entry.matchType,file_name:entry.fileName,score:pathMatch.score+(filenameMatch?FILENAME_MATCH_BONUS:0),indices})}return results.sort(compareResults),results.slice(0,FUZZY_SEARCH_RESULT_LIMIT)}async function walkDirectory(input){let matchers=await addGitignoreScope(input.matchers,input.absoluteDirectory),directoryEntries=await readableDirectory(input.absoluteDirectory);directoryEntries.sort((left,right)=>compareText(left.name,right.name));for(let directoryEntry of directoryEntries){if(input.signal?.aborted||input.state.stopped)return;if(input.state.visitedEntries>=input.maxVisitedEntries){input.state.stopped=!0;return}if(input.state.visitedEntries+=1,directoryEntry.name===".git"||directoryEntry.name==="node_modules"||directoryEntry.isSymbolicLink())continue;let isDirectory=directoryEntry.isDirectory();if(!isDirectory&&!directoryEntry.isFile())continue;let absolutePath=join2(input.absoluteDirectory,directoryEntry.name),relativePath=toPosixPath(relative2(input.absoluteRoot,absolutePath));if(isIgnored(matchers,absolutePath,isDirectory))continue;input.entries.push({root:input.wireRoot,path:relativePath,matchType:isDirectory?"directory":"file",fileName:basename(relativePath)});let entryDepth=input.depth+1;isDirectory&&entryDepth<input.maxDepth&&await walkDirectory({...input,absoluteDirectory:absolutePath,depth:entryDepth,matchers}),input.state.visitedEntries%CANCELLATION_YIELD_INTERVAL===0&&await yieldToEventLoop()}}function scoreSubsequence(candidate,query){let normalizedCandidate=normalizeText(candidate),normalizedQuery=query.toLowerCase(),contiguousStart=normalizedCandidate.value.indexOf(normalizedQuery),normalizedIndices=[];if(contiguousStart>=0){let cursor=contiguousStart;for(let character of normalizedQuery)normalizedIndices.push(cursor),cursor+=character.length}else{let cursor=0;for(let character of normalizedQuery){let matchIndex=normalizedCandidate.value.indexOf(character,cursor);if(matchIndex<0)return;normalizedIndices.push(matchIndex),cursor=matchIndex+character.length}}let indices=[];for(let normalizedIndex of normalizedIndices){let sourceIndex=normalizedCandidate.sourceIndices[normalizedIndex];if(sourceIndex===void 0)return;indices.at(-1)!==sourceIndex&&indices.push(sourceIndex)}let candidateCharacters=Array.from(candidate),score=indices.length*CHARACTER_MATCH_SCORE;for(let index=0;index<indices.length;index++){let current=indices[index];if(current===void 0)continue;let previous=indices[index-1];previous!==void 0&¤t===previous+1&&(score+=CONTIGUOUS_MATCH_BONUS),(current===0||isBoundary(candidateCharacters[current-1]))&&(score+=BOUNDARY_MATCH_BONUS)}return{score,indices}}function normalizeText(value){let sourceIndices=[];for(let[sourceIndex,character]of Array.from(value).entries())for(let index=0;index<character.toLowerCase().length;index++)sourceIndices.push(sourceIndex);return{value:value.toLowerCase(),sourceIndices}}function compareResults(left,right){return left.score!==right.score?right.score-left.score:compareText(left.path,right.path)}function compareText(left,right){return left<right?-1:left>right?1:0}function isBoundary(character){return character==="/"||character==="-"||character==="_"||character==="."||character===" "}async function readableDirectory(path){try{return await readdir(path,{withFileTypes:!0})}catch(error){if(error instanceof Error)return[];throw error}}async function readableStat(path){try{return await lstat(path)}catch(error){if(error instanceof Error)return;throw error}}function toPosixPath(path){return path.split(sep2).join("/")}function yieldToEventLoop(){return new Promise(resolve4=>setImmediate(resolve4))}var FuzzyFileSearchService=class{constructor(options){this.activeSearches=new Set,this.pendingSearches=new Map,this.sessions=new Map,this.disposed=!1,this.broadcast=options.broadcast,this.collectEntries=options.collectEntries??((roots,signal)=>collectFuzzyFileEntries(roots,{signal})),this.rankEntries=options.rankEntries??rankFuzzyFileEntries}async search(params){if(this.disposed)return{files:[]};let controller=new AbortController;this.activeSearches.add(controller);let token=params.cancellationToken;token!==null&&(this.pendingSearches.get(token)?.abort(),this.pendingSearches.set(token,controller));try{if(params.query.length===0)return{files:[]};let entries=await this.collectEntries(params.roots,controller.signal);return controller.signal.aborted||this.disposed?{files:[]}:{files:this.rankEntries(params.query,entries)}}finally{this.activeSearches.delete(controller),token!==null&&this.pendingSearches.get(token)===controller&&this.pendingSearches.delete(token)}}startSession(params){if(params.sessionId.length===0)throw invalidRequest2("sessionId must not be empty");this.cancelSession(params.sessionId);let session={id:params.sessionId,roots:params.roots,controller:new AbortController,query:"",version:0,entries:void 0,active:!this.disposed,completedVersion:void 0};return this.sessions.set(session.id,session),this.runTraversal(session),{}}updateSession(params){let session=this.sessions.get(params.sessionId);if(!session?.active)throw invalidRequest2(`fuzzy file search session not found: ${params.sessionId}`);return session.query=params.query,session.version+=1,session.entries!==void 0&&this.emitVersion(session,session.version),{}}stopSession(params){return this.cancelSession(params.sessionId),{}}dispose(){if(!this.disposed){this.disposed=!0;for(let controller of this.activeSearches)controller.abort();this.activeSearches.clear(),this.pendingSearches.clear();for(let session of this.sessions.values())this.deactivate(session);this.sessions.clear()}}async runTraversal(session){let entries;try{entries=await this.collectEntries(session.roots,session.controller.signal)}catch(error){if(!(error instanceof Error))throw error;entries=[]}if(this.isLive(session))for(session.entries=entries;this.isLive(session);){let version=session.version;if(await this.emitVersion(session,version))break}}async emitVersion(session,version){if(await Promise.resolve(),!this.isLive(session)||session.version!==version||session.entries===void 0)return!1;if(session.completedVersion===version)return!0;let query=session.query,files=query.length===0?[]:this.rankEntries(query,session.entries);return!this.isLive(session)||session.version!==version||(this.broadcast({method:"fuzzyFileSearch/sessionUpdated",params:{sessionId:session.id,query,files}}),!this.isLive(session)||session.version!==version)?!1:(session.completedVersion=version,this.broadcast({method:"fuzzyFileSearch/sessionCompleted",params:{sessionId:session.id}}),!0)}cancelSession(sessionId){let session=this.sessions.get(sessionId);session&&(this.deactivate(session),this.sessions.get(sessionId)===session&&this.sessions.delete(sessionId))}deactivate(session){session.active=!1,session.controller.abort()}isLive(session){return!this.disposed&&session.active&&this.sessions.get(session.id)===session}};function invalidRequest2(message){return new RpcHandlerError({code:-32600,message})}var APPROVAL_DECISIONS=["accept","acceptForSession","decline","cancel"],PERMISSION_OPTIONS=["Allow once","Allow always","Deny","Deny with feedback"],CANCEL_REASON="approval request was cancelled because the turn ended",NO_SUBSCRIBER_REASON="no client connected to approve";var ApprovalBridge=class{constructor(sendToThreadSubscribers){this.nextServerRequestId=0,this.pending=new Map,this.sessionAllows=new Set,this.sendToThreadSubscribers=sendToThreadSubscribers}get pendingCount(){return this.pending.size}requestApproval(threadId,kind,payload){let allowKey=createAllowKey(threadId,kind,payload);if(allowKey&&this.sessionAllows.has(allowKey))return Promise.resolve({allow:!0,decision:"acceptForSession"});let requestId=this.nextServerRequestId;this.nextServerRequestId+=1;let request=buildApprovalRequest(threadId,requestId,kind,payload);return new Promise(resolve4=>{this.pending.set(requestId,{threadId,request,allowKey,resolve:resolve4}),!(this.sendToThreadSubscribers(threadId,request)>0)&&(this.pending.delete(requestId),resolve4({allow:!1,decision:"decline",reason:NO_SUBSCRIBER_REASON}))})}resolveResponse(response){let pending=this.pending.get(response.id);if(!pending)return!1;let decision=readDecision(response);return this.pending.delete(response.id),decision==="acceptForSession"&&pending.allowKey&&this.sessionAllows.add(pending.allowKey),this.emitResolved(pending.threadId,response.id),pending.resolve(decisionToOutcome(decision,readReason(response))),!0}replayPendingForThread(threadId){let replayed=0;for(let pending of this.pending.values())pending.threadId===threadId&&(this.sendToThreadSubscribers(threadId,pending.request),replayed+=1);return replayed}cancelPendingForThread(threadId){let cancelled=0;for(let[requestId,pending]of Array.from(this.pending.entries()))pending.threadId===threadId&&(this.pending.delete(requestId),this.emitResolved(threadId,requestId),pending.resolve({allow:!1,decision:"cancel",reason:CANCEL_REASON}),cancelled+=1);return cancelled}emitResolved(threadId,requestId){this.sendToThreadSubscribers(threadId,{method:"serverRequest/resolved",params:{threadId,requestId}})}};function buildApprovalRequest(threadId,requestId,kind,payload){let base={threadId,turnId:payload.turnId??"turn-approval",itemId:payload.itemId??(kind==="commandExecution"?"approval-command":"approval-file-change"),startedAtMs:Date.now()};return kind==="fileChange"?{id:requestId,method:"item/fileChange/requestApproval",params:{...base,reason:payload.reason??null,grantRoot:payload.grantRoot??null}}:{id:requestId,method:"item/commandExecution/requestApproval",params:{...base,approvalId:payload.approvalId??null,environmentId:payload.environmentId??null,reason:payload.reason??null,command:payload.command??null,cwd:payload.cwd??null,availableDecisions:[...APPROVAL_DECISIONS]}}}function decisionToOutcome(decision,reason){return decision==="accept"||decision==="acceptForSession"?{allow:!0,decision}:reason?{allow:!1,decision,reason}:{allow:!1,decision}}function readDecision(response){let decision=isRecord2(response.result)?response.result.decision:void 0;return isApprovalDecision(decision)?decision:"cancel"}function readReason(response){let resultReason=isRecord2(response.result)?response.result.reason??response.result.message:void 0,errorReason=isRecord2(response.error)?response.error.message:void 0,reason=typeof resultReason=="string"?resultReason:errorReason;return typeof reason=="string"&&reason.length>0?reason:void 0}function createAllowKey(threadId,kind,payload){if(!(!payload.toolName||typeof payload.command!="string"))return`${threadId}\0${kind}\0${payload.toolName}\0${payload.command}`}function isApprovalDecision(value){return value==="accept"||value==="acceptForSession"||value==="decline"||value==="cancel"}function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function createAppServerUIContext(bridge,threadId,userInput,getTurnId=()=>"turn-user-input"){let pendingInput,editorText="",editorFactory,toolsExpanded=!1;return{question(request,opts){return userInput?.requestUserInput(threadId,getTurnId(),request.requestId,request,opts)??Promise.resolve({status:"unavailable",answers:{},unanswered:request.questions.map(question=>question.id)})},async select(title,options){let prompt=parsePermissionPrompt(title);if(!prompt||!isPermissionOptions(options))return;let outcome=await bridge.requestApproval(threadId,prompt.kind,{turnId:"turn-approval",itemId:`approval-${prompt.toolName}`,toolName:prompt.toolName,command:prompt.command,reason:prompt.reason});return outcome.decision==="accept"?"Allow once":outcome.decision==="acceptForSession"?"Allow always":outcome.reason&&outcome.reason!==NO_SUBSCRIBER_REASON&&outcome.reason!==CANCEL_REASON?(pendingInput=outcome.reason,"Deny with feedback"):"Deny"},async confirm(title,message){return(await bridge.requestApproval(threadId,"commandExecution",{turnId:"turn-approval",itemId:"approval-confirm",toolName:"confirm",command:title,reason:message})).allow},async input(){let value=pendingInput;return pendingInput=void 0,value},notify(){},onTerminalInput(){return()=>{}},setStatus(){},setWorkingMessage(){},setWorkingVisible(){},setWorkingIndicator(){},setHiddenThinkingLabel(){},setWidget(){},setFooter(){},setHeader(){},setTitle(){},custom(){return Promise.reject(new Error("Custom UI is not available in app-server mode."))},pasteToEditor(text){editorText=text},setEditorText(text){editorText=text},getEditorText(){return editorText},async editor(_title,prefill){return prefill},addAutocompleteProvider(){},setEditorComponent(factory){editorFactory=factory},getEditorComponent(){return editorFactory},theme,getAllThemes:getAvailableThemesWithPaths,getTheme(name){return getThemeByName(name)},setTheme(_nextTheme){return{success:!1,error:"Theme switching is not available in app-server mode."}},getToolsExpanded(){return toolsExpanded},setToolsExpanded(expanded){toolsExpanded=expanded}}}function parsePermissionPrompt(title){let[firstLine="",...rest]=title.split(`
|
|
2
|
+
import{import_websocket_server,wrapper_default}from"./chunk-YBZDAABT.js";import{AuthStorage,DefaultResourceLoader,GOAL_STORE_CHANGED_EVENT,ModelRegistry,clearGoal,createAgentSession,createGoal,createPendingQuestion,defaultModelPerProvider,formatProviderNativeBody,formatProviderNativeSummary,getMcpService,goalStoreRef,loadMcpConfig,loadSkills,readGoal,resolveAuthMode,updateGoal}from"./chunk-56COAYOU.js";import"./chunk-FAV4DX74.js";import{require_ignore}from"./chunk-VW3QZSX3.js";import"./chunk-YJUO37UX.js";import"./chunk-TFA3YAJK.js";import{SessionManager,loadEntriesFromFile}from"./chunk-F2YU5BZG.js";import"./chunk-NZRVAZL2.js";import"./chunk-U5KVIAHJ.js";import{getCredentialAccounts,getModels,getProviders,getSupportedThinkingLevels,pinCredentialAccount,removeCredentialAccount,subscribeProviderAccountEvents}from"./chunk-DB2X7SOZ.js";import{getAvailableThemesWithPaths,getThemeByName,takeOverStdout,theme,waitForRawStdoutBackpressure,writeRawStdout}from"./chunk-3HCYHGK2.js";import{x}from"./chunk-6BWCPOCV.js";import"./chunk-AIM2YESB.js";import{SettingsManager}from"./chunk-CXVI2YV7.js";import"./chunk-YU2P3CAR.js";import"./chunk-BMX2U22F.js";import"./chunk-JASECXGC.js";import{acquireOwnershipSafeLock}from"./chunk-LM2UMEX6.js";import"./chunk-CEMOA3TF.js";import{parseDaemonPidFile,processMatchesPidFile,readProcessStartTime,stopValidatedPid,waitForGone,waitForStartTime}from"./chunk-Y4CH7LYI.js";import{attachJsonlLineReader}from"./chunk-E5F5SVZF.js";import"./chunk-DA4W77J7.js";import"./chunk-H4QOEVS5.js";import"./chunk-HB35RBP4.js";import"./chunk-SQUUL72H.js";import"./chunk-JK5LVAOU.js";import"./chunk-ASAQQ6VM.js";import"./chunk-36ACNYQR.js";import"./chunk-7X72IACK.js";import"./chunk-KUPQO7MC.js";import"./chunk-B32PXL7U.js";import"./chunk-Y5A6TKBN.js";import"./chunk-L4OOEWAT.js";import"./chunk-26UBFF6N.js";import"./chunk-TOCWAEQB.js";import"./chunk-GZVCRV7I.js";import"./chunk-U3VOXCGP.js";import"./chunk-RFB5CBW7.js";import"./chunk-2VZJTDUX.js";import"./chunk-UDTWLXSQ.js";import"./chunk-4LVZQRDG.js";import"./chunk-ZOFZRJSL.js";import"./chunk-2SOVDETB.js";import{APP_NAME,CONFIG_DIR_NAME,ENV_SESSION_DIR,VERSION,getAgentDir,getSessionsDir,isLocalPath,resolvePath}from"./chunk-PBWQK23Z.js";import"./chunk-ATQFN5HI.js";import"./chunk-RBCQOIAD.js";import"./chunk-WASCMT6Y.js";import"./chunk-X4Q5YSTE.js";import"./chunk-IXAUDPOK.js";import"./chunk-J5TMX734.js";import"./chunk-LHYG7TK7.js";import"./chunk-KRNXUTZJ.js";import"./chunk-TJV2LFKM.js";import"./chunk-45SZOLCU.js";import"./chunk-WVGCQOLJ.js";import"./chunk-BBK6SSYT.js";import"./chunk-BPX6QWPD.js";import{normalizeProviderId}from"./chunk-7X4ZMME4.js";import"./chunk-WDTJ6JKE.js";import"./chunk-5ROMKDLK.js";import"./chunk-3W47LTXQ.js";import"./chunk-DMI55M2N.js";import"./chunk-BQVVQ2LQ.js";import{__toESM}from"./chunk-IKOFCODM.js";import{isIP}from"node:net";var APP_SERVER_LISTEN_USAGE="Invalid --listen value. Use stdio://, unix://, unix:///abs/path, or ws://IP:PORT.";function formatAppServerUsage(){let listenForms="stdio://|unix://|unix:///abs/path|ws://IP:PORT";return[`Usage: ${APP_NAME} app-server [--listen <${listenForms}>] [--ws-auth <token-file|off>] [--json-logs] [--extension <path>]...`,` ${APP_NAME} app-server daemon <start|stop|status|restart> [--listen <${listenForms}>] [--extension <path>]...`].join(`
|
|
3
|
+
`)}function parseListen(value){if(value==="stdio://")return{kind:"stdio",url:"stdio://"};if(value==="unix://")return{kind:"unix",url:"unix://"};if(value.startsWith("unix:///")){let path=value.slice(7);return path.startsWith("/")?{kind:"unix",url:value,path}:void 0}if(!value.startsWith("ws://"))return;let parsed;try{parsed=new URL(value)}catch(error){if(error instanceof TypeError)return;throw error}let port=Number(parsed.port);if(!(parsed.protocol!=="ws:"||parsed.username!==""||parsed.password!==""||parsed.pathname!=="/"||parsed.search!==""||parsed.hash!==""||parsed.port===""||!Number.isInteger(port)||port<1||port>65535||isIP(parsed.hostname)===0))return{kind:"ws",url:value,host:parsed.hostname,port}}function parseDaemonVerb(value){switch(value){case"start":case"stop":case"status":case"restart":return value;default:return}}function parseWsAuth(value){return value==="off"?{kind:"off"}:{kind:"token-file",path:value}}var EXTENSION_USAGE="--extension requires <path>.";function parseServerArgs(args){let listen3={kind:"stdio",url:"stdio://"},wsAuth,jsonLogs=!1,extensions=[];for(let index=0;index<args.length;index++){let arg=args[index];if(arg==="--extension"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:EXTENSION_USAGE};extensions.push(value),index++;continue}if(arg==="--listen"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};let parsed=parseListen(value);if(parsed===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};listen3=parsed,index++;continue}if(arg==="--ws-auth"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:"--ws-auth requires <token-file|off>."};wsAuth=parseWsAuth(value),index++;continue}if(arg==="--json-logs"){jsonLogs=!0;continue}return{kind:"usage-error",message:`Unexpected app-server argument: ${arg}`}}return{kind:"server",listen:listen3,wsAuth,jsonLogs,extensions}}function parseDaemonArgs(args){let verb=parseDaemonVerb(args[0]);if(verb===void 0)return{kind:"usage-error",message:"Usage: app-server daemon <start|stop|status|restart>."};let listen3={kind:"ws",url:"ws://127.0.0.1:18800",host:"127.0.0.1",port:18800},extensions=[];for(let index=1;index<args.length;index++){let arg=args[index];if(arg==="--extension"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:EXTENSION_USAGE};extensions.push(value),index++;continue}if(arg==="--listen"){let value=args[index+1];if(value===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};let parsed=parseListen(value);if(parsed===void 0)return{kind:"usage-error",message:APP_SERVER_LISTEN_USAGE};listen3=parsed,index++;continue}return{kind:"usage-error",message:`Unexpected app-server daemon argument: ${arg}`}}return{kind:"daemon",verb,listen:listen3,extensions}}function parseAppServerCliArgs(args){return args[0]==="daemon"?parseDaemonArgs(args.slice(1)):parseServerArgs(args)}function resolveAppServerExtensionPaths(extensions,cwd=process.cwd()){return extensions.map(value=>isLocalPath(value)?resolvePath(value,cwd):value)}var RpcHandlerError=class extends Error{constructor(error){super(error.message),this.name="RpcHandlerError",this.rpcError=error}};function invalidRequestError(){return{code:-32600,message:"Invalid request"}}function methodNotFoundError(method){return{code:-32601,message:`Method not found: ${method}`}}function invalidParamsError(){return{code:-32602,message:"Invalid params"}}function internalError(message="Internal error"){return{code:-32603,message}}function notInitializedError(){return{code:-32600,message:"Not initialized"}}function alreadyInitializedError(){return{code:-32600,message:"Already initialized"}}function experimentalCapabilityError(method){return{code:-32600,message:`${method} requires experimentalApi capability`}}function registerExtensionRequestMethod(registry,getThread){registry.register("extension_request",{scope:"thread",handler:async({request})=>{let params=request.params;if(typeof params!="object"||params===null||Array.isArray(params))throw new RpcHandlerError({code:-32602,message:"Invalid params"});let threadId=Reflect.get(params,"threadId"),name=Reflect.get(params,"name");if(typeof threadId!="string"||threadId.length===0||typeof name!="string"||name.length===0)throw new RpcHandlerError({code:-32602,message:"Invalid params"});try{return await getThread(threadId).extensionRunner.requestRpc(name,Reflect.get(params,"data"))}catch(error){throw new RpcHandlerError(internalError(error instanceof Error?error.message:String(error)))}}})}function createRegistry(){return new InMemoryMethodRegistry}var InMemoryMethodRegistry=class{constructor(){this.methods=new Map}register(method,registration){this.methods.set(method,registration)}async dispatch(connection,request){if(!connection.initialized){let registration2=this.methods.get(request.method);if(request.method!=="initialize"||registration2?.requiresInit!==!1)return{id:request.id,error:notInitializedError()}}if(connection.initialized&&request.method==="initialize")return{id:request.id,error:alreadyInitializedError()};let registration=this.methods.get(request.method);if(!registration)return{id:request.id,error:methodNotFoundError(request.method)};if(registration.experimental===!0&&connection.capabilities?.experimentalApi!==!0)return{id:request.id,error:experimentalCapabilityError(request.method)};try{let result=await registration.handler({connection,request});return{id:request.id,result}}catch(error){return error instanceof RpcHandlerError?{id:request.id,error:error.rpcError}:{id:request.id,error:internalError(error instanceof Error?error.message:String(error))}}}};function registerFuzzyFileSearchMethods(registry,service){registry.register("fuzzyFileSearch",{scope:"global",handler:({request})=>service.search(parseOneShotParams(request.params))}),registry.register("fuzzyFileSearch/sessionStart",{experimental:!0,scope:"global",handler:({request})=>service.startSession(parseSessionStartParams(request.params))}),registry.register("fuzzyFileSearch/sessionUpdate",{experimental:!0,scope:"global",handler:({request})=>service.updateSession(parseSessionUpdateParams(request.params))}),registry.register("fuzzyFileSearch/sessionStop",{experimental:!0,scope:"global",handler:({request})=>service.stopSession(parseSessionStopParams(request.params))})}function parseOneShotParams(value){let params=objectParams(value,"fuzzyFileSearch");return{query:requiredString(params.query,"fuzzyFileSearch query"),roots:requiredStringArray(params.roots,"fuzzyFileSearch roots"),cancellationToken:optionalString(params.cancellationToken,"fuzzyFileSearch cancellationToken")}}function parseSessionStartParams(value){let params=objectParams(value,"fuzzyFileSearch/sessionStart");return{sessionId:requiredString(params.sessionId,"fuzzyFileSearch/sessionStart sessionId"),roots:requiredStringArray(params.roots,"fuzzyFileSearch/sessionStart roots")}}function parseSessionUpdateParams(value){let params=objectParams(value,"fuzzyFileSearch/sessionUpdate");return{sessionId:requiredString(params.sessionId,"fuzzyFileSearch/sessionUpdate sessionId"),query:requiredString(params.query,"fuzzyFileSearch/sessionUpdate query")}}function parseSessionStopParams(value){let params=objectParams(value,"fuzzyFileSearch/sessionStop");return{sessionId:requiredString(params.sessionId,"fuzzyFileSearch/sessionStop sessionId")}}function objectParams(value,method){if(!isRecord(value))throw invalidRequest(`${method} params must be an object`);return value}function requiredString(value,field){if(typeof value!="string")throw invalidRequest(`${field} must be a string`);return value}function requiredStringArray(value,field){if(!Array.isArray(value)||value.some(entry=>typeof entry!="string"))throw invalidRequest(`${field} must be an array of strings`);return value}function optionalString(value,field){if(value==null)return null;if(typeof value!="string")throw invalidRequest(`${field} must be a string or null`);return value}function invalidRequest(message){return new RpcHandlerError({code:-32600,message})}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}import{lstat,readdir}from"node:fs/promises";import{basename,join as join2,relative as relative2,resolve,sep as sep2}from"node:path";var import_ignore=__toESM(require_ignore(),1);import{readFile}from"node:fs/promises";import{join,relative,sep}from"node:path";async function addGitignoreScope(matchers,directory){let gitignore=await readableText(join(directory,".gitignore"));return gitignore===void 0?matchers:[...matchers,{directory,matcher:(0,import_ignore.default)().add(gitignore)}]}function isIgnored(matchers,path,isDirectory){let ignored=!1;for(let scope of matchers){let relativePath=relative(scope.directory,path).split(sep).join("/"),result=scope.matcher.test(isDirectory?`${relativePath}/`:relativePath);result.ignored&&(ignored=!0),result.unignored&&(ignored=!1)}return ignored}async function readableText(path){try{return await readFile(path,"utf8")}catch(error){if(error instanceof Error)return;throw error}}var FUZZY_SEARCH_MAX_DEPTH=12,FUZZY_SEARCH_MAX_VISITED_ENTRIES=5e4,FUZZY_SEARCH_RESULT_LIMIT=50,FILENAME_MATCH_BONUS=1e6,CHARACTER_MATCH_SCORE=10,CONTIGUOUS_MATCH_BONUS=20,BOUNDARY_MATCH_BONUS=5,CANCELLATION_YIELD_INTERVAL=256;async function collectFuzzyFileEntries(roots,options={}){let entries=[],state={visitedEntries:0,stopped:!1},maxDepth=options.maxDepth??FUZZY_SEARCH_MAX_DEPTH,maxVisitedEntries=options.maxVisitedEntries??FUZZY_SEARCH_MAX_VISITED_ENTRIES;for(let root of roots){if(options.signal?.aborted||state.stopped)break;let absoluteRoot=resolve(root),rootStat=await readableStat(absoluteRoot);!rootStat?.isDirectory()||rootStat.isSymbolicLink()||await walkDirectory({wireRoot:root,absoluteRoot,absoluteDirectory:absoluteRoot,depth:0,matchers:[],entries,state,signal:options.signal,maxDepth,maxVisitedEntries})}return entries}function rankFuzzyFileEntries(query,entries){if(query.length===0)return[];let results=[];for(let entry of entries){let filenameMatch=scoreSubsequence(entry.fileName,query),pathMatch=filenameMatch??scoreSubsequence(entry.path,query);if(!pathMatch)continue;let filenameOffset=Array.from(entry.path).length-Array.from(entry.fileName).length,indices=filenameMatch?filenameMatch.indices.map(index=>index+filenameOffset):pathMatch.indices;results.push({root:entry.root,path:entry.path,match_type:entry.matchType,file_name:entry.fileName,score:pathMatch.score+(filenameMatch?FILENAME_MATCH_BONUS:0),indices})}return results.sort(compareResults),results.slice(0,FUZZY_SEARCH_RESULT_LIMIT)}async function walkDirectory(input){let matchers=await addGitignoreScope(input.matchers,input.absoluteDirectory),directoryEntries=await readableDirectory(input.absoluteDirectory);directoryEntries.sort((left,right)=>compareText(left.name,right.name));for(let directoryEntry of directoryEntries){if(input.signal?.aborted||input.state.stopped)return;if(input.state.visitedEntries>=input.maxVisitedEntries){input.state.stopped=!0;return}if(input.state.visitedEntries+=1,directoryEntry.name===".git"||directoryEntry.name==="node_modules"||directoryEntry.isSymbolicLink())continue;let isDirectory=directoryEntry.isDirectory();if(!isDirectory&&!directoryEntry.isFile())continue;let absolutePath=join2(input.absoluteDirectory,directoryEntry.name),relativePath=toPosixPath(relative2(input.absoluteRoot,absolutePath));if(isIgnored(matchers,absolutePath,isDirectory))continue;input.entries.push({root:input.wireRoot,path:relativePath,matchType:isDirectory?"directory":"file",fileName:basename(relativePath)});let entryDepth=input.depth+1;isDirectory&&entryDepth<input.maxDepth&&await walkDirectory({...input,absoluteDirectory:absolutePath,depth:entryDepth,matchers}),input.state.visitedEntries%CANCELLATION_YIELD_INTERVAL===0&&await yieldToEventLoop()}}function scoreSubsequence(candidate,query){let normalizedCandidate=normalizeText(candidate),normalizedQuery=query.toLowerCase(),contiguousStart=normalizedCandidate.value.indexOf(normalizedQuery),normalizedIndices=[];if(contiguousStart>=0){let cursor=contiguousStart;for(let character of normalizedQuery)normalizedIndices.push(cursor),cursor+=character.length}else{let cursor=0;for(let character of normalizedQuery){let matchIndex=normalizedCandidate.value.indexOf(character,cursor);if(matchIndex<0)return;normalizedIndices.push(matchIndex),cursor=matchIndex+character.length}}let indices=[];for(let normalizedIndex of normalizedIndices){let sourceIndex=normalizedCandidate.sourceIndices[normalizedIndex];if(sourceIndex===void 0)return;indices.at(-1)!==sourceIndex&&indices.push(sourceIndex)}let candidateCharacters=Array.from(candidate),score=indices.length*CHARACTER_MATCH_SCORE;for(let index=0;index<indices.length;index++){let current=indices[index];if(current===void 0)continue;let previous=indices[index-1];previous!==void 0&¤t===previous+1&&(score+=CONTIGUOUS_MATCH_BONUS),(current===0||isBoundary(candidateCharacters[current-1]))&&(score+=BOUNDARY_MATCH_BONUS)}return{score,indices}}function normalizeText(value){let sourceIndices=[];for(let[sourceIndex,character]of Array.from(value).entries())for(let index=0;index<character.toLowerCase().length;index++)sourceIndices.push(sourceIndex);return{value:value.toLowerCase(),sourceIndices}}function compareResults(left,right){return left.score!==right.score?right.score-left.score:compareText(left.path,right.path)}function compareText(left,right){return left<right?-1:left>right?1:0}function isBoundary(character){return character==="/"||character==="-"||character==="_"||character==="."||character===" "}async function readableDirectory(path){try{return await readdir(path,{withFileTypes:!0})}catch(error){if(error instanceof Error)return[];throw error}}async function readableStat(path){try{return await lstat(path)}catch(error){if(error instanceof Error)return;throw error}}function toPosixPath(path){return path.split(sep2).join("/")}function yieldToEventLoop(){return new Promise(resolve4=>setImmediate(resolve4))}var FuzzyFileSearchService=class{constructor(options){this.activeSearches=new Set,this.pendingSearches=new Map,this.sessions=new Map,this.disposed=!1,this.broadcast=options.broadcast,this.collectEntries=options.collectEntries??((roots,signal)=>collectFuzzyFileEntries(roots,{signal})),this.rankEntries=options.rankEntries??rankFuzzyFileEntries}async search(params){if(this.disposed)return{files:[]};let controller=new AbortController;this.activeSearches.add(controller);let token=params.cancellationToken;token!==null&&(this.pendingSearches.get(token)?.abort(),this.pendingSearches.set(token,controller));try{if(params.query.length===0)return{files:[]};let entries=await this.collectEntries(params.roots,controller.signal);return controller.signal.aborted||this.disposed?{files:[]}:{files:this.rankEntries(params.query,entries)}}finally{this.activeSearches.delete(controller),token!==null&&this.pendingSearches.get(token)===controller&&this.pendingSearches.delete(token)}}startSession(params){if(params.sessionId.length===0)throw invalidRequest2("sessionId must not be empty");this.cancelSession(params.sessionId);let session={id:params.sessionId,roots:params.roots,controller:new AbortController,query:"",version:0,entries:void 0,active:!this.disposed,completedVersion:void 0};return this.sessions.set(session.id,session),this.runTraversal(session),{}}updateSession(params){let session=this.sessions.get(params.sessionId);if(!session?.active)throw invalidRequest2(`fuzzy file search session not found: ${params.sessionId}`);return session.query=params.query,session.version+=1,session.entries!==void 0&&this.emitVersion(session,session.version),{}}stopSession(params){return this.cancelSession(params.sessionId),{}}dispose(){if(!this.disposed){this.disposed=!0;for(let controller of this.activeSearches)controller.abort();this.activeSearches.clear(),this.pendingSearches.clear();for(let session of this.sessions.values())this.deactivate(session);this.sessions.clear()}}async runTraversal(session){let entries;try{entries=await this.collectEntries(session.roots,session.controller.signal)}catch(error){if(!(error instanceof Error))throw error;entries=[]}if(this.isLive(session))for(session.entries=entries;this.isLive(session);){let version=session.version;if(await this.emitVersion(session,version))break}}async emitVersion(session,version){if(await Promise.resolve(),!this.isLive(session)||session.version!==version||session.entries===void 0)return!1;if(session.completedVersion===version)return!0;let query=session.query,files=query.length===0?[]:this.rankEntries(query,session.entries);return!this.isLive(session)||session.version!==version||(this.broadcast({method:"fuzzyFileSearch/sessionUpdated",params:{sessionId:session.id,query,files}}),!this.isLive(session)||session.version!==version)?!1:(session.completedVersion=version,this.broadcast({method:"fuzzyFileSearch/sessionCompleted",params:{sessionId:session.id}}),!0)}cancelSession(sessionId){let session=this.sessions.get(sessionId);session&&(this.deactivate(session),this.sessions.get(sessionId)===session&&this.sessions.delete(sessionId))}deactivate(session){session.active=!1,session.controller.abort()}isLive(session){return!this.disposed&&session.active&&this.sessions.get(session.id)===session}};function invalidRequest2(message){return new RpcHandlerError({code:-32600,message})}var APPROVAL_DECISIONS=["accept","acceptForSession","decline","cancel"],PERMISSION_OPTIONS=["Allow once","Allow always","Deny","Deny with feedback"],CANCEL_REASON="approval request was cancelled because the turn ended",NO_SUBSCRIBER_REASON="no client connected to approve";var ApprovalBridge=class{constructor(sendToThreadSubscribers){this.nextServerRequestId=0,this.pending=new Map,this.sessionAllows=new Set,this.sendToThreadSubscribers=sendToThreadSubscribers}get pendingCount(){return this.pending.size}requestApproval(threadId,kind,payload){let allowKey=createAllowKey(threadId,kind,payload);if(allowKey&&this.sessionAllows.has(allowKey))return Promise.resolve({allow:!0,decision:"acceptForSession"});let requestId=this.nextServerRequestId;this.nextServerRequestId+=1;let request=buildApprovalRequest(threadId,requestId,kind,payload);return new Promise(resolve4=>{this.pending.set(requestId,{threadId,request,allowKey,resolve:resolve4}),!(this.sendToThreadSubscribers(threadId,request)>0)&&(this.pending.delete(requestId),resolve4({allow:!1,decision:"decline",reason:NO_SUBSCRIBER_REASON}))})}resolveResponse(response){let pending=this.pending.get(response.id);if(!pending)return!1;let decision=readDecision(response);return this.pending.delete(response.id),decision==="acceptForSession"&&pending.allowKey&&this.sessionAllows.add(pending.allowKey),this.emitResolved(pending.threadId,response.id),pending.resolve(decisionToOutcome(decision,readReason(response))),!0}replayPendingForThread(threadId){let replayed=0;for(let pending of this.pending.values())pending.threadId===threadId&&(this.sendToThreadSubscribers(threadId,pending.request),replayed+=1);return replayed}cancelPendingForThread(threadId){let cancelled=0;for(let[requestId,pending]of Array.from(this.pending.entries()))pending.threadId===threadId&&(this.pending.delete(requestId),this.emitResolved(threadId,requestId),pending.resolve({allow:!1,decision:"cancel",reason:CANCEL_REASON}),cancelled+=1);return cancelled}emitResolved(threadId,requestId){this.sendToThreadSubscribers(threadId,{method:"serverRequest/resolved",params:{threadId,requestId}})}};function buildApprovalRequest(threadId,requestId,kind,payload){let base={threadId,turnId:payload.turnId??"turn-approval",itemId:payload.itemId??(kind==="commandExecution"?"approval-command":"approval-file-change"),startedAtMs:Date.now()};return kind==="fileChange"?{id:requestId,method:"item/fileChange/requestApproval",params:{...base,reason:payload.reason??null,grantRoot:payload.grantRoot??null}}:{id:requestId,method:"item/commandExecution/requestApproval",params:{...base,approvalId:payload.approvalId??null,environmentId:payload.environmentId??null,reason:payload.reason??null,command:payload.command??null,cwd:payload.cwd??null,availableDecisions:[...APPROVAL_DECISIONS]}}}function decisionToOutcome(decision,reason){return decision==="accept"||decision==="acceptForSession"?{allow:!0,decision}:reason?{allow:!1,decision,reason}:{allow:!1,decision}}function readDecision(response){let decision=isRecord2(response.result)?response.result.decision:void 0;return isApprovalDecision(decision)?decision:"cancel"}function readReason(response){let resultReason=isRecord2(response.result)?response.result.reason??response.result.message:void 0,errorReason=isRecord2(response.error)?response.error.message:void 0,reason=typeof resultReason=="string"?resultReason:errorReason;return typeof reason=="string"&&reason.length>0?reason:void 0}function createAllowKey(threadId,kind,payload){if(!(!payload.toolName||typeof payload.command!="string"))return`${threadId}\0${kind}\0${payload.toolName}\0${payload.command}`}function isApprovalDecision(value){return value==="accept"||value==="acceptForSession"||value==="decline"||value==="cancel"}function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function createAppServerUIContext(bridge,threadId,userInput,getTurnId=()=>"turn-user-input"){let pendingInput,editorText="",editorFactory,toolsExpanded=!1;return{question(request,opts){return userInput?.requestUserInput(threadId,getTurnId(),request.requestId,request,opts)??Promise.resolve({status:"unavailable",answers:{},unanswered:request.questions.map(question=>question.id)})},async select(title,options){let prompt=parsePermissionPrompt(title);if(!prompt||!isPermissionOptions(options))return;let outcome=await bridge.requestApproval(threadId,prompt.kind,{turnId:"turn-approval",itemId:`approval-${prompt.toolName}`,toolName:prompt.toolName,command:prompt.command,reason:prompt.reason});return outcome.decision==="accept"?"Allow once":outcome.decision==="acceptForSession"?"Allow always":outcome.reason&&outcome.reason!==NO_SUBSCRIBER_REASON&&outcome.reason!==CANCEL_REASON?(pendingInput=outcome.reason,"Deny with feedback"):"Deny"},async confirm(title,message){return(await bridge.requestApproval(threadId,"commandExecution",{turnId:"turn-approval",itemId:"approval-confirm",toolName:"confirm",command:title,reason:message})).allow},async input(){let value=pendingInput;return pendingInput=void 0,value},notify(){},onTerminalInput(){return()=>{}},setStatus(){},setWorkingMessage(){},setWorkingVisible(){},setWorkingIndicator(){},setHiddenThinkingLabel(){},setWidget(){},setFooter(){},setHeader(){},setTitle(){},custom(){return Promise.reject(new Error("Custom UI is not available in app-server mode."))},pasteToEditor(text){editorText=text},setEditorText(text){editorText=text},getEditorText(){return editorText},async editor(_title,prefill){return prefill},addAutocompleteProvider(){},setEditorComponent(factory){editorFactory=factory},getEditorComponent(){return editorFactory},theme,getAllThemes:getAvailableThemesWithPaths,getTheme(name){return getThemeByName(name)},setTheme(_nextTheme){return{success:!1,error:"Theme switching is not available in app-server mode."}},getToolsExpanded(){return toolsExpanded},setToolsExpanded(expanded){toolsExpanded=expanded}}}function parsePermissionPrompt(title){let[firstLine="",...rest]=title.split(`
|
|
4
4
|
`),prefix="Permission required: ";if(!firstLine.startsWith(prefix))return;let toolName=firstLine.slice(prefix.length).trim(),reason=rest.join(`
|
|
5
5
|
`).trim();return{kind:permissionKind(toolName),toolName,command:readLineValue(reason,"Command: $ ")??readLineValue(reason,"File: ")??readLineValue(reason,"Path: ")??null,reason}}function permissionKind(toolName){return toolName==="edit"||toolName==="write"||toolName==="apply_patch"||toolName==="multiedit"?"fileChange":"commandExecution"}function readLineValue(text,prefix){return text.split(`
|
|
6
6
|
`).find(line=>line.startsWith(prefix))?.slice(prefix.length)}function isPermissionOptions(options){return PERMISSION_OPTIONS.every(option=>options.includes(option))}var SERVER_NOTIFICATION_METHODS=["account/login/completed","account/rateLimits/updated","account/updated","app/list/updated","command/exec/outputDelta","configWarning","deprecationNotice","error","externalAgentConfig/import/completed","externalAgentConfig/import/progress","fs/changed","fuzzyFileSearch/sessionCompleted","fuzzyFileSearch/sessionUpdated","guardianWarning","hook/completed","hook/started","item/agentMessage/delta","item/autoApprovalReview/completed","item/autoApprovalReview/started","item/commandExecution/outputDelta","item/commandExecution/terminalInteraction","item/completed","item/fileChange/outputDelta","item/fileChange/patchUpdated","item/mcpToolCall/progress","item/plan/delta","item/reasoning/summaryPartAdded","item/reasoning/summaryTextDelta","item/reasoning/textDelta","item/started","mcpServer/oauthLogin/completed","mcpServer/startupStatus/updated","model/rerouted","model/safetyBuffering/updated","model/verification","process/exited","process/outputDelta","rawResponse/completed","rawResponseItem/completed","remoteControl/status/changed","serverRequest/resolved","skills/changed","thread/archived","thread/closed","thread/compacted","thread/deleted","thread/environment/connected","thread/environment/disconnected","thread/goal/cleared","thread/goal/updated","thread/name/updated","thread/realtime/closed","thread/realtime/error","thread/realtime/itemAdded","thread/realtime/outputAudio/delta","thread/realtime/sdp","thread/realtime/started","thread/realtime/transcript/delta","thread/realtime/transcript/done","thread/settings/updated","thread/started","thread/status/changed","thread/tokenUsage/updated","thread/unarchived","turn/completed","turn/diff/updated","turn/moderationMetadata","turn/plan/updated","turn/started","warning","windows/worldWritableWarning","windowsSandbox/setupCompleted"],ADDITIVE_CLIENT_NOTIFICATION_METHODS=["item/tool/userInputProgress"],ADDITIVE_SERVER_NOTIFICATION_METHODS=["account/providerAccounts/failover","account/providerAccounts/updated"],EXPERIMENTAL_SERVER_NOTIFICATION_METHODS=["process/exited","process/outputDelta","thread/environment/connected","thread/environment/disconnected","thread/realtime/closed","thread/realtime/error","thread/realtime/itemAdded","thread/realtime/outputAudio/delta","thread/realtime/sdp","thread/realtime/started","thread/realtime/transcript/delta","thread/realtime/transcript/done","thread/settings/updated","turn/moderationMetadata"];function populateNotificationEnvelope(notification,emittedAtMs){return{...notification,emittedAtMs:notification.emittedAtMs??emittedAtMs??Date.now()}}function populateOutboundNotificationEnvelope(message,emittedAtMs){return isRpcNotification(message)?populateNotificationEnvelope(message,emittedAtMs):message}function isJsonObject(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isRpcNotification(message){return"method"in message&&!("id"in message)}function hasOwn(value,key){return Reflect.getOwnPropertyDescriptor(value,key)!==void 0}function isRequestId(value){return typeof value=="string"||typeof value=="number"}function isResponseId(value){return value===null||isRequestId(value)}function parseRpcError(value){if(!isJsonObject(value))return null;let code=value.code,message=value.message;return typeof code!="number"||typeof message!="string"?null:hasOwn(value,"data")?{code,message,data:value.data}:{code,message}}function requestFrom(value,id,method){return hasOwn(value,"params")?{id,method,params:value.params}:{id,method}}function notificationFrom(value,method){return hasOwn(value,"params")?{method,params:value.params}:{method}}function classifyIncoming(value){if(!isJsonObject(value))return{kind:"protocol-invalid",value};let id=value.id,method=value.method;if(hasOwn(value,"id")&&hasOwn(value,"method")&&isRequestId(id)&&typeof method=="string")return{kind:"request",message:requestFrom(value,id,method)};if(hasOwn(value,"id")&&isResponseId(id)&&hasOwn(value,"result"))return{kind:"response",message:{id,result:value.result}};if(hasOwn(value,"id")&&isResponseId(id)&&hasOwn(value,"error")){let error=parseRpcError(value.error);if(error!==null)return{kind:"response",message:{id,error}}}return!hasOwn(value,"id")&&hasOwn(value,"method")&&typeof method=="string"?{kind:"notification",message:notificationFrom(value,method)}:{kind:"protocol-invalid",value}}var DEFAULT_OUTBOUND_QUEUE_LIMIT=32768,DEFAULT_TERMINAL_QUEUE_LIMIT=100,BROADCAST_NOTIFICATION_METHODS=new Set(["remoteControl/status/changed","thread/started","thread/status/changed","thread/closed","thread/deleted","thread/archived","thread/unarchived","thread/name/updated","thread/goal/updated","thread/goal/cleared","thread/tokenUsage/updated","fuzzyFileSearch/sessionUpdated","fuzzyFileSearch/sessionCompleted"]),TERMINAL_NOTIFICATION_METHODS=new Set(["turn/completed","error"]),BroadcastNotificationMethodError=class extends Error{constructor(method){super(`Notification method ${method} is not allowed for broadcast`),this.name="BroadcastNotificationMethodError",this.method=method}},NotificationRouter=class{constructor(options={}){this.connections=new Map,this.threads=new Map,this.outboundQueueLimit=options.outboundQueueLimit??DEFAULT_OUTBOUND_QUEUE_LIMIT,this.terminalQueueLimit=options.terminalQueueLimit??DEFAULT_TERMINAL_QUEUE_LIMIT,this.experimentalNotificationMethods=new Set(options.experimentalNotificationMethods??EXPERIMENTAL_SERVER_NOTIFICATION_METHODS),this.now=options.now??Date.now;for(let connection of options.connections??[])this.addConnection(connection);for(let thread of options.threads??[])this.addThread(thread)}addConnection(connection){this.connections.set(connection.id,{connection,pending:0,closed:!1})}removeConnection(connectionId2){this.connections.delete(connectionId2);let emptiedThreadIds=[];for(let thread of this.threads.values())thread.subscribers.delete(connectionId2)&&thread.subscribers.size===0&&emptiedThreadIds.push(thread.id);return emptiedThreadIds}addThread(thread){this.threads.set(thread.id,thread)}removeThread(threadId){this.threads.delete(threadId)}subscribe(threadId,connectionId2){let thread=this.threads.get(threadId);if(!thread)return;thread.subscribers.add(connectionId2);let connection=this.connections.get(connectionId2);if(connection){for(let notification of thread.queuedTerminalNotifications)this.enqueue(connection,notification);thread.queuedTerminalNotifications=[]}}unsubscribe(threadId,connectionId2){this.threads.get(threadId)?.subscribers.delete(connectionId2)}broadcast(notification){if(!this.canBroadcast(notification.method))throw new BroadcastNotificationMethodError(notification.method);let envelope=populateNotificationEnvelope(notification,this.now());for(let state of this.connections.values())state.connection.initialized&&this.enqueue(state,envelope)}toThread(threadId,message){let thread=this.threads.get(threadId);if(!thread)return;let outbound="id"in message?message:populateNotificationEnvelope(message,this.now()),routed=0;for(let connectionId2 of thread.subscribers){let state=this.connections.get(connectionId2);state?.connection.initialized&&(routed+=1,this.enqueue(state,outbound))}if(routed===0&&!("id"in outbound)&&TERMINAL_NOTIFICATION_METHODS.has(outbound.method))for(thread.queuedTerminalNotifications.push(outbound);thread.queuedTerminalNotifications.length>this.terminalQueueLimit;)thread.queuedTerminalNotifications.shift()}enqueue(state,message){if(state.closed||!("id"in message)&&this.filtered(state.connection,message))return;if(state.connection.transport!=="stdio"&&state.pending>=this.outboundQueueLimit){state.closed=!0,state.connection.close?.("slow-client");return}state.pending+=1;let sendResult=state.connection.send(message);if(sendResult===void 0){state.pending-=1;return}sendResult.then(()=>{state.pending-=1},()=>{state.pending-=1})}canBroadcast(method){return BROADCAST_NOTIFICATION_METHODS.has(method)}filtered(connection,notification){return connection.optOutNotificationMethods?.includes(notification.method)?!0:this.experimentalNotificationMethods.has(notification.method)&&connection.capabilities?.experimentalApi!==!0}};import{existsSync,statSync}from"node:fs";import{resolve as resolve2}from"node:path";function registerAppServerSkillMethods(registry,options={}){let serverCwd=resolvePath(options.serverCwd??process.cwd()),agentDir=options.agentDir??getAgentDir(),cachedLoaders=new Map;registry.register("skills/list",{scope:"global",handler:async({request})=>{let params=parseSkillsListParams(request.params),requestedCwds=params.cwds&¶ms.cwds.length>0?params.cwds:[serverCwd];return{data:await Promise.all(requestedCwds.map(cwd=>{let resolvedCwd=resolvePath(cwd,serverCwd,{trim:!0});return buildSkillsListEntry(resolvedCwd,params.forceReload===!0,{agentDir,threads:options.threads,resourceLoaderFactory:options.resourceLoaderFactory,cachedLoaders})}))}}})}async function buildSkillsListEntry(cwd,forceReload,options){let invalidEntry=validateSkillCwd(cwd);if(invalidEntry)return invalidEntry;try{let loader=await getCachedLoader(cwd,options);forceReload&&await loader.reload();let loaded=loader.getSkills();return{cwd,skills:loaded.skills.map(toWireSkill),errors:loaded.diagnostics.map(diagnostic=>toWireSkillError(diagnostic,cwd))}}catch(error){return{cwd,skills:[],errors:[{path:cwd,message:errorMessage(error)}]}}}function validateSkillCwd(cwd){if(!existsSync(cwd))return{cwd,skills:[],errors:[{path:cwd,message:"skill cwd does not exist"}]};try{if(!statSync(cwd).isDirectory())return{cwd,skills:[],errors:[{path:cwd,message:"skill cwd is not a directory"}]}}catch(error){return{cwd,skills:[],errors:[{path:cwd,message:errorMessage(error)}]}}}async function getCachedLoader(cwd,options){let loaderPromise=options.cachedLoaders.get(cwd);loaderPromise||(loaderPromise=Promise.resolve().then(()=>findLoadedLoader(cwd,options.threads)??createLoader(cwd,options)),options.cachedLoaders.set(cwd,loaderPromise));try{return await loaderPromise}catch(error){throw options.cachedLoaders.delete(cwd),error}}function findLoadedLoader(cwd,threads){if(!threads)return;let loaded=threads.listLoaded().find(thread=>resolve2(thread.cwd)===cwd);if(loaded)try{return threads.getLoadedThread(loaded.id).session.resourceLoader}catch{return}}async function createLoader(cwd,options){if(options.resourceLoaderFactory)return options.resourceLoaderFactory(cwd);let loaded=loadSkills({cwd,agentDir:options.agentDir,skillPaths:[],includeDefaults:!0});return{getSkills:()=>loaded,reload:async()=>{loaded=loadSkills({cwd,agentDir:options.agentDir,skillPaths:[],includeDefaults:!0})}}}function toWireSkill(skill){return{name:skill.name,description:skill.description,path:skill.filePath,scope:mapSkillScope(skill.sourceInfo.scope),enabled:!skill.disableModelInvocation}}function mapSkillScope(scope){switch(scope){case"user":return"user";case"project":return"repo";case"temporary":case"system":return"system";default:return assertNever(scope)}}function toWireSkillError(diagnostic,cwd){return{path:diagnostic.path??cwd,message:diagnostic.message}}function parseSkillsListParams(value){if(value==null)return{};if(!isRecord3(value))throw new RpcHandlerError(invalidParamsError());let cwds=value.cwds;if(cwds!==void 0&&!isStringArray(cwds))throw new RpcHandlerError(invalidParamsError());let forceReload=value.forceReload;if(forceReload!==void 0&&typeof forceReload!="boolean")throw new RpcHandlerError(invalidParamsError());return{cwds,forceReload}}function isStringArray(value){return Array.isArray(value)&&value.every(item=>typeof item=="string")}function isRecord3(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function errorMessage(error){return error instanceof Error?error.message:String(error)}function assertNever(value){throw new Error(`Unhandled skill scope: ${String(value)}`)}var UserInputProtocolError=class extends Error{constructor(message){super(message),this.name="UserInputProtocolError"}};function isRecord4(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function readUserInputResult(value){if(!isRecord4(value))throw new UserInputProtocolError("Invalid user input result");if(value.comment!==void 0&&typeof value.comment!="string")throw new UserInputProtocolError("Invalid user input comment");if(value.cancelled!==void 0&&typeof value.cancelled!="boolean")throw new UserInputProtocolError("Invalid user input cancellation");let answers={};if(value.answers!==void 0){if(!isRecord4(value.answers))throw new UserInputProtocolError("Invalid user input answers");for(let[id,answer]of Object.entries(value.answers)){if(!isRecord4(answer)||!Array.isArray(answer.answers))throw new UserInputProtocolError("Invalid user input answer");let strings=[];for(let text of answer.answers){if(typeof text!="string")throw new UserInputProtocolError("Invalid user input answer text");strings.push(text)}answers[id]={answers:strings}}}return{answers,comment:value.comment,cancelled:value.cancelled}}function toDraft(request,result){let answers={};for(let question of request.questions){let values=result.answers[question.id]?.answers;if(!values)continue;let labels=new Set(question.options.map(option=>option.label)),selected=values.filter(value=>labels.has(value)),text=values.filter(value=>!labels.has(value)).join(`
|
|
@@ -14,7 +14,7 @@ import{import_websocket_server,wrapper_default}from"./chunk-YBZDAABT.js";import{
|
|
|
14
14
|
`}function readOperation(value){switch(value){case"add":case"delete":case"update":return value;default:return}}function patchChangeKind(operation,movePath){switch(operation){case"add":return{type:"add"};case"delete":return{type:"delete"};case"update":return{type:"update",move_path:movePath};default:return operation}}function readString(value){return typeof value=="string"&&value.length>0?value:void 0}function isRecord9(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function webSearchItem(id,content){let action=readAction(content);return{type:"webSearch",id,query:actionDetail(action),action,results:readResults(content)}}function readResults(content){return!isRecord10(content.raw)||!Array.isArray(content.raw.results)?null:content.raw.results}function readAction(content){if(!isRecord10(content.raw))return{type:"other"};let rawAction=content.raw.action;if(!isRecord10(rawAction))return{type:"other"};let type2=readString2(rawAction.type);switch(type2){case"search":return{type:type2,query:readNullableString(rawAction.query),queries:readNullableStrings(rawAction.queries)};case"open_page":case"openPage":return{type:"openPage",url:readNullableString(rawAction.url)};case"find_in_page":case"findInPage":return{type:"findInPage",url:readNullableString(rawAction.url),pattern:readNullableString(rawAction.pattern)};default:return{type:"other"}}}function actionDetail(action){switch(action.type){case"search":{if(action.query)return action.query;let first=action.queries?.[0]??"";return action.queries&&action.queries.length>1&&first?`${first} ...`:first}case"openPage":return action.url??"";case"findInPage":return action.pattern&&action.url?`'${action.pattern}' in ${action.url}`:action.pattern?`'${action.pattern}'`:action.url??"";case"other":return"";default:return assertNeverAction(action)}}function readNullableString(value){return typeof value=="string"?value:null}function readNullableStrings(value){return Array.isArray(value)&&value.every(entry=>typeof entry=="string")?value:null}function readString2(value){return typeof value=="string"?value:void 0}function isRecord10(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function assertNeverAction(value){throw new Error(`Unhandled web search action: ${String(value)}`)}var MAX_TOOL_OUTPUT_BYTES=256*1024,MCP_TOOL_NAME_PATTERN=/^[^_]+__/;function classifyTool(name){return name==="bash"?"commandExecution":name==="edit"||name==="write"||name==="apply_patch"?"fileChange":MCP_TOOL_NAME_PATTERN.test(name)?"mcpToolCall":"dynamicToolCall"}function remainingCommandOutputBytes(output){return MAX_TOOL_OUTPUT_BYTES-byteLength(output)}function capCommandOutput(value,maxBytes=MAX_TOOL_OUTPUT_BYTES){return capUtf8(value,maxBytes)}function extractToolText(result){if(!isRecord11(result))return"";let content=result.content;return Array.isArray(content)?content.map(item=>isRecord11(item)&&item.type==="text"?readString3(item,"text")??"":"").join(""):readString3(result,"text")??""}function commandExecutionItem(tool,status,cwd,result){return{type:"commandExecution",id:tool.id,command:commandFromArgs(tool.args),cwd,processId:null,source:"agent",status,commandActions:[],aggregatedOutput:tool.output||null,exitCode:status==="inProgress"?null:exitCodeFromResult(result),durationMs:null}}function mcpToolCallItem(tool,status,result){let[server,toolName]=splitMcpName(tool.name);return{type:"mcpToolCall",id:tool.id,server,tool:toolName,status,arguments:toJsonValue2(tool.args),appContext:null,pluginId:null,result:status==="completed"?{content:toolResultContent(result),structuredContent:null,_meta:null}:null,error:status==="failed"?{message:extractToolText(result)||"Tool execution failed"}:null,durationMs:null}}function dynamicToolCallItem(tool,status,result,isError){return{type:"dynamicToolCall",id:tool.id,namespace:null,tool:tool.name,arguments:toJsonValue2(tool.args),status,contentItems:status==="inProgress"?null:[{type:"inputText",text:extractToolText(result)}],success:status==="inProgress"?null:!isError,durationMs:null}}function toolWireProjection(input){switch(input.tool.itemType){case"commandExecution":return{item:commandExecutionItem(input.tool,input.status,input.cwd,input.result),diff:""};case"fileChange":return fileChangeProjection({id:input.tool.id,name:input.tool.name,args:input.tool.args,status:input.status,result:input.result});case"mcpToolCall":return{item:mcpToolCallItem(input.tool,input.status,input.result),diff:""};case"dynamicToolCall":return{item:dynamicToolCallItem(input.tool,input.status,input.result,input.isError),diff:""};default:return assertNeverToolItem(input.tool.itemType)}}function providerNativeItem(id,message,content){if(content.subtype==="web_search_call")return webSearchItem(id,content);let summary=formatProviderNativeSummary(message,content,!0),body=formatProviderNativeBody(content,!0);return{type:"webSearch",id,query:body?`${summary}
|
|
15
15
|
${body}`:summary,action:null,results:null}}function buildWireItem(item){return{...item}}function commandFromArgs(args){return isRecord11(args)?readString3(args,"command")??readString3(args,"cmd")??stringifyJson(toJsonValue2(args)):""}function splitMcpName(name){let marker=name.indexOf("__");return marker===-1?["",name]:[name.slice(0,marker),name.slice(marker+2)]}function exitCodeFromResult(result){if(!isRecord11(result))return null;let details=isRecord11(result.details)?result.details:void 0,value=details?details.exitCode??details.code:void 0;return typeof value=="number"?value:null}function toolResultContent(result){return!isRecord11(result)||!Array.isArray(result.content)?[]:result.content.map(toJsonValue2)}function capUtf8(value,maxBytes){if(maxBytes<=0)return"";let used=0,result="";for(let char of value){let size=byteLength(char);if(used+size>maxBytes)break;used+=size,result+=char}return result}function byteLength(value){return new TextEncoder().encode(value).byteLength}function isRecord11(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function readString3(record,key){let value=record[key];return typeof value=="string"?value:void 0}function toJsonValue2(value){return value===null||typeof value=="boolean"||typeof value=="number"||typeof value=="string"?value:Array.isArray(value)?value.map(toJsonValue2):isRecord11(value)?Object.fromEntries(Object.entries(value).map(([key,entry])=>[key,toJsonValue2(entry)])):null}function stringifyJson(value){return JSON.stringify(value)??""}function assertNeverToolItem(value){throw new Error(`Unhandled tool item type: ${String(value)}`)}var EventProjector=class{constructor(options){this.toolItems=new Map,this.completedItemIds=new Set,this.turnDiff=new TurnDiffTracker,this.messageCounter=0,this.finalized=!1,this.options=options,this.messageItems=new MessageItemProjector({itemId:contentIndex=>this.itemId(contentIndex),started:item=>this.started(item),completed:item=>this.completed(item),notification:(method,params)=>this.notification(method,params)})}project(event){if(this.finalized)return emptyResult();switch(event.type){case"message_start":return event.message.role==="assistant"&&(this.activeMessageId=messageIdFromMessage(event.message)??this.nextMessageId()),emptyResult();case"message_update":return event.message.role!=="assistant"?emptyResult():(this.activeMessageId=messageIdFromMessage(event.message)??this.activeMessageId??this.nextMessageId(),this.projectAssistantEvent(event.assistantMessageEvent));case"message_end":return event.message.role!=="assistant"?emptyResult():(this.activeMessageId=messageIdFromMessage(event.message)??this.activeMessageId??this.nextMessageId(),{notifications:[...this.messageItems.completeDanglingText(event.message),...this.projectProviderNative(event.message)]});case"tool_execution_start":return this.rememberTool(event.toolCallId,event.toolName,event.args),emptyResult();case"tool_execution_update":return{notifications:this.projectToolUpdate(event.toolCallId,event.partialResult)};case"tool_execution_end":return{notifications:this.completeTool(event.toolCallId,event.isError,event.result)};case"compaction_start":return{notifications:this.startCompaction()};case"compaction_end":return{notifications:this.completeCompaction()};default:return emptyResult()}}projectAssistantEvent(event){switch(event.type){case"start":case"toolcall_delta":return emptyResult();case"text_start":return{notifications:this.messageItems.startText(event.contentIndex)};case"text_delta":return{notifications:this.messageItems.deltaText(event.contentIndex,event.delta)};case"text_end":return{notifications:this.messageItems.completeText(event.contentIndex,event.content)};case"thinking_start":return{notifications:this.messageItems.startReasoning(event.contentIndex)};case"thinking_delta":return{notifications:this.messageItems.deltaReasoning(event.contentIndex,event.delta)};case"thinking_end":return{notifications:this.messageItems.completeReasoning(event.contentIndex,event.content)};case"toolcall_start":return emptyResult();case"toolcall_end":return{notifications:this.startTool(event.toolCall)};case"done":return{notifications:this.messageItems.closeDanglingItems(),turnCompletion:{status:"completed"}};case"error":return{notifications:[],turnCompletion:{status:event.reason==="aborted"?"interrupted":"failed",errorMessage:event.error.errorMessage}};default:return assertNeverProjection(event)}}finalize(){return this.finalized?[]:(this.finalized=!0,this.closeDanglingItems())}nextMessageId(){return this.messageCounter+=1,`message-${this.messageCounter}`}itemId(contentIndex){return`${this.activeMessageId??this.nextMessageId()}:${contentIndex}`}startTool(toolCall){let itemType=classifyTool(toolCall.name),active={id:toolCall.id,name:toolCall.name,itemType,args:toolCall.arguments,output:"",completed:!1};return this.toolItems.set(toolCall.id,active),[this.started(this.projectToolItem(active,!1).item)]}rememberTool(toolCallId,toolName,args){this.toolItems.has(toolCallId)||this.toolItems.set(toolCallId,{id:toolCallId,name:toolName,itemType:classifyTool(toolName),args,output:"",completed:!1})}projectToolUpdate(toolCallId,partialResult){let tool=this.toolItems.get(toolCallId);if(tool?.itemType!=="commandExecution")return[];let delta=capCommandOutput(extractToolText(partialResult),remainingCommandOutputBytes(tool.output));return delta?(tool.output+=delta,[this.notification("item/commandExecution/outputDelta",{itemId:tool.id,delta})]):[]}completeTool(toolCallId,isError,result){let tool=this.toolItems.get(toolCallId);if(!tool||tool.completed)return[];tool.completed=!0;let resultText=extractToolText(result);tool.itemType==="commandExecution"&&resultText&&(tool.output=capCommandOutput(resultText));let projection=this.projectToolItem(tool,!0,isError,result),cumulativeDiff=this.turnDiff.update(tool.id,projection.diff,this.toolItems.keys());return[this.completed(projection.item),...cumulativeDiff===void 0?[]:[this.notification("turn/diff/updated",{diff:cumulativeDiff})]]}projectToolItem(tool,completed,isError=!1,result){return toolWireProjection({tool,status:completed?isError?"failed":"completed":"inProgress",cwd:this.options.cwd??process.cwd(),result,isError})}projectProviderNative(message){return message.content.flatMap((content,contentIndex)=>{if(content.type!=="providerNative")return[];let id=`${this.activeMessageId??this.nextMessageId()}:providerNative:${contentIndex}`,item=providerNativeItem(id,message,content);return[this.started(item),this.completed(item)]})}closeDanglingItems(){return[...this.messageItems.closeDanglingItems(),...Array.from(this.toolItems.values()).flatMap(tool=>tool.completed?[]:this.completeTool(tool.id,!1,void 0))]}startCompaction(){return this.compactionItemId=`context-compaction:${this.messageCounter+1}`,[this.started({type:"contextCompaction",id:this.compactionItemId})]}completeCompaction(){let id=this.compactionItemId??`context-compaction:${this.messageCounter+1}`;return this.compactionItemId=void 0,[this.completed({type:"contextCompaction",id})]}started(item){return this.notification("item/started",{item:buildWireItem(item),startedAtMs:this.nowMs()})}completed(item){let wireItem=buildWireItem(item);return this.completedItemIds.has(String(wireItem.id))||(this.completedItemIds.add(String(wireItem.id)),this.options.turnLog?.appendItem(this.options.threadId,this.options.turnId,wireItem)),this.notification("item/completed",{item:wireItem,completedAtMs:this.nowMs()})}notification(method,params){return{method,params:{threadId:this.options.threadId,turnId:this.options.turnId,...params}}}nowMs(){return this.options.nowMs?.()??Date.now()}};function emitTurnTerminalNotifications(threadId,turn,emitToThread){turn.status==="failed"&&turn.error!==null&&emitToThread(threadId,{method:"error",params:{threadId,turnId:turn.id,error:turn.error,willRetry:!1}}),emitToThread(threadId,{method:"turn/completed",params:{threadId,turn}})}function createTurnEngine(options){return new TurnEngine(options)}var TurnEngine=class{constructor(options){this.pendingByThreadId=new Map,this.subscribedThreadIds=new Set,this.projectorByThreadId=new Map,this.store=options.store,this.turnLog=options.turnLog,this.emitToThread=options.emitToThread,this.broadcast=options.broadcast}observeThread(threadId){this.ensureSessionSubscription(threadId,this.getLoadedThreadOrThrow(threadId))}startTurn(params,deferNotifications){this.getLoadedThreadOrThrow(params.threadId);let didSettle=!1;return new Promise((resolve4,reject)=>{this.store.runThreadTask(params.threadId,async()=>{try{let parsedInput=parseInput(params.input),entry=this.getLoadedThreadOrThrow(params.threadId);this.observeThread(params.threadId);let turnId=createTurnId(),startedAtMs=Date.now(),startedAt=new Date(startedAtMs).toISOString(),turn=buildTurn(turnId,"inProgress",startedAtMs,null,[]),userMessage=buildUserMessage(params.clientUserMessageId??null,parsedInput.content);entry.activeTurn={turnId,startedAt},entry.status="active",entry.updatedAt=startedAt,this.turnLog.recordTurn(params.threadId,{turnId,startedAt,status:"running"});let emitInitialNotifications=()=>{this.broadcast({method:"thread/status/changed",params:{threadId:params.threadId,status:{type:"active",activeFlags:[]}}}),this.emitToThread(params.threadId,{method:"turn/started",params:{threadId:params.threadId,turn}}),this.emitUserMessage(params.threadId,turnId,startedAtMs,userMessage)};deferNotifications?.(emitInitialNotifications)!==!0&&emitInitialNotifications();let pendingTurn,completion=new Promise(complete=>{pendingTurn={turnId,startedAt,startedAtMs,resolve:complete,interrupted:!1,completed:!1,deferTerminalNotifications:void 0},this.pendingByThreadId.set(params.threadId,pendingTurn)});entry.session.prompt(parsedInput.text,{source:"rpc",preflightResult:success=>{if(success){didSettle||(didSettle=!0,resolve4({turn}));return}pendingTurn.completed||this.completeTurn(params.threadId,"failed","Prompt preflight failed")}}).then(()=>{didSettle||(didSettle=!0,reject(toTurnEngineError(new Error("Prompt preflight failed"))))}).catch(error=>{didSettle||(didSettle=!0,reject(toTurnEngineError(error))),this.completeTurn(params.threadId,"failed",error instanceof Error?error.message:String(error))}),await completion}catch(error){didSettle||(didSettle=!0,reject(toTurnEngineError(error instanceof Error?error:new Error(String(error)))))}}).catch(error=>{didSettle||(didSettle=!0,reject(toTurnEngineError(error)))})})}async steerTurn(params){let entry=this.getLoadedThreadOrThrow(params.threadId),activeTurn=entry.activeTurn;if(!activeTurn)throw invalidRequest3(`No active turn for thread ${params.threadId}`);if(activeTurn.turnId!==params.expectedTurnId)throw invalidRequest3(`Turn id mismatch: expected ${params.expectedTurnId} but active turn is ${activeTurn.turnId}`);let parsedInput=parseInput(params.input);return await entry.session.steer(parsedInput.text,{source:"rpc"}),this.emitUserMessage(params.threadId,activeTurn.turnId,Date.now(),buildUserMessage(params.clientUserMessageId??null,parsedInput.content)),{turnId:activeTurn.turnId}}async interruptTurn(params,deferNotifications){let entry=this.getLoadedThreadOrThrow(params.threadId),activeTurn=entry.activeTurn;if(!activeTurn)return{};if(activeTurn.turnId!==params.turnId)throw invalidRequest3(`Turn id mismatch: expected ${params.turnId} but active turn is ${activeTurn.turnId}`);let pending=this.pendingByThreadId.get(params.threadId);return pending?.turnId===params.turnId&&(pending.interrupted=!0,pending.deferTerminalNotifications=deferNotifications),await entry.session.abort(),entry.activeTurn?.turnId===params.turnId&&this.completeTurn(params.threadId,"interrupted"),{}}completeTurn(threadId,status="completed",message){let entry=this.getLoadedThreadOrThrow(threadId),activeTurn=entry.activeTurn,pending=this.pendingByThreadId.get(threadId);if(!activeTurn||!pending||pending.completed||pending.turnId!==activeTurn.turnId)return;pending.completed=!0,this.finalizeProjection(threadId);let completedStatus=pending.interrupted&&status==="completed"?"interrupted":status,completedAtMs=Date.now();this.turnLog.completeTurn(threadId,pending.turnId,{status:completedStatus,completedAt:new Date(completedAtMs).toISOString(),error:message??null});let turn=buildTurn(pending.turnId,completedStatus,pending.startedAtMs,completedAtMs,readLoggedItems(this.turnLog,threadId,pending.turnId),message);entry.activeTurn=null,entry.status="idle",entry.updatedAt=new Date(completedAtMs).toISOString(),this.pendingByThreadId.delete(threadId);let emitTerminalNotifications=()=>{this.broadcast({method:"thread/status/changed",params:{threadId,status:{type:"idle"}}}),emitTurnTerminalNotifications(threadId,turn,this.emitToThread)};pending.deferTerminalNotifications?.(emitTerminalNotifications)!==!0&&emitTerminalNotifications(),pending.resolve()}emitUserMessage(threadId,turnId,startedAtMs,userMessage){let wireUserMessage=wireItemToJson(userMessage);this.emitToThread(threadId,{method:"item/started",params:{threadId,turnId,item:wireUserMessage,startedAtMs}}),this.emitToThread(threadId,{method:"item/completed",params:{threadId,turnId,item:wireUserMessage,completedAtMs:startedAtMs}}),this.turnLog.appendItem(threadId,turnId,userMessage)}ensureSessionSubscription(threadId,entry){this.subscribedThreadIds.has(threadId)||(this.subscribedThreadIds.add(threadId),entry.session.subscribe(event=>{this.projectSessionEvent(threadId,event),event.type==="agent_end"&&this.completeTurn(threadId)}))}projectSessionEvent(threadId,event){let entry;try{entry=this.getLoadedThreadOrThrow(threadId)}catch{return}let activeTurn=entry.activeTurn;if(!activeTurn&&(activeTurn=this.startExtensionTurn(threadId,entry,event),!activeTurn))return;let state=this.projectorByThreadId.get(threadId);(!state||state.turnId!==activeTurn.turnId)&&(state={turnId:activeTurn.turnId,projector:new EventProjector({threadId,turnId:activeTurn.turnId,turnLog:this.turnLog,cwd:entry.cwd})},this.projectorByThreadId.set(threadId,state));let result=state.projector.project(event);this.emitProjected(threadId,result.notifications);let completion=result.turnCompletion;completion&&completion.status!=="completed"&&completion.status!=="running"&&this.completeTurn(threadId,completion.status,completion.errorMessage)}startExtensionTurn(threadId,entry,event){let sessionEvent=event,userMessage=sessionEvent.type==="message_start"&&sessionEvent.message.role==="user"?buildExtensionUserMessage(sessionEvent.message):null,isAgentStart=sessionEvent.type==="agent_start";if(userMessage===null&&!isAgentStart)return null;let turnId=createTurnId(),startedAtMs=Date.now(),startedAt=new Date(startedAtMs).toISOString(),turn=buildTurn(turnId,"inProgress",startedAtMs,null,[]);return entry.activeTurn={turnId,startedAt},entry.status="active",entry.updatedAt=startedAt,this.turnLog.recordTurn(threadId,{turnId,startedAt,status:"running"}),this.pendingByThreadId.set(threadId,{turnId,startedAt,startedAtMs,resolve:()=>{},interrupted:!1,completed:!1,deferTerminalNotifications:void 0}),this.broadcast({method:"thread/status/changed",params:{threadId,status:{type:"active",activeFlags:[]}}}),this.emitToThread(threadId,{method:"turn/started",params:{threadId,turn}}),userMessage&&this.emitUserMessage(threadId,turnId,startedAtMs,userMessage),entry.activeTurn}finalizeProjection(threadId){let state=this.projectorByThreadId.get(threadId);state&&(this.projectorByThreadId.delete(threadId),this.emitProjected(threadId,state.projector.finalize()))}emitProjected(threadId,notifications){for(let notification of notifications)this.emitToThread(threadId,{method:notification.method,params:notification.params})}getLoadedThreadOrThrow(threadId){try{return this.store.getLoadedThread(threadId)}catch{throw invalidRequest3(`Thread not found: ${threadId}`)}}};function buildExtensionUserMessage(message){let content=typeof message.content=="string"?[{type:"text",text:message.content,text_elements:[]}]:message.content.map(part=>part.type==="text"?{type:"text",text:part.text,text_elements:[]}:{type:"image",url:`data:${part.mimeType};base64,${part.data}`});return buildUserMessage(null,content)}import{AsyncLocalStorage}from"node:async_hooks";import{join as join6}from"node:path";var RATE_LIMITS_AUTHENTICATION_MESSAGE="codex account authentication required to read rate limits",TOKEN_USAGE_AUTHENTICATION_MESSAGE="codex account authentication required to read token usage";function registerAppServerAccountMethods(registry,options={}){let agentDir=resolvePath(options.agentDir??getAgentDir());registry.register("account/read",{scope:"global",handler:({request})=>(parseAccountReadParams(request.params),accountReadResponse(agentDir))}),registry.register("account/providerAccounts/read",{scope:"global",handler:({request})=>providerAccountsResponse(agentDir,parseProviderAccountsReadParams(request.params))}),registry.register("account/providerAccounts/pin",{scope:"global",handler:async({request})=>{let params=parseProviderAccountsPinParams(request.params);return await pinCredentialAccount(AuthStorage.create(join6(agentDir,"auth.json")),params.provider,params.name),{}}}),registry.register("account/providerAccounts/remove",{scope:"global",handler:async({request})=>{let params=parseProviderAccountsRemoveParams(request.params);return await removeCredentialAccount(AuthStorage.create(join6(agentDir,"auth.json")),params.provider,params.name),{}}}),registry.register("account/rateLimits/read",{scope:"global",handler:()=>{throw unauthenticatedAccountReadError(RATE_LIMITS_AUTHENTICATION_MESSAGE)}}),registry.register("account/usage/read",{scope:"global",handler:()=>{throw unauthenticatedAccountReadError(TOKEN_USAGE_AUTHENTICATION_MESSAGE)}})}function providerAccountEventNotification(event){return event.type==="accounts_changed"?{method:"account/providerAccounts/updated",params:{provider:event.provider}}:{method:"account/providerAccounts/failover",params:{provider:event.provider,from:event.from,to:event.to,reason:event.reason}}}function accountReadResponse(agentDir){let credentials=AuthStorage.create(join6(agentDir,"auth.json")).getAll();return{account:Object.keys(credentials).length>0?{type:"apiKey"}:null,requiresOpenaiAuth:!1}}async function providerAccountsResponse(agentDir,params){let storage=AuthStorage.create(join6(agentDir,"auth.json"));return{provider:params.provider,accounts:await getCredentialAccounts(storage,params.provider)}}function parseAccountReadParams(value){if(value==null)return{};if(!isRecord12(value))throw new RpcHandlerError({code:-32600,message:"account/read params must be an object"});let refreshToken=value.refreshToken;if(refreshToken!==void 0&&typeof refreshToken!="boolean")throw new RpcHandlerError({code:-32600,message:"account/read refreshToken must be a boolean"});return refreshToken===void 0?{}:{refreshToken}}function parseProviderAccountsReadParams(value){return{provider:providerFrom(value,"account/providerAccounts/read")}}function parseProviderAccountsPinParams(value){let params=requiredRecord(value,"account/providerAccounts/pin");if(typeof params.name!="string"&¶ms.name!==null)throw invalidParams2("account/providerAccounts/pin name must be a string or null");return{provider:requiredProvider(params,"account/providerAccounts/pin"),name:params.name}}function parseProviderAccountsRemoveParams(value){let params=requiredRecord(value,"account/providerAccounts/remove");if(typeof params.name!="string"||params.name.length===0)throw invalidParams2("account/providerAccounts/remove name must be a non-empty string");return{provider:requiredProvider(params,"account/providerAccounts/remove"),name:params.name}}function providerFrom(value,method){return requiredProvider(requiredRecord(value,method),method)}function requiredProvider(params,method){if(typeof params.provider!="string"||params.provider.length===0)throw invalidParams2(`${method} provider must be a non-empty string`);return normalizeProviderId(params.provider)}function requiredRecord(value,method){if(!isRecord12(value))throw invalidParams2(`${method} params must be an object`);return value}function invalidParams2(message){return new RpcHandlerError({code:-32600,message})}function unauthenticatedAccountReadError(message){return new RpcHandlerError({code:-32600,message})}function isRecord12(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var PERMISSION_PROFILES=[{id:"dangerFullAccess",description:null,allowed:!0}],EXPERIMENTAL_FEATURES=[];function registerAppServerCatalogMethods(registry,options={}){let serverCwd=resolvePath(options.serverCwd??process.cwd()),agentDir=options.agentDir??getAgentDir(),defaultModelRegistry,getModelRegistry=()=>(defaultModelRegistry??=ModelRegistry.create(AuthStorage.create()),options.modelRegistry??defaultModelRegistry);registry.register("collaborationMode/list",{experimental:!0,scope:"global",handler:({request})=>{parseObjectParams(request.params,"collaborationMode/list");let model=defaultModelId(getModelRegistry().getAvailable());return{data:[buildSenpiCollaborationModePreset(model)]}}}),registry.register("permissionProfile/list",{scope:"global",handler:({request})=>{let params=parsePermissionProfileListParams(request.params),cwd=resolvePath(params.cwd??serverCwd,serverCwd,{trim:!0});return SettingsManager.create(cwd,agentDir).getProjectSettings(),paginateCatalog(PERMISSION_PROFILES,params.cursor,params.limit,"permissionProfile/list")}}),registry.register("experimentalFeature/list",{scope:"global",handler:({request})=>{let params=parseExperimentalFeatureListParams(request.params);return validateThreadId(params.threadId,options.threads,"experimentalFeature/list"),paginateCatalog(EXPERIMENTAL_FEATURES,params.cursor,params.limit,"experimentalFeature/list")}}),registry.register("mcpServerStatus/list",{scope:"global",handler:({request})=>{let params=parseMcpServerStatusListParams(request.params);validateThreadId(params.threadId,options.threads,"mcpServerStatus/list");let adapter=options.threads?.getMcpWireStatusAdapter(params.threadId),detail=params.detail??"full",visibleStatuses=(adapter?.getServerStatuses()??[]).map(status=>applyMcpServerStatusDetail(status,detail));return paginateCatalog(visibleStatuses,params.cursor,params.limit,"mcpServerStatus/list")}})}function defaultModelId(models){return models.find(model=>Object.entries(defaultModelPerProvider).some(([provider,modelId])=>provider===model.provider&&modelId===model.id))?.id??models[0]?.id??"unknown"}function parsePermissionProfileListParams(value){let params=parseObjectParams(value,"permissionProfile/list");return{cursor:optionalString3(params.cursor,"permissionProfile/list","cursor"),limit:optionalNumber2(params.limit,"permissionProfile/list","limit"),cwd:optionalString3(params.cwd,"permissionProfile/list","cwd")}}function parseExperimentalFeatureListParams(value){let params=parseObjectParams(value,"experimentalFeature/list");return{cursor:optionalString3(params.cursor,"experimentalFeature/list","cursor"),limit:optionalNumber2(params.limit,"experimentalFeature/list","limit"),threadId:optionalString3(params.threadId,"experimentalFeature/list","threadId")}}function parseMcpServerStatusListParams(value){let params=parseObjectParams(value,"mcpServerStatus/list");return{cursor:optionalString3(params.cursor,"mcpServerStatus/list","cursor"),limit:optionalNumber2(params.limit,"mcpServerStatus/list","limit"),detail:optionalMcpServerStatusDetail(params.detail),threadId:optionalString3(params.threadId,"mcpServerStatus/list","threadId")}}function optionalMcpServerStatusDetail(value){if(value==null||value==="full"||value==="toolsAndAuthOnly")return value;throw invalidCatalogParams("mcpServerStatus/list detail must be full, toolsAndAuthOnly, or null")}function applyMcpServerStatusDetail(server,detail){switch(detail){case"full":return server;case"toolsAndAuthOnly":return{...server,serverInfo:null,resources:[],resourceTemplates:[]};default:return assertNever2(detail)}}function parseObjectParams(value,method){if(value==null)return{};if(!isRecord13(value))throw invalidCatalogParams(`${method} params must be an object`);return value}function optionalString3(value,method,field){if(value==null)return value;if(typeof value!="string")throw invalidCatalogParams(`${method} ${field} must be a string or null`);return value}function optionalNumber2(value,method,field){if(value==null)return value;if(typeof value!="number"||!Number.isSafeInteger(value)||value<0)throw invalidCatalogParams(`${method} ${field} must be a non-negative integer or null`);return value}function paginateCatalog(items,cursor,limit,method){let start=parseCursor2(cursor,items.length,method),pageSize=Math.max(1,limit??items.length),data=items.slice(start,start+pageSize),nextOffset=start+data.length;return{data,nextCursor:nextOffset<items.length?String(nextOffset):null}}function parseCursor2(cursor,total,method){if(cursor==null)return 0;if(!/^\d+$/u.test(cursor))throw invalidCatalogParams(`${method} received an invalid cursor: ${cursor}`);let offset=Number(cursor);if(!Number.isSafeInteger(offset)||offset>total)throw invalidCatalogParams(`${method} cursor ${cursor} exceeds total records ${total}`);return offset}function validateThreadId(threadId,threads,method){if(threadId!=null){if(!threads)throw invalidCatalogParams(`${method} received an unknown threadId: ${threadId}`);try{threads.getLoadedThread(threadId)}catch(error){throw error instanceof Error&&error.name==="ThreadNotFoundError"?invalidCatalogParams(`${method} received an unknown threadId: ${threadId}`):error}}}function invalidCatalogParams(message){return new RpcHandlerError({code:-32600,message})}function isRecord13(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function assertNever2(value){throw new Error(`Unhandled MCP server status detail: ${JSON.stringify(value)}`)}import{join as join7}from"node:path";var CONFIG_LAYER_VERSION="unversioned",SETTINGS_MAPPINGS=[{settingsKey:"defaultModel",wireKey:"model"},{settingsKey:"defaultProvider",wireKey:"model_provider"},{settingsKey:"defaultThinkingLevel",wireKey:"model_reasoning_effort"}];function registerAppServerConfigMethods(registry,options={}){let serverCwd=resolvePath(options.serverCwd??process.cwd()),agentDir=resolvePath(options.agentDir??getAgentDir());registry.register("config/read",{scope:"global",handler:({request})=>{let params=parseConfigReadParams(request.params),cwd=resolvePath(params.cwd??serverCwd,serverCwd,{trim:!0});return buildConfigReadResponse(SettingsManager.create(cwd,agentDir),agentDir,cwd,params.includeLayers===!0)}}),registry.register("configRequirements/read",{scope:"global",handler:()=>({requirements:null})})}function buildConfigReadResponse(settingsManager,agentDir,cwd,includeLayers){let globalSettings=settingsManager.getGlobalSettings(),projectSettings=settingsManager.getProjectSettings(),userSource={type:"user",file:join7(agentDir,"settings.json"),profile:null},projectSource={type:"project",dotCodexFolder:join7(cwd,CONFIG_DIR_NAME)},userMetadata=buildLayerMetadata(userSource),projectMetadata=buildLayerMetadata(projectSource),origins={};for(let mapping of SETTINGS_MAPPINGS)readMappedSetting(projectSettings,mapping.settingsKey)!==void 0?origins[mapping.wireKey]=projectMetadata:readMappedSetting(globalSettings,mapping.settingsKey)!==void 0&&(origins[mapping.wireKey]=userMetadata);return{config:{model:settingsManager.getDefaultModel()??null,model_provider:settingsManager.getDefaultProvider()??null,approval_policy:"never",sandbox_mode:"danger-full-access",model_reasoning_effort:settingsManager.getDefaultThinkingLevel()??null},origins,layers:includeLayers?[buildLayer(userSource,globalSettings),buildLayer(projectSource,projectSettings)]:null}}function buildLayerMetadata(source){return{name:source,version:CONFIG_LAYER_VERSION}}function buildLayer(source,settings){return{name:source,version:CONFIG_LAYER_VERSION,config:buildMappedSettings(settings),disabledReason:null}}function buildMappedSettings(settings){let config={};for(let mapping of SETTINGS_MAPPINGS){let value=readMappedSetting(settings,mapping.settingsKey);value!==void 0&&(config[mapping.wireKey]=value)}return config}function readMappedSetting(settings,key){let value=settings[key];return typeof value=="string"?value:void 0}function parseConfigReadParams(value){if(value==null)return{};if(!isRecord14(value))throw invalidConfigParams("config/read params must be an object");let includeLayers=value.includeLayers;if(includeLayers!==void 0&&typeof includeLayers!="boolean")throw invalidConfigParams("config/read includeLayers must be a boolean");let cwd=value.cwd;if(cwd!=null&&typeof cwd!="string")throw invalidConfigParams("config/read cwd must be a string or null");return{...includeLayers===void 0?{}:{includeLayers},...cwd===void 0?{}:{cwd}}}function invalidConfigParams(message){return new RpcHandlerError({code:-32600,message})}function isRecord14(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}import{arch,platform,release,type}from"node:os";var DEFAULT_CAPABILITIES={experimentalApi:!1,requestAttestation:!1},SERVER_NOTIFICATIONS=new Set([...SERVER_NOTIFICATION_METHODS,...ADDITIVE_SERVER_NOTIFICATION_METHODS]),EXPERIMENTAL_SERVER_NOTIFICATIONS=new Set(EXPERIMENTAL_SERVER_NOTIFICATION_METHODS),ManagedConnection=class{constructor(input){this.optOutNotificationMethods=new Set,this.state={initialized:!1},this.id=input.id,this.transportKind=input.transportKind,this.sendMessage=input.send,this.closeConnection=input.close}get initialized(){return this.state.initialized}get initializedState(){return this.state.initialized?this.state:void 0}get capabilities(){return this.state.initialized?this.state.capabilities:DEFAULT_CAPABILITIES}initialize(params,serverVersion){if(this.state.initialized)return{kind:"already-initialized"};let capabilities=normalizeCapabilities(params.capabilities);this.optOutNotificationMethods.clear();for(let method of capabilities.optOutNotificationMethods??[])this.optOutNotificationMethods.add(method);let userAgent=buildUserAgent(params.clientInfo.name,serverVersion);return this.state={initialized:!0,clientInfo:params.clientInfo,capabilities,userAgent},{kind:"initialized"}}async send(message){await this.sendMessage(populateOutboundNotificationEnvelope(message))}async close(reason){await this.closeConnection(reason)}};function createConnection(input){return new ManagedConnection(input)}function parseInitializeParams(params){if(!isJsonObject2(params))return null;let clientInfo=parseClientInfo(params.clientInfo);if(!clientInfo)return null;let capabilities=parseCapabilities(params.capabilities);return capabilities==="invalid"?null:{clientInfo,capabilities}}function canDeliverNotification(connection,notification){return!connection.initialized||!SERVER_NOTIFICATIONS.has(notification.method)||connection.optOutNotificationMethods.has(notification.method)?!1:!EXPERIMENTAL_SERVER_NOTIFICATIONS.has(notification.method)||connection.capabilities.experimentalApi}function buildInitializeResponse(connection,codexHome){let state=connection.initializedState;if(!state)throw new Error("Connection is not initialized");return{userAgent:state.userAgent,codexHome,platformFamily:platformFamily(),platformOs:platformOs()}}function parseClientInfo(value){if(!isJsonObject2(value))return null;let name=value.name,version=value.version,title=value.title;return typeof name!="string"||name.length===0||typeof version!="string"||version.length===0||title!=null&&typeof title!="string"?null:{name,title:typeof title=="string"?title:null,version}}function parseCapabilities(value){if(value==null)return null;if(!isJsonObject2(value))return"invalid";let experimentalApi=parseOptionalBoolean(value.experimentalApi),requestAttestation=parseOptionalBoolean(value.requestAttestation),mcpServerOpenaiFormElicitation=parseOptionalBoolean(value.mcpServerOpenaiFormElicitation),optOutNotificationMethods=parseOptionalStringArray(value.optOutNotificationMethods);return experimentalApi==="invalid"||requestAttestation==="invalid"||mcpServerOpenaiFormElicitation==="invalid"||optOutNotificationMethods==="invalid"?"invalid":{experimentalApi:experimentalApi??!1,requestAttestation:requestAttestation??!1,...mcpServerOpenaiFormElicitation===void 0?{}:{mcpServerOpenaiFormElicitation},...optOutNotificationMethods===null?{}:{optOutNotificationMethods}}}function normalizeCapabilities(capabilities){return capabilities??DEFAULT_CAPABILITIES}function parseOptionalBoolean(value){if(value!==void 0)return typeof value=="boolean"?value:"invalid"}function parseOptionalStringArray(value){return value==null?null:!Array.isArray(value)||value.some(entry=>typeof entry!="string")?"invalid":value}function buildUserAgent(clientName,serverVersion){return`${clientName}/${serverVersion} (${type()} ${release()}; ${arch()}) senpi_app_server`}function platformFamily(){return platform()==="win32"?"windows":"unix"}function platformOs(){switch(platform()){case"darwin":return"macos";case"win32":return"windows";default:return"linux"}}function isJsonObject2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}import{randomUUID as randomUUID4}from"node:crypto";import{mkdir as mkdir2,readdir as readdir4,readFile as readFile5,rmdir,stat as stat3,unlink,writeFile as writeFile2}from"node:fs/promises";import{dirname as dirname2,join as join8}from"node:path";import{setTimeout as delay}from"node:timers/promises";var DEFAULT_MODEL_IDS=new Map(Object.entries(defaultModelPerProvider));function buildModelListResponse(models,params){let filteredModels=models.map(model=>buildWireModel(model)).filter(model=>params.includeHidden===!0||!model.hidden),start=parseModelListCursor(params.cursor,filteredModels.length),limit=normalizeModelListLimit(params.limit,filteredModels.length),end=Math.min(start+limit,filteredModels.length);return{data:filteredModels.slice(start,end),nextCursor:end<filteredModels.length?String(end):null}}function buildWireModel(model){return{id:`${model.provider}/${model.id}`,model:model.id,upgrade:null,upgradeInfo:null,availabilityNux:null,displayName:model.name??model.id,description:"",hidden:model.hidden===!0,supportedReasoningEfforts:model.reasoning?getSupportedThinkingLevels(model).filter(level=>level!=="off").map(reasoningEffort=>({reasoningEffort,description:""})):[],defaultReasoningEffort:"medium",inputModalities:["text"],supportsPersonality:!1,additionalSpeedTiers:[],serviceTiers:[],defaultServiceTier:null,isDefault:DEFAULT_MODEL_IDS.get(model.provider)===model.id}}function parseModelListCursor(cursor,total){if(cursor==null)return 0;let start=Number(cursor);if(!Number.isSafeInteger(start)||start<0||!/^\d+$/u.test(cursor))throw invalidModelListParams(`model/list received an invalid cursor: ${cursor}`);if(start>total)throw invalidModelListParams(`model/list cursor ${start} exceeds total models ${total}`);return start}function normalizeModelListLimit(limit,total){return total===0?0:limit==null?total:Math.min(total,Math.max(1,limit))}function invalidModelListParams(message){return new RpcHandlerError({code:-32600,message})}var INSTALLATION_ID_PATH=["app-server","installation-id"],INSTALLATION_ID_LOCK_SUFFIX=".lock",INSTALLATION_ID_LOCK_MARKER_PREFIX="owner-",INSTALLATION_ID_LOCK_MARKER_SUFFIX=".json",INSTALLATION_ID_LOCK_RETRY_ATTEMPTS=100,INSTALLATION_ID_LOCK_RETRY_MS=10,INSTALLATION_ID_INVALID_LOCK_RECLAIM_MS=500,UUID_V4_PATTERN=/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu,pendingInstallationIds=new Map;function registerAppServerModelMethods(registry,options={}){let defaultModelRegistry,getModelRegistry=()=>(defaultModelRegistry??=ModelRegistry.create(AuthStorage.create()),options.modelRegistry??defaultModelRegistry);registry.register("model/list",{handler:({request})=>buildModelListResponse(getModelRegistry().getAvailable(),parseModelListParams(request.params))}),registry.register("remoteControl/status/read",{experimental:!0,handler:()=>buildRemoteControlStatusReadResponse(options.agentDir??getAgentDir())}),registry.register("remoteControl/client/list",{experimental:!0,handler:({request})=>{throw parseRemoteControlClientListParams(request.params),new Error("remote control is unavailable for this app-server")}})}async function buildRemoteControlStatusReadResponse(agentDir){return{status:"disabled",serverName:"senpi app-server",installationId:await ensureInstallationId(agentDir),environmentId:null}}async function ensureInstallationId(agentDir){let installationIdPath=join8(agentDir,...INSTALLATION_ID_PATH),pendingInstallationId=pendingInstallationIds.get(installationIdPath);if(pendingInstallationId)return pendingInstallationId;let installationId=loadOrCreateInstallationId(installationIdPath);pendingInstallationIds.set(installationIdPath,installationId);try{return await installationId}finally{pendingInstallationIds.get(installationIdPath)===installationId&&pendingInstallationIds.delete(installationIdPath)}}async function loadOrCreateInstallationId(installationIdPath){for(let attempt=0;attempt<INSTALLATION_ID_LOCK_RETRY_ATTEMPTS;attempt+=1){let existing=await readInstallationId(installationIdPath);if(existing)return existing;let claimed=await tryCreateInstallationIdUnderLock(installationIdPath);if(claimed)return claimed;await delay(INSTALLATION_ID_LOCK_RETRY_MS)}throw new Error(`timed out waiting for installation id lock: ${installationIdPath}`)}async function readInstallationId(installationIdPath){try{let existing=(await readFile5(installationIdPath,"utf8")).trim();return UUID_V4_PATTERN.test(existing)?existing:void 0}catch(error){if(!isNodeErrorCode(error,"ENOENT"))throw error;return}}async function tryCreateInstallationIdUnderLock(installationIdPath){await mkdir2(dirname2(installationIdPath),{recursive:!0});let lockPath=`${installationIdPath}${INSTALLATION_ID_LOCK_SUFFIX}`,ownerToken=randomUUID4();try{await mkdir2(lockPath,{mode:448})}catch(error){if(isNodeErrorCode(error,"EEXIST")){await reclaimAbandonedInstallationIdLock(lockPath);return}throw error}let markerPath=join8(lockPath,`${INSTALLATION_ID_LOCK_MARKER_PREFIX}${ownerToken}${INSTALLATION_ID_LOCK_MARKER_SUFFIX}`),markerCreated=!1;try{await writeFile2(markerPath,`${JSON.stringify({ownerToken,pid:process.pid,createdAtMs:Date.now()})}
|
|
16
16
|
`,{encoding:"utf8",mode:384,flag:"wx"}),markerCreated=!0;let existing=await readInstallationId(installationIdPath);if(existing)return existing;let installationId=randomUUID4();return await writeFile2(installationIdPath,`${installationId}
|
|
17
|
-
`,{encoding:"utf8",mode:384}),installationId}finally{markerCreated&&await releaseInstallationIdLock(lockPath,markerPath)}}async function reclaimAbandonedInstallationIdLock(lockPath){let metadata=await readInstallationIdLockMetadata(lockPath);metadata.kind==="missing"||!isReclaimableInstallationIdLock(metadata)||(metadata.markerPath!==void 0&&await unlinkInstallationIdLock(metadata.markerPath),await removeEmptyInstallationIdLock(lockPath))}async function removeEmptyInstallationIdLock(lockPath){try{await rmdir(lockPath)}catch(error){if(isNodeErrorCode(error,"ENOENT")||isNodeErrorCode(error,"ENOTDIR")||isNodeErrorCode(error,"ENOTEMPTY"))return;throw error}}function isReclaimableInstallationIdLock(metadata){switch(metadata.kind){case"abandoned":return!0;case"invalid":return Date.now()-metadata.mtimeMs>=INSTALLATION_ID_INVALID_LOCK_RECLAIM_MS;case"live":case"missing":return!1;default:return metadata}}async function readInstallationIdLockMetadata(lockPath){let lockStat;try{lockStat=await stat3(lockPath)}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};throw error}if(!lockStat.isDirectory())return readLegacyInstallationIdLockMetadata(lockPath,lockStat.mtimeMs);let entries;try{entries=await readdir4(lockPath)}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};if(isNodeErrorCode(error,"ENOTDIR"))return readLegacyInstallationIdLockMetadata(lockPath);throw error}let markerNames=entries.filter(entry=>entry.startsWith(INSTALLATION_ID_LOCK_MARKER_PREFIX)&&entry.endsWith(INSTALLATION_ID_LOCK_MARKER_SUFFIX));if(markerNames.length===0)return{kind:"invalid",mtimeMs:lockStat.mtimeMs};let invalidMarkerPath,abandonedMarkerPath;for(let markerName of markerNames){let markerPath=join8(lockPath,markerName),metadataResult=await readLockFile(markerPath);if(metadataResult.kind==="metadata"){if(isProcessAlive(metadataResult.metadata.pid))return{kind:"live",markerPath};abandonedMarkerPath??=markerPath;continue}metadataResult.kind==="invalid"&&(invalidMarkerPath??=markerPath)}return abandonedMarkerPath!==void 0?{kind:"abandoned",markerPath:abandonedMarkerPath}:invalidMarkerPath!==void 0?{kind:"invalid",mtimeMs:lockStat.mtimeMs,markerPath:invalidMarkerPath}:{kind:"missing"}}async function readLegacyInstallationIdLockMetadata(lockPath,knownMtimeMs){let metadataResult=await readLockFile(lockPath);if(metadataResult.kind==="missing")return{kind:"missing"};if(metadataResult.kind==="invalid"){if(knownMtimeMs!==void 0)return{kind:"invalid",mtimeMs:knownMtimeMs};try{return{kind:"invalid",mtimeMs:(await stat3(lockPath)).mtimeMs}}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};throw error}}return isProcessAlive(metadataResult.metadata.pid)?{kind:"live"}:{kind:"abandoned"}}async function readLockFile(lockPath){try{let parsed=JSON.parse((await readFile5(lockPath,"utf8")).trim());return parseInstallationIdLockMetadata(parsed)}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};if(error instanceof SyntaxError||isNodeErrorCode(error,"EISDIR"))return{kind:"invalid"};throw error}}function parseInstallationIdLockMetadata(value){if(!isRecord15(value))return{kind:"invalid"};let{ownerToken,pid,createdAtMs}=value;return typeof ownerToken!="string"||ownerToken.length===0?{kind:"invalid"}:typeof pid!="number"||!Number.isSafeInteger(pid)||pid<=0?{kind:"invalid"}:typeof createdAtMs!="number"||!Number.isFinite(createdAtMs)||createdAtMs<0?{kind:"invalid"}:{kind:"metadata",metadata:{ownerToken,pid,createdAtMs}}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch(error){if(isNodeErrorCode(error,"ESRCH"))return!1;if(isNodeErrorCode(error,"EPERM"))return!0;throw error}}async function releaseInstallationIdLock(lockPath,markerPath){await unlinkInstallationIdLock(markerPath),await removeEmptyInstallationIdLock(lockPath)}async function unlinkInstallationIdLock(lockPath){try{await unlink(lockPath)}catch(error){if(!isNodeErrorCode(error,"ENOENT")&&!isNodeErrorCode(error,"ENOTDIR"))throw error}}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}function parseModelListParams(params){if(!isRecord15(params))return{};let cursor=params.cursor;if(cursor!=null&&typeof cursor!="string")throw new RpcHandlerError({code:-32600,message:"model/list received an invalid cursor"});let limit=params.limit;if(limit!=null&&(typeof limit!="number"||!Number.isInteger(limit)||limit<0||limit>4294967295))throw new RpcHandlerError({code:-32600,message:"model/list received an invalid limit"});let includeHidden=params.includeHidden;if(includeHidden!=null&&typeof includeHidden!="boolean")throw new RpcHandlerError({code:-32600,message:"model/list received an invalid includeHidden"});return{cursor,limit,includeHidden:includeHidden===!0}}function parseRemoteControlClientListParams(params){if(!isRecord15(params))throw invalidRemoteControlParams("remoteControl/client/list requires params");let environmentId=params.environmentId;if(typeof environmentId!="string")throw invalidRemoteControlParams("remoteControl/client/list requires a string environmentId");let cursor=params.cursor;if(cursor!=null&&typeof cursor!="string")throw invalidRemoteControlParams("remoteControl/client/list received an invalid cursor");let limit=params.limit;if(limit!=null&&(typeof limit!="number"||!Number.isInteger(limit)||limit<0||limit>4294967295))throw invalidRemoteControlParams("remoteControl/client/list received an invalid limit");let order=params.order;if(order!=null&&order!=="asc"&&order!=="desc")throw invalidRemoteControlParams("remoteControl/client/list received an invalid order");return{environmentId,cursor,limit,order}}function invalidRemoteControlParams(message){return new RpcHandlerError({code:-32600,message})}function isRecord15(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var ServerCore=class{constructor(options={}){this.connections=new Map,this.activeDispatch=new AsyncLocalStorage,this.registry=options.registry??createRegistry(),this.codexHome=options.codexHome??getAgentDir(),this.version=options.version??VERSION,this.now=options.now??Date.now,registerAppServerModelMethods(this.registry,{modelRegistry:options.modelRegistry,agentDir:this.codexHome}),registerAppServerCatalogMethods(this.registry,{modelRegistry:options.modelRegistry,agentDir:this.codexHome,serverCwd:options.serverCwd,threads:options.threads}),registerAppServerConfigMethods(this.registry,{agentDir:this.codexHome,serverCwd:options.serverCwd}),registerAppServerAccountMethods(this.registry,{agentDir:this.codexHome}),registerAppServerSkillMethods(this.registry,{agentDir:this.codexHome,serverCwd:options.serverCwd,threads:options.threads})}addConnection(input){let connection=createConnection(input);return this.connections.set(connection.id,connection),this.subscribeToProviderAccountEvents(),connection}removeConnection(id){this.connections.delete(id),this.connections.size===0&&(this.unsubscribeProviderAccountEvents?.(),this.unsubscribeProviderAccountEvents=void 0)}getConnection(id){return this.connections.get(id)}registerMethod(method,registration){this.registry.register(method,registration)}deferUntilResponded(connectionId2,action){let dispatch=this.activeDispatch.getStore();return dispatch?.connectionId!==connectionId2||!this.connections.has(connectionId2)?!1:(dispatch.actions.push(action),!0)}async receive(connectionId2,envelope){let connection=this.connections.get(connectionId2);if(connection)switch(envelope.kind){case"request":await this.respondToRequest(connection,envelope.message);return;case"notification":this.handleNotification(envelope.message);return;case"response":return;case"protocol-invalid":await connection.send({id:null,error:invalidRequestError()});return;default:return assertNever3(envelope)}}async sendNotificationToConnection(connectionId2,notification){let connection=this.connections.get(connectionId2);return!connection||!canDeliverNotification(connection,notification)?!1:(await connection.send(populateNotificationEnvelope(notification,this.now())),!0)}async broadcastNotification(notification){let delivered=0,envelope=populateNotificationEnvelope(notification,this.now());for(let connection of this.connections.values())canDeliverNotification(connection,envelope)&&(await connection.send(envelope),delivered+=1);return delivered}subscribeToProviderAccountEvents(){this.unsubscribeProviderAccountEvents||(this.unsubscribeProviderAccountEvents=subscribeProviderAccountEvents(event=>{let notification=providerAccountEventNotification(event),send=async()=>{await this.broadcastNotification(notification)},dispatch=this.activeDispatch.getStore();dispatch&&this.deferUntilResponded(dispatch.connectionId,send)||send()}))}async respondToRequest(connection,request){let dispatch={connectionId:connection.id,actions:[]},response=await this.activeDispatch.run(dispatch,()=>this.dispatchRequest(connection,request));try{await connection.send(response)}catch(error){throw dispatch.actions.length=0,error}if("error"in response||this.connections.get(connection.id)!==connection){dispatch.actions.length=0;return}let actions=dispatch.actions.splice(0);for(let action of actions)await action()}async dispatchRequest(connection,request){return request.method==="initialize"?this.handleInitialize(connection,request):this.registry.dispatch(connection,request)}handleInitialize(connection,request){if(connection.initialized)return{id:request.id,error:alreadyInitializedError()};let params=parseInitializeParams(request.params);if(!params)return{id:request.id,error:invalidParamsError()};let initialized=connection.initialize(params,this.version);switch(initialized.kind){case"initialized":return{id:request.id,result:buildInitializeResponse(connection,this.codexHome)};case"already-initialized":return{id:request.id,error:alreadyInitializedError()};default:return assertNever3(initialized)}}handleNotification(notification){notification.method}};function assertNever3(value){throw new Error(`Unhandled app-server envelope kind: ${JSON.stringify(value)}`)}function createModeTurnStore(threads){return new ModeTurnStore(threads)}function turnStartParams(request){let params=objectParams2(request);return{threadId:requiredStringParam(params.threadId,"threadId"),clientUserMessageId:optionalNullableStringParam(params.clientUserMessageId,"clientUserMessageId"),input:userInputArrayParam(params.input)}}function turnSteerParams(request){let params=objectParams2(request);return{threadId:requiredStringParam(params.threadId,"threadId"),expectedTurnId:requiredStringParam(params.expectedTurnId,"expectedTurnId"),clientUserMessageId:optionalNullableStringParam(params.clientUserMessageId,"clientUserMessageId"),input:userInputArrayParam(params.input)}}function turnInterruptParams(request){let params=objectParams2(request);return{threadId:requiredStringParam(params.threadId,"threadId"),turnId:requiredStringParam(params.turnId,"turnId")}}function createRoutedServerCore(registry,notifications,approvals,onThreadSubscribersEmpty,options={},userInput){return new RoutedServerCore(registry,notifications,approvals,onThreadSubscribersEmpty,options,userInput)}function registerLoadedThreadObjectListHandler(registry,threads){registry.register("thread/loaded/list",{scope:"thread",handler:context=>{let params=objectValue(context.request.params),cursor=decodeCursor(optionalString2(params.cursor)??null),limit=optionalNumber(params.limit)??Number.POSITIVE_INFINITY,loaded=threads.listLoaded().map(thread=>thread.id),data=loaded.slice(cursor,cursor+limit),nextOffset=cursor+data.length;return{data,nextCursor:nextOffset<loaded.length?encodeCursor(nextOffset):null}}})}var RoutedServerCore=class extends ServerCore{constructor(registry,notifications,approvals,onThreadSubscribersEmpty,options={},userInput){super({...options,registry}),this.notifications=notifications,this.approvals=approvals,this.userInput=userInput,this.onThreadSubscribersEmpty=onThreadSubscribersEmpty}addConnection(input){let connection=super.addConnection(input);return this.notifications.addConnection({id:connection.id,get initialized(){return connection.initialized},get transport(){return routerTransport(connection.transportKind)},get capabilities(){return connection.capabilities},get optOutNotificationMethods(){return[...connection.optOutNotificationMethods]},send:message=>connection.send(message),close:()=>{connection.close("slow-client")}}),connection}removeConnection(id){let emptiedThreadIds=this.notifications.removeConnection(id);super.removeConnection(id);for(let threadId of emptiedThreadIds)this.onThreadSubscribersEmpty?.(threadId)}async receive(connectionId2,envelope){let connection=this.getConnection(connectionId2);if(connection?.initialized&&this.userInput)try{if(envelope.kind==="notification"&&envelope.message.method===ADDITIVE_CLIENT_NOTIFICATION_METHODS[0]){this.userInput.progress(envelope.message.params);return}if(envelope.kind==="response"&&envelope.message.id!==null){let response={...envelope.message,id:envelope.message.id};if(this.userInput.resolveResponse(response)||this.resolveApproval(response))return;await connection.send({id:envelope.message.id,error:{code:-32600,message:"Unknown server request id"}});return}}catch(error){if(!(error instanceof UserInputProtocolError))throw error;await connection.send({id:envelope.kind==="response"?envelope.message.id:null,error:{code:-32602,message:error.message}});return}!this.userInput&&envelope.kind==="response"&&this.resolveApproval(envelope.message)||await super.receive(connectionId2,envelope)}resolveApproval(response){let id=response.id;return id===null?!1:"result"in response?this.approvals.resolveResponse({id,result:response.result}):this.approvals.resolveResponse({id,error:response.error})}},ModeTurnStore=class{constructor(threads){this.threads=threads}getLoadedThread(threadId){return new ModeTurnThreadEntry(this.threads.getLoadedThread(threadId))}runThreadTask(threadId,task){return this.threads.runThreadTask(threadId,task)}},ModeTurnThreadEntry=class{constructor(entry){this.entry=entry,this.sessionAdapter=new ModeTurnSession(entry.session)}get id(){return this.entry.id}get cwd(){return this.entry.cwd}get session(){return this.sessionAdapter}get activeTurn(){return this.entry.activeTurn}set activeTurn(value){this.entry.activeTurn=value}get status(){return this.entry.status}set status(value){this.entry.status=value}get updatedAt(){return this.entry.updatedAt}set updatedAt(value){this.entry.updatedAt=value}},ModeTurnSession=class{constructor(session){this.session=session}prompt(text,options){return this.session.prompt(text,options)}steer(text,options){return this.session.steer(text,void 0,{source:options?.source??"rpc"})}abort(){return this.session.abort()}subscribe(listener){return this.session.subscribe(listener)}};function objectParams2(request){if(typeof request.params=="object"&&request.params!==null&&!Array.isArray(request.params))return Object.fromEntries(Object.entries(request.params));throw new TurnEngineError({code:-32602,message:"Invalid params"})}function requiredStringParam(value,name){if(typeof value!="string"||value.length===0)throw new TurnEngineError({code:-32602,message:`Invalid params: ${name} is required`});return value}function optionalNullableStringParam(value,name){if(value==null)return value;if(typeof value!="string")throw new TurnEngineError({code:-32602,message:`Invalid params: ${name} must be a string`});return value}function userInputArrayParam(value){if(!Array.isArray(value))throw new TurnEngineError({code:-32602,message:"Invalid params: input must be an array"});return value.map(userInputParam)}function userInputParam(value){if(typeof value!="object"||value===null||Array.isArray(value))throw new TurnEngineError({code:-32602,message:"Invalid params: input item must be an object"});let item=Object.fromEntries(Object.entries(value)),type2=item.type;switch(type2){case"text":return{type:type2,text:requiredStringParam(item.text,"input.text"),text_elements:Array.isArray(item.text_elements)?item.text_elements:[]};case"image":return{type:type2,url:requiredStringParam(item.url,"input.url")};case"localImage":return{type:type2,path:requiredStringParam(item.path,"input.path")};case"skill":case"mention":return{type:type2,name:requiredStringParam(item.name,"input.name"),path:requiredStringParam(item.path,"input.path")};default:throw new TurnEngineError({code:-32602,message:"Invalid params: unsupported input item type"})}}function routerTransport(transport){switch(transport){case"stdio":return"stdio";case"websocket":return"ws";case"unix":return"unix"}}function createAppServerRuntime(requestShutdown){let notifications=new NotificationRouter,registry=createRegistry(),fuzzySearch=new FuzzyFileSearchService({broadcast:notification=>notifications.broadcast(notification)});registerFuzzyFileSearchMethods(registry,fuzzySearch);let threads,processMcpWireStatusAdapter=createProcessMcpWireStatusAdapter({agentDir:getAgentDir(),cwd:process.cwd(),env:process.env}),sendToSubscribers=(threadId,message)=>{let subscriberCount=0;try{subscriberCount=threads.getLoadedThread(threadId).subscribers.size}catch(error){if(error instanceof ThreadNotFoundError)return 0;throw error}return notifications.toThread(threadId,message),subscriberCount},approvals=new ApprovalBridge(sendToSubscribers),userInput=new UserInputBridge(sendToSubscribers),lifecycle;threads=new ThreadRegistry({agentDir:getAgentDir(),sessionDir:process.env[ENV_SESSION_DIR],createSession:options=>createBoundAppServerSession(options,approvals,notifications,requestShutdown,userInput,threadId=>{try{return threads.getLoadedThread(threadId).activeTurn?.turnId??"turn-user-input"}catch(error){if(error instanceof ThreadNotFoundError)return"turn-user-input";throw error}}),mcpWireStatusAdapter:processMcpWireStatusAdapter}),registerExtensionRequestMethod(registry,threadId=>threads.getLoadedThread(threadId).session);let core=createRoutedServerCore(registry,notifications,approvals,threadId=>{lifecycle?.scheduleIdleUnloadForThread(threadId)},{codexHome:getAgentDir(),serverCwd:process.cwd(),threads},userInput);registerAppServerSkillMethods(registry,{agentDir:getAgentDir(),serverCwd:process.cwd(),threads,resourceLoaderFactory:async cwd=>{let loader=new DefaultResourceLoader({cwd,agentDir:getAgentDir()});return await loader.reload(),loader}});let turnLog=new TurnLog,turns=createTurnEngine({store:createModeTurnStore(threads),turnLog,emitToThread:(threadId,notification)=>notifications.toThread(threadId,notification),broadcast:notification=>notifications.broadcast(notification)});return registerTurnHandlers(registry,turns,core),lifecycle=registerThreadLifecycleHandlers(registry,{threads,turnLog,notifications,deferUntilResponded:(connectionId2,action)=>core.deferUntilResponded(connectionId2,action),observeThread:threadId=>turns.observeThread(threadId),idleUnloadMinutes:30,replayPendingApprovals:threadId=>{approvals.replayPendingForThread(threadId),userInput.replayPendingForThread(threadId)}}),registerLoadedThreadObjectListHandler(registry,threads),{core,threads,turnLog,turns,dispose:()=>{for(let thread of threads.listLoaded())userInput.cancelPendingForThread(thread.id);fuzzySearch.dispose(),lifecycle?.dispose()}}}async function createBoundAppServerSession(options,approvals,notifications,requestShutdown,userInput,getTurnId){let result=await createAgentSession(options),threadId=result.session.sessionId,initialNotifications=[],bindingExtensions=!0;result.session.extensionRunner.onRpcEvent(({name,data})=>{let notification={method:"extension_event",params:{type:"extension_event",name,data,threadId}};if(bindingExtensions){initialNotifications.push(notification);return}notifications.toThread(threadId,notification)}),await result.session.bindExtensions({uiContext:createAppServerUIContext(approvals,threadId,userInput,()=>getTurnId(threadId)),mode:"app-server",shutdownHandler:()=>requestShutdown("extension shutdown"),onError:error=>{notifications.toThread(threadId,{method:"error",params:error})}}),bindingExtensions=!1;let mcpService=getMcpService(),mcpWireStatusAdapter=createMcpWireStatusAdapter(mcpService.getWireStatusSnapshot(threadId));return mcpWireStatusAdapter.bindLiveUpdates(mcpService.onWireStatusChanged((sessionId,snapshot)=>{sessionId===threadId&&mcpWireStatusAdapter.update(snapshot)})),result.session.subscribe(event=>{event.type==="agent_end"&&(approvals.cancelPendingForThread(threadId),userInput.cancelPendingForThread(threadId))}),{...result,initialNotifications,mcpWireStatusAdapter}}function registerTurnHandlers(registry,turns,core){let deferForResponse=async(connection,run)=>{let actions=[],result=await run(action=>(actions.push(action),!0));for(let action of actions)core.deferUntilResponded(connectionId(connection),action);return result};registry.register("turn/start",{scope:"thread",handler:context=>deferForResponse(context.connection,defer=>turns.startTurn(turnStartParams(context.request),defer))}),registry.register("turn/steer",{scope:"thread",handler:context=>turns.steerTurn(turnSteerParams(context.request))}),registry.register("turn/interrupt",{scope:"thread",handler:context=>deferForResponse(context.connection,defer=>turns.interruptTurn(turnInterruptParams(context.request),defer))})}function messageWithoutJsonrpc(message){return"method"in message?"id"in message?"params"in message?{id:message.id,method:message.method,params:message.params}:{id:message.id,method:message.method}:{method:message.method,..."params"in message?{params:message.params}:{},..."emittedAtMs"in message?{emittedAtMs:message.emittedAtMs}:{}}:"result"in message?{id:message.id,result:message.result}:{id:message.id,error:"data"in message.error?{code:message.error.code,message:message.error.message,data:message.error.data}:{code:message.error.code,message:message.error.message}}}function serializeNdjsonMessage(message){let serialized=JSON.stringify(messageWithoutJsonrpc(message));if(serialized===void 0)throw new Error("JSON-RPC message did not serialize to an object");if(serialized.includes(`
|
|
17
|
+
`,{encoding:"utf8",mode:384}),installationId}finally{markerCreated&&await releaseInstallationIdLock(lockPath,markerPath)}}async function reclaimAbandonedInstallationIdLock(lockPath){let metadata=await readInstallationIdLockMetadata(lockPath);metadata.kind==="missing"||!isReclaimableInstallationIdLock(metadata)||(metadata.markerPath!==void 0&&await unlinkInstallationIdLock(metadata.markerPath),await removeEmptyInstallationIdLock(lockPath))}async function removeEmptyInstallationIdLock(lockPath){try{await rmdir(lockPath)}catch(error){if(isNodeErrorCode(error,"ENOENT")||isNodeErrorCode(error,"ENOTDIR")||isNodeErrorCode(error,"ENOTEMPTY"))return;throw error}}function isReclaimableInstallationIdLock(metadata){switch(metadata.kind){case"abandoned":return!0;case"invalid":return Date.now()-metadata.mtimeMs>=INSTALLATION_ID_INVALID_LOCK_RECLAIM_MS;case"live":case"missing":return!1;default:return metadata}}async function readInstallationIdLockMetadata(lockPath){let lockStat;try{lockStat=await stat3(lockPath)}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};throw error}if(!lockStat.isDirectory())return readLegacyInstallationIdLockMetadata(lockPath,lockStat.mtimeMs);let entries;try{entries=await readdir4(lockPath)}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};if(isNodeErrorCode(error,"ENOTDIR"))return readLegacyInstallationIdLockMetadata(lockPath);throw error}let markerNames=entries.filter(entry=>entry.startsWith(INSTALLATION_ID_LOCK_MARKER_PREFIX)&&entry.endsWith(INSTALLATION_ID_LOCK_MARKER_SUFFIX));if(markerNames.length===0)return{kind:"invalid",mtimeMs:lockStat.mtimeMs};let invalidMarkerPath,abandonedMarkerPath;for(let markerName of markerNames){let markerPath=join8(lockPath,markerName),metadataResult=await readLockFile(markerPath);if(metadataResult.kind==="metadata"){if(isProcessAlive(metadataResult.metadata.pid))return{kind:"live",markerPath};abandonedMarkerPath??=markerPath;continue}metadataResult.kind==="invalid"&&(invalidMarkerPath??=markerPath)}return abandonedMarkerPath!==void 0?{kind:"abandoned",markerPath:abandonedMarkerPath}:invalidMarkerPath!==void 0?{kind:"invalid",mtimeMs:lockStat.mtimeMs,markerPath:invalidMarkerPath}:{kind:"missing"}}async function readLegacyInstallationIdLockMetadata(lockPath,knownMtimeMs){let metadataResult=await readLockFile(lockPath);if(metadataResult.kind==="missing")return{kind:"missing"};if(metadataResult.kind==="invalid"){if(knownMtimeMs!==void 0)return{kind:"invalid",mtimeMs:knownMtimeMs};try{return{kind:"invalid",mtimeMs:(await stat3(lockPath)).mtimeMs}}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};throw error}}return isProcessAlive(metadataResult.metadata.pid)?{kind:"live"}:{kind:"abandoned"}}async function readLockFile(lockPath){try{let parsed=JSON.parse((await readFile5(lockPath,"utf8")).trim());return parseInstallationIdLockMetadata(parsed)}catch(error){if(isNodeErrorCode(error,"ENOENT"))return{kind:"missing"};if(error instanceof SyntaxError||isNodeErrorCode(error,"EISDIR"))return{kind:"invalid"};throw error}}function parseInstallationIdLockMetadata(value){if(!isRecord15(value))return{kind:"invalid"};let{ownerToken,pid,createdAtMs}=value;return typeof ownerToken!="string"||ownerToken.length===0?{kind:"invalid"}:typeof pid!="number"||!Number.isSafeInteger(pid)||pid<=0?{kind:"invalid"}:typeof createdAtMs!="number"||!Number.isFinite(createdAtMs)||createdAtMs<0?{kind:"invalid"}:{kind:"metadata",metadata:{ownerToken,pid,createdAtMs}}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch(error){if(isNodeErrorCode(error,"ESRCH"))return!1;if(isNodeErrorCode(error,"EPERM"))return!0;throw error}}async function releaseInstallationIdLock(lockPath,markerPath){await unlinkInstallationIdLock(markerPath),await removeEmptyInstallationIdLock(lockPath)}async function unlinkInstallationIdLock(lockPath){try{await unlink(lockPath)}catch(error){if(!isNodeErrorCode(error,"ENOENT")&&!isNodeErrorCode(error,"ENOTDIR"))throw error}}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}function parseModelListParams(params){if(!isRecord15(params))return{};let cursor=params.cursor;if(cursor!=null&&typeof cursor!="string")throw new RpcHandlerError({code:-32600,message:"model/list received an invalid cursor"});let limit=params.limit;if(limit!=null&&(typeof limit!="number"||!Number.isInteger(limit)||limit<0||limit>4294967295))throw new RpcHandlerError({code:-32600,message:"model/list received an invalid limit"});let includeHidden=params.includeHidden;if(includeHidden!=null&&typeof includeHidden!="boolean")throw new RpcHandlerError({code:-32600,message:"model/list received an invalid includeHidden"});return{cursor,limit,includeHidden:includeHidden===!0}}function parseRemoteControlClientListParams(params){if(!isRecord15(params))throw invalidRemoteControlParams("remoteControl/client/list requires params");let environmentId=params.environmentId;if(typeof environmentId!="string")throw invalidRemoteControlParams("remoteControl/client/list requires a string environmentId");let cursor=params.cursor;if(cursor!=null&&typeof cursor!="string")throw invalidRemoteControlParams("remoteControl/client/list received an invalid cursor");let limit=params.limit;if(limit!=null&&(typeof limit!="number"||!Number.isInteger(limit)||limit<0||limit>4294967295))throw invalidRemoteControlParams("remoteControl/client/list received an invalid limit");let order=params.order;if(order!=null&&order!=="asc"&&order!=="desc")throw invalidRemoteControlParams("remoteControl/client/list received an invalid order");return{environmentId,cursor,limit,order}}function invalidRemoteControlParams(message){return new RpcHandlerError({code:-32600,message})}function isRecord15(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var ServerCore=class{constructor(options={}){this.connections=new Map,this.activeDispatch=new AsyncLocalStorage,this.registry=options.registry??createRegistry(),this.codexHome=options.codexHome??getAgentDir(),this.version=options.version??VERSION,this.now=options.now??Date.now,registerAppServerModelMethods(this.registry,{modelRegistry:options.modelRegistry,agentDir:this.codexHome}),registerAppServerCatalogMethods(this.registry,{modelRegistry:options.modelRegistry,agentDir:this.codexHome,serverCwd:options.serverCwd,threads:options.threads}),registerAppServerConfigMethods(this.registry,{agentDir:this.codexHome,serverCwd:options.serverCwd}),registerAppServerAccountMethods(this.registry,{agentDir:this.codexHome}),registerAppServerSkillMethods(this.registry,{agentDir:this.codexHome,serverCwd:options.serverCwd,threads:options.threads})}addConnection(input){let connection=createConnection(input);return this.connections.set(connection.id,connection),this.subscribeToProviderAccountEvents(),connection}removeConnection(id){this.connections.delete(id),this.connections.size===0&&(this.unsubscribeProviderAccountEvents?.(),this.unsubscribeProviderAccountEvents=void 0)}getConnection(id){return this.connections.get(id)}registerMethod(method,registration){this.registry.register(method,registration)}deferUntilResponded(connectionId2,action){let dispatch=this.activeDispatch.getStore();return dispatch?.connectionId!==connectionId2||!this.connections.has(connectionId2)?!1:(dispatch.actions.push(action),!0)}async receive(connectionId2,envelope){let connection=this.connections.get(connectionId2);if(connection)switch(envelope.kind){case"request":await this.respondToRequest(connection,envelope.message);return;case"notification":this.handleNotification(envelope.message);return;case"response":return;case"protocol-invalid":await connection.send({id:null,error:invalidRequestError()});return;default:return assertNever3(envelope)}}async sendNotificationToConnection(connectionId2,notification){let connection=this.connections.get(connectionId2);return!connection||!canDeliverNotification(connection,notification)?!1:(await connection.send(populateNotificationEnvelope(notification,this.now())),!0)}async broadcastNotification(notification){let delivered=0,envelope=populateNotificationEnvelope(notification,this.now());for(let connection of this.connections.values())canDeliverNotification(connection,envelope)&&(await connection.send(envelope),delivered+=1);return delivered}subscribeToProviderAccountEvents(){this.unsubscribeProviderAccountEvents||(this.unsubscribeProviderAccountEvents=subscribeProviderAccountEvents(event=>{let notification=providerAccountEventNotification(event),send=async()=>{await this.broadcastNotification(notification)},dispatch=this.activeDispatch.getStore();dispatch&&this.deferUntilResponded(dispatch.connectionId,send)||send()}))}async respondToRequest(connection,request){let dispatch={connectionId:connection.id,actions:[]},response=await this.activeDispatch.run(dispatch,()=>this.dispatchRequest(connection,request));try{await connection.send(response)}catch(error){throw dispatch.actions.length=0,error}if("error"in response||this.connections.get(connection.id)!==connection){dispatch.actions.length=0;return}let actions=dispatch.actions.splice(0);for(let action of actions)await action()}async dispatchRequest(connection,request){return request.method==="initialize"?this.handleInitialize(connection,request):this.registry.dispatch(connection,request)}handleInitialize(connection,request){if(connection.initialized)return{id:request.id,error:alreadyInitializedError()};let params=parseInitializeParams(request.params);if(!params)return{id:request.id,error:invalidParamsError()};let initialized=connection.initialize(params,this.version);switch(initialized.kind){case"initialized":return{id:request.id,result:buildInitializeResponse(connection,this.codexHome)};case"already-initialized":return{id:request.id,error:alreadyInitializedError()};default:return assertNever3(initialized)}}handleNotification(notification){notification.method}};function assertNever3(value){throw new Error(`Unhandled app-server envelope kind: ${JSON.stringify(value)}`)}function createModeTurnStore(threads){return new ModeTurnStore(threads)}function turnStartParams(request){let params=objectParams2(request);return{threadId:requiredStringParam(params.threadId,"threadId"),clientUserMessageId:optionalNullableStringParam(params.clientUserMessageId,"clientUserMessageId"),input:userInputArrayParam(params.input)}}function turnSteerParams(request){let params=objectParams2(request);return{threadId:requiredStringParam(params.threadId,"threadId"),expectedTurnId:requiredStringParam(params.expectedTurnId,"expectedTurnId"),clientUserMessageId:optionalNullableStringParam(params.clientUserMessageId,"clientUserMessageId"),input:userInputArrayParam(params.input)}}function turnInterruptParams(request){let params=objectParams2(request);return{threadId:requiredStringParam(params.threadId,"threadId"),turnId:requiredStringParam(params.turnId,"turnId")}}function createRoutedServerCore(registry,notifications,approvals,onThreadSubscribersEmpty,options={},userInput){return new RoutedServerCore(registry,notifications,approvals,onThreadSubscribersEmpty,options,userInput)}function registerLoadedThreadObjectListHandler(registry,threads){registry.register("thread/loaded/list",{scope:"thread",handler:context=>{let params=objectValue(context.request.params),cursor=decodeCursor(optionalString2(params.cursor)??null),limit=optionalNumber(params.limit)??Number.POSITIVE_INFINITY,loaded=threads.listLoaded().map(thread=>thread.id),data=loaded.slice(cursor,cursor+limit),nextOffset=cursor+data.length;return{data,nextCursor:nextOffset<loaded.length?encodeCursor(nextOffset):null}}})}var RoutedServerCore=class extends ServerCore{constructor(registry,notifications,approvals,onThreadSubscribersEmpty,options={},userInput){super({...options,registry}),this.notifications=notifications,this.approvals=approvals,this.userInput=userInput,this.onThreadSubscribersEmpty=onThreadSubscribersEmpty}addConnection(input){let connection=super.addConnection(input);return this.notifications.addConnection({id:connection.id,get initialized(){return connection.initialized},get transport(){return routerTransport(connection.transportKind)},get capabilities(){return connection.capabilities},get optOutNotificationMethods(){return[...connection.optOutNotificationMethods]},send:message=>connection.send(message),close:()=>{connection.close("slow-client")}}),connection}removeConnection(id){let emptiedThreadIds=this.notifications.removeConnection(id);super.removeConnection(id);for(let threadId of emptiedThreadIds)this.onThreadSubscribersEmpty?.(threadId)}async receive(connectionId2,envelope){let connection=this.getConnection(connectionId2);if(connection?.initialized&&this.userInput)try{if(envelope.kind==="notification"&&envelope.message.method===ADDITIVE_CLIENT_NOTIFICATION_METHODS[0]){this.userInput.progress(envelope.message.params);return}if(envelope.kind==="response"&&envelope.message.id!==null){let response={...envelope.message,id:envelope.message.id};if(this.userInput.resolveResponse(response)||this.resolveApproval(response))return;await connection.send({id:envelope.message.id,error:{code:-32600,message:"Unknown server request id"}});return}}catch(error){if(!(error instanceof UserInputProtocolError))throw error;await connection.send({id:envelope.kind==="response"?envelope.message.id:null,error:{code:-32602,message:error.message}});return}!this.userInput&&envelope.kind==="response"&&this.resolveApproval(envelope.message)||await super.receive(connectionId2,envelope)}resolveApproval(response){let id=response.id;return id===null?!1:"result"in response?this.approvals.resolveResponse({id,result:response.result}):this.approvals.resolveResponse({id,error:response.error})}},ModeTurnStore=class{constructor(threads){this.threads=threads}getLoadedThread(threadId){return new ModeTurnThreadEntry(this.threads.getLoadedThread(threadId))}runThreadTask(threadId,task){return this.threads.runThreadTask(threadId,task)}},ModeTurnThreadEntry=class{constructor(entry){this.entry=entry,this.sessionAdapter=new ModeTurnSession(entry.session)}get id(){return this.entry.id}get cwd(){return this.entry.cwd}get session(){return this.sessionAdapter}get activeTurn(){return this.entry.activeTurn}set activeTurn(value){this.entry.activeTurn=value}get status(){return this.entry.status}set status(value){this.entry.status=value}get updatedAt(){return this.entry.updatedAt}set updatedAt(value){this.entry.updatedAt=value}},ModeTurnSession=class{constructor(session){this.session=session}prompt(text,options){return this.session.prompt(text,options)}steer(text,options){return this.session.steer(text,void 0,{source:options?.source??"rpc"})}abort(){return this.session.abort()}subscribe(listener){return this.session.subscribe(listener)}};function objectParams2(request){if(typeof request.params=="object"&&request.params!==null&&!Array.isArray(request.params))return Object.fromEntries(Object.entries(request.params));throw new TurnEngineError({code:-32602,message:"Invalid params"})}function requiredStringParam(value,name){if(typeof value!="string"||value.length===0)throw new TurnEngineError({code:-32602,message:`Invalid params: ${name} is required`});return value}function optionalNullableStringParam(value,name){if(value==null)return value;if(typeof value!="string")throw new TurnEngineError({code:-32602,message:`Invalid params: ${name} must be a string`});return value}function userInputArrayParam(value){if(!Array.isArray(value))throw new TurnEngineError({code:-32602,message:"Invalid params: input must be an array"});return value.map(userInputParam)}function userInputParam(value){if(typeof value!="object"||value===null||Array.isArray(value))throw new TurnEngineError({code:-32602,message:"Invalid params: input item must be an object"});let item=Object.fromEntries(Object.entries(value)),type2=item.type;switch(type2){case"text":return{type:type2,text:requiredStringParam(item.text,"input.text"),text_elements:Array.isArray(item.text_elements)?item.text_elements:[]};case"image":return{type:type2,url:requiredStringParam(item.url,"input.url")};case"localImage":return{type:type2,path:requiredStringParam(item.path,"input.path")};case"skill":case"mention":return{type:type2,name:requiredStringParam(item.name,"input.name"),path:requiredStringParam(item.path,"input.path")};default:throw new TurnEngineError({code:-32602,message:"Invalid params: unsupported input item type"})}}function routerTransport(transport){switch(transport){case"stdio":return"stdio";case"websocket":return"ws";case"unix":return"unix"}}function createAppServerRuntime(requestShutdown,runtimeOptions={}){let additionalExtensionPaths=[...runtimeOptions.extensionPaths??[]],notifications=new NotificationRouter,registry=createRegistry(),fuzzySearch=new FuzzyFileSearchService({broadcast:notification=>notifications.broadcast(notification)});registerFuzzyFileSearchMethods(registry,fuzzySearch);let threads,processMcpWireStatusAdapter=createProcessMcpWireStatusAdapter({agentDir:getAgentDir(),cwd:process.cwd(),env:process.env}),sendToSubscribers=(threadId,message)=>{let subscriberCount=0;try{subscriberCount=threads.getLoadedThread(threadId).subscribers.size}catch(error){if(error instanceof ThreadNotFoundError)return 0;throw error}return notifications.toThread(threadId,message),subscriberCount},approvals=new ApprovalBridge(sendToSubscribers),userInput=new UserInputBridge(sendToSubscribers),lifecycle;threads=new ThreadRegistry({agentDir:getAgentDir(),sessionDir:process.env[ENV_SESSION_DIR],createSession:async options=>createBoundAppServerSession(await withExtensionPaths(options,additionalExtensionPaths),approvals,notifications,requestShutdown,userInput,threadId=>{try{return threads.getLoadedThread(threadId).activeTurn?.turnId??"turn-user-input"}catch(error){if(error instanceof ThreadNotFoundError)return"turn-user-input";throw error}}),mcpWireStatusAdapter:processMcpWireStatusAdapter}),registerExtensionRequestMethod(registry,threadId=>threads.getLoadedThread(threadId).session);let core=createRoutedServerCore(registry,notifications,approvals,threadId=>{lifecycle?.scheduleIdleUnloadForThread(threadId)},{codexHome:getAgentDir(),serverCwd:process.cwd(),threads},userInput);registerAppServerSkillMethods(registry,{agentDir:getAgentDir(),serverCwd:process.cwd(),threads,resourceLoaderFactory:async cwd=>{let loader=new DefaultResourceLoader({cwd,agentDir:getAgentDir(),additionalExtensionPaths});return await loader.reload(),loader}});let turnLog=new TurnLog,turns=createTurnEngine({store:createModeTurnStore(threads),turnLog,emitToThread:(threadId,notification)=>notifications.toThread(threadId,notification),broadcast:notification=>notifications.broadcast(notification)});return registerTurnHandlers(registry,turns,core),lifecycle=registerThreadLifecycleHandlers(registry,{threads,turnLog,notifications,deferUntilResponded:(connectionId2,action)=>core.deferUntilResponded(connectionId2,action),observeThread:threadId=>turns.observeThread(threadId),idleUnloadMinutes:30,replayPendingApprovals:threadId=>{approvals.replayPendingForThread(threadId),userInput.replayPendingForThread(threadId)}}),registerLoadedThreadObjectListHandler(registry,threads),{core,threads,turnLog,turns,dispose:()=>{for(let thread of threads.listLoaded())userInput.cancelPendingForThread(thread.id);fuzzySearch.dispose(),lifecycle?.dispose()}}}async function withExtensionPaths(options,additionalExtensionPaths){if(additionalExtensionPaths.length===0||options.resourceLoader)return options;let cwd=options.cwd??options.sessionManager?.getCwd()??process.cwd(),agentDir=options.agentDir??getAgentDir(),settingsManager=options.settingsManager??SettingsManager.create(cwd,agentDir),resourceLoader=new DefaultResourceLoader({cwd,agentDir,settingsManager,additionalExtensionPaths});return await resourceLoader.reload(),{...options,settingsManager,resourceLoader}}async function createBoundAppServerSession(options,approvals,notifications,requestShutdown,userInput,getTurnId){let result=await createAgentSession(options),threadId=result.session.sessionId,initialNotifications=[],bindingExtensions=!0;result.session.extensionRunner.onRpcEvent(({name,data})=>{let notification={method:"extension_event",params:{type:"extension_event",name,data,threadId}};if(bindingExtensions){initialNotifications.push(notification);return}notifications.toThread(threadId,notification)}),await result.session.bindExtensions({uiContext:createAppServerUIContext(approvals,threadId,userInput,()=>getTurnId(threadId)),mode:"app-server",shutdownHandler:()=>requestShutdown("extension shutdown"),onError:error=>{notifications.toThread(threadId,{method:"error",params:error})}}),bindingExtensions=!1;let mcpService=getMcpService(),mcpWireStatusAdapter=createMcpWireStatusAdapter(mcpService.getWireStatusSnapshot(threadId));return mcpWireStatusAdapter.bindLiveUpdates(mcpService.onWireStatusChanged((sessionId,snapshot)=>{sessionId===threadId&&mcpWireStatusAdapter.update(snapshot)})),result.session.subscribe(event=>{event.type==="agent_end"&&(approvals.cancelPendingForThread(threadId),userInput.cancelPendingForThread(threadId))}),{...result,initialNotifications,mcpWireStatusAdapter}}function registerTurnHandlers(registry,turns,core){let deferForResponse=async(connection,run)=>{let actions=[],result=await run(action=>(actions.push(action),!0));for(let action of actions)core.deferUntilResponded(connectionId(connection),action);return result};registry.register("turn/start",{scope:"thread",handler:context=>deferForResponse(context.connection,defer=>turns.startTurn(turnStartParams(context.request),defer))}),registry.register("turn/steer",{scope:"thread",handler:context=>turns.steerTurn(turnSteerParams(context.request))}),registry.register("turn/interrupt",{scope:"thread",handler:context=>deferForResponse(context.connection,defer=>turns.interruptTurn(turnInterruptParams(context.request),defer))})}function messageWithoutJsonrpc(message){return"method"in message?"id"in message?"params"in message?{id:message.id,method:message.method,params:message.params}:{id:message.id,method:message.method}:{method:message.method,..."params"in message?{params:message.params}:{},..."emittedAtMs"in message?{emittedAtMs:message.emittedAtMs}:{}}:"result"in message?{id:message.id,result:message.result}:{id:message.id,error:"data"in message.error?{code:message.error.code,message:message.error.message,data:message.error.data}:{code:message.error.code,message:message.error.message}}}function serializeNdjsonMessage(message){let serialized=JSON.stringify(messageWithoutJsonrpc(message));if(serialized===void 0)throw new Error("JSON-RPC message did not serialize to an object");if(serialized.includes(`
|
|
18
18
|
`))throw new Error("JSON-RPC NDJSON payload must not contain a raw newline");return`${serialized}
|
|
19
19
|
`}function parseNdjsonLine(line){try{return classifyIncoming(JSON.parse(line))}catch(error){if(error instanceof SyntaxError)return{kind:"parse-error",message:{id:null,error:{code:-32700,message:"Parse error"}}};throw error}}function attachNdjsonRpcReader(stream,onMessage){return attachJsonlLineReader(stream,line=>{onMessage(parseNdjsonLine(line))})}var DEFAULT_CONNECTION_ID="stdio",activeTransport;function startStdioTransport(options){if(activeTransport)throw new Error("stdio transport already active");takeOverStdout();let transport=new StdioTransportImpl(options);return activeTransport=transport,transport}var StdioTransportImpl=class{constructor(options){this.inputTail=Promise.resolve(),this.closed=!1,this.shutdownRequested=!1,this.handleStdinEnd=()=>{this.requestShutdown("stdin ended")},this.handleStdinClose=()=>{this.requestShutdown("stdin closed")},this.core=options.core,this.stdin=options.stdin??process.stdin,this.connectionId=options.connectionId??DEFAULT_CONNECTION_ID,this.onShutdown=options.onShutdown,this.connection=this.core.addConnection({id:this.connectionId,transportKind:"stdio",send:message=>this.writeMessage(message),close:reason=>this.close(reason)}),this.detachReader=attachNdjsonRpcReader(this.stdin,message=>{this.enqueue(()=>this.handleMessage(message))}),this.stdin.on("end",this.handleStdinEnd),this.stdin.on("close",this.handleStdinClose)}async drain(){await this.inputTail}async close(reason){this.closeCore(reason),await this.inputTail}enqueue(task){let next=this.inputTail.then(task,task);this.inputTail=next.catch(error=>{this.reportError(error)})}requestShutdown(reason){this.shutdownRequested||(this.shutdownRequested=!0,this.enqueue(async()=>{this.closeCore(reason),await this.onShutdown?.(reason)}))}async handleMessage(message){switch(message.kind){case"parse-error":await this.connection.send(message.message);return;case"request":case"notification":case"response":case"protocol-invalid":await this.core.receive(this.connectionId,message);return;default:return assertNever4(message)}}async writeMessage(message){writeRawStdout(serializeNdjsonMessage(message)),await waitForRawStdoutBackpressure()}closeCore(reason){this.closed||(this.closed=!0,this.detachReader(),this.stdin.off("end",this.handleStdinEnd),this.stdin.off("close",this.handleStdinClose),this.core.removeConnection(this.connectionId),activeTransport===this&&(activeTransport=void 0),process.stderr.write(`app-server stdio closed: ${reason}
|
|
20
20
|
`))}reportError(error){let message=error instanceof Error?error.stack??error.message:String(error);process.stderr.write(`app-server stdio error: ${message}
|
|
@@ -30,11 +30,11 @@ Content-Length: 0\r
|
|
|
30
30
|
`),socket.destroy()}var SOCKET_PATH_BYTE_LIMIT=100,AppServerUnixSocketListenError=class extends Error{constructor(message){super(message),this.exitCode=2,this.name="AppServerUnixSocketListenError"}};async function startAppServerUnixSocketListener(options={}){let defaultSocketPath=join9(getAgentDir(),"app-server","app-server.sock"),socketPath=options.socketPath??defaultSocketPath;validateSocketPath(socketPath),await prepareSocketPath(socketPath,socketPath===defaultSocketPath&&(options.auth===void 0||options.auth.kind==="off"));let auth=await resolveWebSocketListenerAuth({auth:options.auth,stderr:options.stderr??process.stderr,tokenLogLabel:"app-server unix socket websocket token"}),core=options.core??new ServerCore,handler=createAppServerWebSocketConnectionHandler({core,auth,transportKind:"unix",connectionIdPrefix:"unix",outboundQueueBytes:options.outboundQueueBytes}),server=createServer();return server.on("upgrade",handler.handleUpgrade),await listen(server,socketPath),{socketPath,core,tokenFile:auth.kind==="bearer"?auth.path:void 0,get connectionCount(){return handler.connectionCount},async close(){handler.terminateConnections(),await closeServer(server),await handler.close(),await removeSocketPath(socketPath)}}}function validateSocketPath(socketPath){if(!(Buffer.byteLength(socketPath)<=SOCKET_PATH_BYTE_LIMIT))throw new AppServerUnixSocketListenError(`Unix socket path is too long for portable app-server startup: ${socketPath}. pass a shorter unix:///path.`)}async function prepareSocketPath(socketPath,enforceOwnerOnlyDirectory){let socketDirectory=dirname4(socketPath);await mkdir4(socketDirectory,{recursive:!0,mode:448}),enforceOwnerOnlyDirectory&&await chmod2(socketDirectory,448);try{await access(socketPath)}catch(error){if(isNodeErrorCode3(error,"ENOENT"))return;throw error}if(await probeLiveSocket(socketPath))throw new AppServerUnixSocketListenError(`${socketPath}: address already in use by a live server.`);await unlink2(socketPath)}function probeLiveSocket(socketPath){return new Promise(resolve4=>{let socket=createConnection2(socketPath),settled=!1,settle=result=>{settled||(settled=!0,socket.destroy(),resolve4(result))};socket.once("connect",()=>settle(!0)),socket.once("error",()=>settle(!1)),socket.setTimeout(1e3,()=>settle(!1))})}function listen(server,socketPath){return new Promise((resolve4,reject)=>{server.once("error",reject),server.listen(socketPath,()=>{server.off("error",reject),resolve4()})})}async function removeSocketPath(socketPath){try{await unlink2(socketPath)}catch(error){if(isNodeErrorCode3(error,"ENOENT"))return;throw error}}function isNodeErrorCode3(error,code){return error instanceof Error&&"code"in error&&error.code===code}import{createServer as createServer2}from"node:http";import{isIP as isIP2}from"node:net";import{join as join10}from"node:path";var AppServerWebSocketListenError=class extends Error{constructor(message){super(message),this.exitCode=2,this.name="AppServerWebSocketListenError"}};async function startAppServerWebSocketListener(options){let auth=await resolveWebSocketListenerAuth({auth:options.auth,stderr:options.stderr??process.stderr,defaultTokenPath:join10(getAgentDir(),"app-server","ws-token"),tokenLogLabel:"app-server websocket token"});if(auth.kind==="off"&&!isLoopbackHost(options.host))throw new AppServerWebSocketListenError("Refusing unauthenticated app-server websocket on non-loopback host.");let core=options.core??new ServerCore,handler=createAppServerWebSocketConnectionHandler({core,auth,transportKind:"websocket",connectionIdPrefix:"ws",outboundQueueBytes:options.outboundQueueBytes}),accepting=!0,server=createServer2((request,response)=>handleHttpRequest(request,response,accepting));server.on("upgrade",handler.handleUpgrade),await listen2(server,options.host,options.port);let address=server.address();if(!address||typeof address=="string")throw new AppServerWebSocketListenError("Websocket listener did not bind to a TCP address.");return{host:options.host,port:address.port,core,tokenFile:auth.kind==="bearer"?auth.path:void 0,get connectionCount(){return handler.connectionCount},async close(){accepting=!1,handler.terminateConnections(),await closeServer(server),await handler.close()}}}function handleHttpRequest(request,response,accepting){if(hasOrigin2(request)){writeText(response,403,`forbidden
|
|
31
31
|
`);return}if((request.url==="/readyz"||request.url==="/healthz")&&accepting){writeText(response,200,`ok
|
|
32
32
|
`);return}writeText(response,400,`websocket upgrade required
|
|
33
|
-
`)}function hasOrigin2(request){return request.headers.origin!==void 0}function writeText(response,statusCode,body){response.writeHead(statusCode,{"content-type":"text/plain; charset=utf-8"}).end(body)}function listen2(server,host,port){return new Promise((resolve4,reject)=>{server.once("error",reject),server.listen(port,host,()=>{server.off("error",reject),resolve4()})})}function isLoopbackHost(host){return host==="::1"||isIP2(host)===4&&host.startsWith("127.")}import{
|
|
34
|
-
|
|
35
|
-
`,{mode:384}),await
|
|
36
|
-
|
|
37
|
-
${stderr}`:message}catch(error){if(isNodeErrorCode6(error,"ENOENT"))return message;throw error}}async function readPidFile(paths){try{return parseDaemonPidFile(await readFile8(paths.pidFile,"utf8"))}catch(error){if(isNodeErrorCode6(error,"ENOENT"))return;throw error}}function resolveCliMainPath(){let modulePath=fileURLToPath(import.meta.url),extension=modulePath.endsWith(".ts")?".ts":".js";return resolve3(dirname6(modulePath),"..","..",`cli-main${extension}`)}function isNodeErrorCode6(error,code){return error instanceof Error&&"code"in error&&error.code===code}async function runAppServerMode(options){let shutdownRequested=!1,forceExit=!1,resolveShutdown=()=>{},shutdownSignal=new Promise(resolve4=>{resolveShutdown=resolve4}),requestShutdown=reason=>{if(shutdownRequested){forceExit||(forceExit=!0,process.exit(1));return}shutdownRequested=!0,resolveShutdown(reason)},handleSignal=signal=>{requestShutdown(signal)};process.on("SIGINT",handleSignal),process.on("SIGTERM",handleSignal);let runtime=createAppServerRuntime(requestShutdown),stdio,unix,websocket;try{options.listen.kind==="stdio"?(stdio=startStdioTransport({core:runtime.core,onShutdown:requestShutdown}),process.stderr.write(`senpi app-server listening on stdio://
|
|
33
|
+
`)}function hasOrigin2(request){return request.headers.origin!==void 0}function writeText(response,statusCode,body){response.writeHead(statusCode,{"content-type":"text/plain; charset=utf-8"}).end(body)}function listen2(server,host,port){return new Promise((resolve4,reject)=>{server.once("error",reject),server.listen(port,host,()=>{server.off("error",reject),resolve4()})})}function isLoopbackHost(host){return host==="::1"||isIP2(host)===4&&host.startsWith("127.")}import{mkdir as mkdir5,readFile as readFile8}from"node:fs/promises";import{dirname as dirname6,join as join12,resolve as resolve3}from"node:path";import{fileURLToPath}from"node:url";import{createServer as createServer3}from"node:net";import{readFile as readFile7,rm as rm3}from"node:fs/promises";import{dirname as dirname5,join as join11}from"node:path";async function probeListen(paths,listen3,timeoutMs,signal){if(listen3.kind==="stdio"||signal?.aborted)return;let token;try{token=(await readFile7(paths.tokenFile,"utf8")).trim()}catch(error){if(isNodeErrorCode4(error,"ENOENT")&&listen3.kind==="ws")return;if(!isNodeErrorCode4(error,"ENOENT"))throw error;token=void 0}let probeTarget=listen3.kind==="ws"?listen3.url:`ws+unix://${listen3.path??join11(dirname5(paths.tokenFile),"app-server.sock")}:/`;return probeWebSocket(probeTarget,token&&token.length>0?token:void 0,timeoutMs,signal)}function probeWebSocket(url,token,timeoutMs,signal){return new Promise(resolveProbe=>{if(signal?.aborted){resolveProbe(void 0);return}let settled=!1,socket=new wrapper_default(url,token?{headers:{Authorization:`Bearer ${token}`}}:void 0),finish=result=>{settled||(settled=!0,clearTimeout(timeout),signal?.removeEventListener("abort",onAbort),socket.terminate(),resolveProbe(result))},onAbort=()=>finish(void 0),timeout=setTimeout(()=>finish(void 0),timeoutMs);signal?.addEventListener("abort",onAbort,{once:!0}),socket.once("open",()=>{socket.send(JSON.stringify({id:1,method:"initialize",params:{clientInfo:{name:"senpi_app_server_daemon",title:"senpi app-server daemon",version:VERSION}}}))}),socket.once("message",(data,isBinary)=>{if(isBinary){finish(void 0);return}finish(readInitializeProbe(data.toString("utf8")))}),socket.on("error",()=>finish(void 0)),socket.once("close",()=>finish(void 0))})}async function pollProbe(paths,listen3,timeoutMs,signal){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){if(signal?.aborted)return;let probe=await probeListen(paths,listen3,2e3,signal);if(probe)return probe;if(signal?.aborted)return;await delay2(100,signal)}}function readInitializeProbe(text){let parsed;try{parsed=JSON.parse(text)}catch(error){if(error instanceof SyntaxError)return;throw error}if(!isRecord16(parsed)||parsed.id!==1||!isRecord16(parsed.result))return;let userAgent=parsed.result.userAgent;return typeof userAgent=="string"?userAgent:void 0}async function readSettings(paths){try{let parsed=JSON.parse(await readFile7(paths.settingsFile,"utf8"));if(!isRecord16(parsed))return;let listen3=parseListen2(parsed.listen),extensions=Array.isArray(parsed.extensions)?parsed.extensions.filter(value=>typeof value=="string"):[];return listen3?{listen:listen3,extensions}:void 0}catch(error){if(isNodeErrorCode4(error,"ENOENT")||error instanceof SyntaxError)return;throw error}}function parseListen2(value){if(!(!isRecord16(value)||typeof value.kind!="string"||typeof value.url!="string")){if(value.kind==="stdio"&&value.url==="stdio://")return{kind:"stdio",url:"stdio://"};if(value.kind==="unix")return typeof value.path=="string"?{kind:"unix",url:value.url,path:value.path}:{kind:"unix",url:value.url};if(value.kind==="ws"&&typeof value.host=="string"&&typeof value.port=="number")return{kind:"ws",url:value.url,host:value.host,port:value.port}}}async function cleanupState(paths,listen3){await rm3(paths.pidFile,{force:!0}),await rm3(paths.settingsFile,{force:!0}),listen3.kind==="unix"&&listen3.path&&await rm3(listen3.path,{force:!0})}function runningOutput(status,pid,listen3,version){return pid===void 0?runningUnmanagedOutput(listen3,version):{status,pid,listen:listen3.url,version}}function runningUnmanagedOutput(listen3,version){return{status:"running-unmanaged",listen:listen3.url,version}}function delay2(ms,signal){return new Promise(resolveDelay=>{if(signal?.aborted){resolveDelay();return}let finish=()=>{clearTimeout(timeout),signal?.removeEventListener("abort",finish),resolveDelay()},timeout=setTimeout(finish,ms);signal?.addEventListener("abort",finish,{once:!0})})}function isRecord16(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isNodeErrorCode4(error,code){return error instanceof Error&&"code"in error&&error.code===code}var AppServerDaemonAddressInUseError=class extends Error{constructor(listen3){super(`EADDRINUSE: app-server daemon cannot listen on ${listen3.url}; the TCP address is occupied by a listener that did not answer initialize.`),this.code="EADDRINUSE",this.name="AppServerDaemonAddressInUseError"}};async function inspectAppServerListenOccupancy(paths,listen3){let version=await probeListen(paths,listen3,2e3);return version?{kind:"app-server",version}:(listen3.kind==="ws"&&await assertTcpAddressAvailable(listen3),{kind:"available"})}async function assertTcpAddressAvailable(listen3){let server=createServer3();try{await listenServer(server,listen3.host,listen3.port)}catch(error){throw isNodeErrorCode5(error,"EADDRINUSE")?new AppServerDaemonAddressInUseError(listen3):error}finally{server.listening&&await closeServer2(server)}}function listenServer(server,host,port){return new Promise((resolveListen,rejectListen)=>{let onError=error=>{server.off("listening",onListening),rejectListen(error)},onListening=()=>{server.off("error",onError),resolveListen()};server.once("error",onError),server.once("listening",onListening),server.listen(port,host)})}function closeServer2(server){return new Promise((resolveClose,rejectClose)=>{server.close(error=>{if(error){rejectClose(error);return}resolveClose()})})}function isNodeErrorCode5(error,code){return error instanceof Error&&"code"in error&&error.code===code}import{spawn}from"node:child_process";import{open,writeFile as writeFile4}from"node:fs/promises";async function spawnDaemon(paths,intent,cliMainPath){let{listen:listen3,extensions}=intent,stderr=await open(paths.stderrLog,"w");try{let daemonExec=process.versions.bun?process.env.npm_node_execpath&&!/[/\\]bun(?:$|[/\\])/.test(process.env.npm_node_execpath)?process.env.npm_node_execpath:"/opt/homebrew/bin/node":process.execPath,child=spawn(daemonExec,[...process.versions.bun?[]:process.execArgv,cliMainPath,"app-server","--listen",listen3.url,...extensions.flatMap(extension=>["--extension",extension])],{detached:!0,windowsHide:!0,env:{...process.env,SENPI_RUNTIME:"node"},stdio:["ignore","ignore",stderr.fd]}),exited=observeDaemonExit(child),pid=child.pid;if(pid===void 0)throw new Error("failed to spawn daemon process");let startTime;try{let resolved=await Promise.race([waitForStartTime(pid,1e4),exited.then(()=>{throw new Error(`spawned daemon ${pid} exited before its start time could be read`)})])??await readProcessStartTime(pid,process.platform,15e3).catch(()=>{});if(resolved===void 0)throw new Error(`spawned daemon ${pid} started but its process identity stayed unreadable`);startTime=resolved}catch(error){if(child.exitCode===null&&child.signalCode===null){try{child.kill("SIGTERM")}catch{}if(await Promise.race([exited,delay3(2e3)]),child.exitCode===null&&child.signalCode===null)try{child.kill("SIGKILL")}catch{}}throw await cleanupState(paths,listen3),error}try{await writeFile4(paths.pidFile,`${JSON.stringify({pid,processStartTime:startTime})}
|
|
34
|
+
`,{mode:384}),await writeFile4(paths.settingsFile,`${JSON.stringify({listen:listen3,extensions})}
|
|
35
|
+
`,{mode:384})}catch(error){if(child.exitCode===null&&child.signalCode===null){try{child.kill("SIGTERM")}catch{}if(await Promise.race([exited,delay3(2e3)]),child.exitCode===null&&child.signalCode===null){try{child.kill("SIGKILL")}catch(killError){throw new Error(`failed to terminate daemon after registration failure: ${killError instanceof Error?killError.message:String(killError)}`)}if(!await Promise.race([exited.then(()=>!0),delay3(2e3).then(()=>!1)]))throw new Error(`daemon ${pid} remained alive after SIGKILL during registration failure`)}}throw await cleanupState(paths,{...listen3,...listen3.kind==="unix"?{path:void 0}:{}}),error}return child.unref(),{pid,exited}}finally{await stderr.close()}}function delay3(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function observeDaemonExit(child){return new Promise(resolveExit=>{child.once("error",error=>resolveExit({kind:"error",error})),child.once("exit",(code,signal)=>resolveExit({kind:"exit",code,signal}))})}var lockOptions={retries:{retries:100,minTimeout:20,maxTimeout:100}};function createDaemonPaths(agentDir=getAgentDir()){let dir=join12(agentDir,"app-server-daemon");return{dir,pidFile:join12(dir,"app-server.pid"),lockFile:join12(dir,"daemon.lock"),settingsFile:join12(dir,"settings.json"),stderrLog:join12(dir,"stderr.log"),tokenFile:join12(agentDir,"app-server","ws-token")}}async function withDaemonStateLock(paths,task){await mkdir5(paths.dir,{recursive:!0});let release2=await acquireOwnershipSafeLock(paths.lockFile,lockOptions);try{return await task()}finally{await release2()}}async function runAppServerDaemonCommand(options){let paths=createDaemonPaths(),output=await withDaemonStateLock(paths,async()=>{try{return await runLockedDaemonCommand(options,paths)}catch(error){return process.exitCode=1,{status:"error",message:error instanceof Error?error.message:String(error)}}});process.stdout.write(`${JSON.stringify(output)}
|
|
36
|
+
`)}async function runLockedDaemonCommand(options,paths){let settings=await readSettings(paths),listen3=options.verb==="start"?options.listen:settings?.listen??options.listen,extensions=resolveAppServerExtensionPaths(options.extensions);switch(options.verb){case"start":return startDaemon(paths,{listen:listen3,extensions});case"stop":return stopDaemon(paths,listen3);case"status":return statusDaemon(paths,listen3);case"restart":{let restartListen=settings?.listen??options.listen;return await stopDaemon(paths,restartListen),startDaemon(paths,{listen:restartListen,extensions:extensions.length>0?extensions:settings?.extensions??[]})}}}async function startDaemon(paths,intent){let{listen:listen3}=intent,occupancy=await inspectAppServerListenOccupancy(paths,listen3);if(occupancy.kind==="app-server"){let pidFile2=await readPidFile(paths),pid=pidFile2&&await processMatchesPidFile(pidFile2)?pidFile2.pid:void 0;return pid!==void 0?runningOutput("already-running",pid,listen3,occupancy.version):{status:"already-running",listen:listen3.url,version:occupancy.version}}let pidFile=await readPidFile(paths);if(pidFile&&await processMatchesPidFile(pidFile)){let lateProbe=await pollProbe(paths,listen3,1e4);if(lateProbe)return runningOutput("already-running",pidFile.pid,listen3,lateProbe);throw new Error(`managed daemon pid ${pidFile.pid} did not answer initialize`)}let spawned=await spawnDaemon(paths,intent,resolveCliMainPath()),readiness=await waitForDaemonReady(paths,listen3,spawned.exited);if(readiness.kind==="ready")return{status:"started",pid:spawned.pid,listen:listen3.url};await stopValidatedPid({pid:spawned.pid,processStartTime:await readProcessStartTime(spawned.pid)??""},"SIGTERM");let message=readiness.kind==="exited"?`spawned daemon ${describeDaemonExit(readiness.exit)} before answering initialize`:"spawned daemon did not answer initialize within 10s",diagnostic=await appendDaemonStderr(paths,message);throw await cleanupState(paths,listen3),new Error(diagnostic)}async function stopDaemon(paths,listen3){let pidFile=await readPidFile(paths);return pidFile?await processMatchesPidFile(pidFile)?(await stopValidatedPid(pidFile,"SIGTERM"),await processMatchesPidFile(pidFile)&&await stopValidatedPid(pidFile,"SIGKILL"),await waitForGone(pidFile,1e4),await cleanupState(paths,listen3),{status:"stopped"}):(await cleanupState(paths,listen3),{status:"not-running"}):(await probeListen(paths,listen3,2e3)||await cleanupState(paths,listen3),{status:"not-running"})}async function statusDaemon(paths,listen3){let probe=await probeListen(paths,listen3,2e3),pidFile=await readPidFile(paths),pidMatches=pidFile?await processMatchesPidFile(pidFile):!1;return probe&&pidFile&&pidMatches?runningOutput("running",pidFile.pid,listen3,probe):probe?runningUnmanagedOutput(listen3,probe):(pidFile&&!pidMatches&&await cleanupState(paths,listen3),{status:"not-running"})}async function waitForDaemonReady(paths,listen3,exited){let controller=new AbortController;try{let probe=pollProbe(paths,listen3,1e4,controller.signal).then(version=>version?{kind:"ready",version}:{kind:"timed-out"}),childExit=exited.then(exit=>({kind:"exited",exit}));return await Promise.race([probe,childExit])}finally{controller.abort()}}function describeDaemonExit(exit){return exit.kind==="error"?`failed to launch (${exit.error.message})`:exit.signal?`was terminated by ${exit.signal}`:`exited with code ${exit.code??"unknown"}`}async function appendDaemonStderr(paths,message){try{let stderr=(await readFile8(paths.stderrLog,"utf8")).trim();return stderr?`${message}
|
|
37
|
+
${stderr}`:message}catch(error){if(isNodeErrorCode6(error,"ENOENT"))return message;throw error}}async function readPidFile(paths){try{return parseDaemonPidFile(await readFile8(paths.pidFile,"utf8"))}catch(error){if(isNodeErrorCode6(error,"ENOENT"))return;throw error}}function resolveCliMainPath(){let modulePath=fileURLToPath(import.meta.url),extension=modulePath.endsWith(".ts")?".ts":".js";return resolve3(dirname6(modulePath),"..","..",`cli-main${extension}`)}function isNodeErrorCode6(error,code){return error instanceof Error&&"code"in error&&error.code===code}async function runAppServerMode(options){let shutdownRequested=!1,forceExit=!1,resolveShutdown=()=>{},shutdownSignal=new Promise(resolve4=>{resolveShutdown=resolve4}),requestShutdown=reason=>{if(shutdownRequested){forceExit||(forceExit=!0,process.exit(1));return}shutdownRequested=!0,resolveShutdown(reason)},handleSignal=signal=>{requestShutdown(signal)};process.on("SIGINT",handleSignal),process.on("SIGTERM",handleSignal);let runtime=createAppServerRuntime(requestShutdown,{extensionPaths:resolveAppServerExtensionPaths(options.extensions)}),stdio,unix,websocket;try{options.listen.kind==="stdio"?(stdio=startStdioTransport({core:runtime.core,onShutdown:requestShutdown}),process.stderr.write(`senpi app-server listening on stdio://
|
|
38
38
|
`)):options.listen.kind==="ws"?(websocket=await startAppServerWebSocketListener({core:runtime.core,host:options.listen.host,port:options.listen.port,auth:toWebSocketAuth(options.wsAuth)}),process.stderr.write(`senpi app-server listening on ws://${websocket.host}:${websocket.port}
|
|
39
39
|
`),process.stderr.write(`readyz http://127.0.0.1:${websocket.port}/readyz
|
|
40
40
|
`),websocket.tokenFile&&process.stderr.write(`token ${websocket.tokenFile}
|