@code-yeongyu/senpi 2026.11.10-3 → 2026.11.11
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 +29 -0
- package/dist/bundle/chunks/{chunk-Z2C5N2LM.js → chunk-5F2XC6UC.js} +2 -2
- package/dist/bundle/chunks/{chunk-H3VJVKUK.js → chunk-5PIZWSNG.js} +2 -2
- package/dist/bundle/chunks/{chunk-DEQLHN2J.js → chunk-BPEA5SWW.js} +1 -1
- package/dist/bundle/chunks/{chunk-DIHIEJFV.js → chunk-BUDJLVMF.js} +1 -1
- package/dist/bundle/chunks/{chunk-47T6YMI2.js → chunk-IBDAV2AL.js} +1 -1
- package/dist/bundle/chunks/{chunk-O5WJVIEU.js → chunk-J4ADZ2OK.js} +1 -1
- package/dist/bundle/chunks/{chunk-T2SMQPRE.js → chunk-K3GAGIPX.js} +1 -1
- package/dist/bundle/chunks/{chunk-HZDKQOWB.js → chunk-L6DF5G6K.js} +1 -1
- package/dist/bundle/chunks/{chunk-E266ORGA.js → chunk-LJFQYCFH.js} +1 -1
- package/dist/bundle/chunks/{chunk-DHAAWL2H.js → chunk-NRZAJUUO.js} +1 -1
- package/dist/bundle/chunks/{chunk-R2Z7CX77.js → chunk-OSNR3IRD.js} +1 -1
- package/dist/bundle/chunks/{chunk-AC6MDRAE.js → chunk-PUZDKFRT.js} +81 -81
- package/dist/bundle/chunks/{chunk-6Q7Y2G5A.js → chunk-SANWO37B.js} +1 -1
- package/dist/bundle/chunks/{chunk-NN7NESW2.js → chunk-SS5VTBAV.js} +1 -1
- package/dist/bundle/chunks/{chunk-JSD6O3RJ.js → chunk-XUDKT4UH.js} +1 -1
- package/dist/bundle/chunks/{cli-main-ZTT3N4DO.js → cli-main-7II7GCIR.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-5KHVGSCY.js → credential-accounts-N3B5YJS7.js} +1 -1
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{hand-entry-UV5XPMBF.js → hand-entry-ONOLRMVB.js} +1 -1
- package/dist/bundle/chunks/{host-command-BZN7WYYV.js → host-command-KWMJLPBS.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-TNHASSFJ.js → host-lifecycle-4WQDCN3Y.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +4 -4
- package/dist/bundle/chunks/{host-session-command-O5WYHZXH.js → host-session-command-ZDYEKCUB.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-OCU4NDPC.js → interactive-mode-EPE4M32G.js} +1 -1
- package/dist/bundle/chunks/{main-CNDP6HCF.js → main-OM7BKVZG.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-ODGJAGJP.js → multi-session-host-DB6XMY53.js} +2 -2
- package/dist/bundle/chunks/{package-manager-cli-HSPUG4RJ.js → package-manager-cli-GLU5Q2KV.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-KOZPXU3U.js → rpc-mode-SDJVBT36.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-SG44FE23.js → schedule-command-5SQD7M5N.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-5A24JYPE.js → session-control-endpoint-R25A2QGS.js} +1 -1
- package/dist/bundle/chunks/{session-picker-73YRS3GE.js → session-picker-GZM5CVQL.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +88 -88
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +4 -4
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/credential-accounts.d.ts +2 -0
- package/dist/core/credential-accounts.js +10 -1
- package/dist/core/dynamic-prompt/intent-gate.js +2 -2
- package/dist/core/dynamic-prompt/style.js +2 -2
- package/dist/core/dynamic-prompt/verification.js +2 -2
- package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-account-test.js +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.d.ts +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.js +47 -16
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.js +4 -2
- package/dist/core/extensions/builtin/anthropic-subscription/errors.d.ts +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/errors.js +14 -5
- package/dist/core/extensions/builtin/anthropic-subscription/oauth-login.js +3 -3
- package/dist/core/extensions/builtin/goal/continuation.d.ts +6 -0
- package/dist/core/extensions/builtin/goal/continuation.js +3 -2
- package/dist/core/extensions/builtin/goal/index.js +10 -26
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +0 -6
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +3 -37
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +66 -7
- package/dist/core/extensions/builtin/goal/provider-retry.d.ts +9 -0
- package/dist/core/extensions/builtin/goal/provider-retry.js +16 -0
- package/dist/core/extensions/builtin/goal/reload-reengagement.d.ts +3 -2
- package/dist/core/extensions/builtin/goal/reload-reengagement.js +4 -4
- package/dist/core/extensions/builtin/goal/session-start-continuation.d.ts +28 -0
- package/dist/core/extensions/builtin/goal/session-start-continuation.js +75 -0
- package/dist/core/extensions/builtin/goal/store.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/store.js +1 -1
- package/dist/core/extensions/builtin/handoff-hint/index.js +15 -11
- package/dist/core/extensions/builtin/handoff-hint/replacement-relay.d.ts +16 -0
- package/dist/core/extensions/builtin/handoff-hint/replacement-relay.js +17 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +5 -4
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-haiku-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +1 -1
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +5 -5
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +7 -6
- package/dist/core/resource-loader.js +19 -3
- package/dist/core/retry-fallback/controller-types.d.ts +2 -0
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/cooldown.d.ts +7 -0
- package/dist/core/retry-fallback/cooldown.js +13 -1
- package/dist/core/retry-fallback/credential-recovery.d.ts +7 -0
- package/dist/core/retry-fallback/credential-recovery.js +24 -0
- package/dist/core/retry-fallback/settings.d.ts +8 -5
- package/dist/core/retry-fallback/settings.js +9 -6
- package/dist/modes/rpc/delegation-declaration.js +1 -1
- package/dist/modes/rpc/host-gc-evidence.d.ts +1 -1
- package/dist/modes/rpc/host-gc-evidence.js +9 -3
- package/dist/modes/rpc/host-gc.js +4 -1
- package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -0
- package/dist/modes/rpc/session-attachment-gate.js +3 -0
- package/dist/private-fd-startup-fillers.d.ts +2 -2
- package/dist/private-fd-startup-fillers.js +34 -4
- package/docs/rpc.md +2 -1
- package/docs/settings.md +1 -1
- package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
- package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2026.11.11] - 2026-11-11
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Every built-in system prompt now tells the model the codebase is its own ("This codebase is yours: what it's for and how it's built are your call, and you'll keep building on it") and to change it the way its owner would: build on the code that already does the job instead of adding a parallel copy, fit new behavior into the structure that is there, leave no duplicate or dead code behind, and fix a defect in the code being changed instead of only noting it. This extends what [#6360](https://github.com/code-yeongyu/senpi/pull/6360) did for Fable 5.2 to every core. The "smallest correct change wins: no refactors beside a focused fix" rule and its rewordings ("no drive-by refactors", "diagnose and fix minimally", "the best change is usually the smallest correct change") are removed from every core so the two cannot cancel each other; the request still sets the scope, and a problem outside the code being changed is still a follow-up for the summary ([#6358](https://github.com/code-yeongyu/senpi/issues/6358)).
|
|
12
|
+
- A rate-limited Opus 5.5 turn falls back to Fable 5.2 first, then Opus 5: Opus 5 shares the Opus quota, so stepping to it alone could not serve the turn ([#6389](https://github.com/code-yeongyu/senpi/pull/6389)).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- `senpi host gc` no longer removes a Windows endpoint whose socket secret is missing or invalid; it keeps it with reason `credential_unavailable` ([#4293](https://github.com/code-yeongyu/senpi/pull/4293))
|
|
17
|
+
- Starting fresh from the handoff hint no longer prints a stale extension context error under the brief, and the `handoff_hint` `accepted` event is delivered by the replacement session; a cancelled switch still reports no acceptance ([#4051](https://github.com/code-yeongyu/senpi/issues/4051))
|
|
18
|
+
- The `bun:sqlite` `Database` declaration moved out of `coding-agent` into the sqlite-bun package, where it is declared once; the package now type-checks standalone and no CLI behavior changes ([#6255](https://github.com/code-yeongyu/senpi/pull/6255))
|
|
19
|
+
- Task hosts started from the compiled binary no longer exit with `EBADF: bad file descriptor, write` seconds after spawn: the startup stub releases only filler descriptors the process still holds ([#6285](https://github.com/code-yeongyu/senpi/pull/6285))
|
|
20
|
+
- The engine starts when its agent dir's `extensions/` is read-only (a sandboxed child): seeding the default extension shims keeps the existing copies instead of exiting with `EROFS` ([#6298](https://github.com/code-yeongyu/senpi/pull/6298))
|
|
21
|
+
- The relay's peer lane for agent messages is no longer refused by the session attachment check once attachment becomes mandatory (senpi#6305).
|
|
22
|
+
- Wait for correlated host readiness before opening sessions in the RPC worker routing fixture, and compose its test deadlines from host startup, worker response and cleanup phases while preserving the runtime's opening deadline ([#6372](https://github.com/code-yeongyu/senpi/pull/6372)).
|
|
23
|
+
- Claude subscription connection tests retain the redacted provider error and HTTP status, and an older test cannot mark a replacement credential failed. Legacy flat credentials and migrated placeholder `default` accounts no longer appear in subscription lists (desktop#6444, desktop#6445).
|
|
24
|
+
- An account blocked for a usage limit serves again once the reset it named has passed, without a re-login: a fallback pinned by the limit returns to the original, and a hold no longer outlives a reset that passed while the machine slept ([#6384](https://github.com/code-yeongyu/senpi/pull/6384)).
|
|
25
|
+
- A goal whose subscription accounts are all limited keeps waiting for the reset instead of blocking after eight refused wakes, re-checks a limit that named no reset, re-checks no sooner than the provider backoff and pauses after 48 re-checks of a limit that never clears, and resumes at once after a sign-in or an account block reset ([#6388](https://github.com/code-yeongyu/senpi/pull/6388)).
|
|
26
|
+
- Reverted the Claude subscription diagnostics and legacy `default` cleanup from #6380. It also dropped a pooled credential's real first account named `default` and reshaped a freshly stored flat subscription credential. Both behave as before again until the fix is re-landed.
|
|
27
|
+
- Claude subscription connection tests keep the redacted provider error and HTTP status, so a failed test says why it failed. A result for an older credential can no longer mark a replacement sign-in as failed (re-land of #6380 without its legacy `default` cleanup).
|
|
28
|
+
- A goal that waited out a usage limit for eight or more wakes resumes on its own after its session restarts or a config reload, instead of waiting for a message ([#6403](https://github.com/code-yeongyu/senpi/pull/6403)).
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
|
|
3
32
|
## [2026.11.10-3] - 2026-11-10
|
|
4
33
|
|
|
5
34
|
### Breaking Changes
|
|
@@ -3,6 +3,6 @@ import{BUILTIN_PATH_PREFIX}from"./chunk-QMRJYMNH.js";import{listHostEndpoints}fr
|
|
|
3
3
|
`).sort();return createHash2("sha256").update(lines.join("")).digest("hex")}function digestValue(entry){return typeof entry=="string"?entry:`${DYNAMIC}@${entry.version}`}var RPC_METHOD_CLASSES_DIGEST=rpcMethodClassesDigest(Object.fromEntries(Object.entries(RPC_METHOD_CLASS).map(([type,entry])=>[type,digestValue(entry)])));function describeRpcMethodClasses(){let classes={},dynamicVersions={};for(let[type,entry]of Object.entries(RPC_METHOD_CLASS))typeof entry=="string"?classes[type]=entry:(classes[type]=DYNAMIC,dynamicVersions[type]=entry.version);return{version:RPC_METHOD_CLASSES_VERSION,digest:RPC_METHOD_CLASSES_DIGEST,classes,dynamic_versions:dynamicVersions}}var INSTANCE_ID=resolveInstanceId(process.env[HOST_INSTANCE_ID_ENV]);function resolveInstanceId(value){return value!==void 0&&value.trim()!==""?value:randomUUID2()}function hostInstanceId(){return INSTANCE_ID}var cachedProfile;function hostGeneration(env){let value=env[HOST_GENERATION_ENV];return value!==void 0&&/^\d+$/.test(value)?Number(value):0}function hostLaunchProfile(argv,cwd){let parsed=parseArgs([...argv],{grokNeoEnabled:!1}),core={extensions:[...new Set((parsed.extensions??[]).map(path=>resolve(cwd,path)))].sort(),multi_session:parsed.multiSession===!0,session_runtime:resolveSessionRuntime(parsed)},canonical=JSON.stringify({extensions:core.extensions,multi_session:core.multi_session,session_runtime:core.session_runtime});return{profile_id:createHash3("sha256").update(canonical).digest("hex"),core}}function socketHostLaunchProfile(hostArgs,cwd){return hostLaunchProfile(["--mode","rpc","--multi-session","--listen","unix://",...hostArgs],cwd)}function protocolIdentity(agentDir=resolve(getAgentDir())){cachedProfile??=hostLaunchProfile(process.argv.slice(2),process.cwd());let build=engineBuildIdentity();return{instanceId:INSTANCE_ID,generation:hostGeneration(process.env),engineVersion:build.text,engineOrdinal:build.ordinal,launch_profile:cachedProfile,agentDir,method_classes:describeRpcMethodClasses()}}function holdAttachment(socket){return socket.removeAllListeners("data"),socket.resume(),socket.on("error",()=>socket.destroy()),socket.unref(),{release:()=>socket.destroy()}}var DEFAULT_PROBE_TIMEOUT_MS=1e4;function probeHost(options){return probeProtocolInfo(options.socket,options.timeoutMs??DEFAULT_PROBE_TIMEOUT_MS,options)}async function probeProtocolInfo(socketPath,timeoutMs,options={}){let reply=await requestOnSocket(socketPath,{type:"get_protocol_info"},timeoutMs,options);return reply===void 0?void 0:parseHostProtocolInfo(reply)}async function observeProtocolInfo(socketPath,timeoutMs,options={}){let reply=await requestOnSocket(socketPath,{type:"get_protocol_info",[OBSERVE_REQUEST_FIELD]:!0},timeoutMs,options);return reply===void 0?void 0:parseHostProtocolInfo(reply)}async function holdProtocolInfo(socketPath,timeoutMs,options={}){let outcome=await connectAndAsk(socketPath,{id:PROBE_REQUEST_ID,type:"get_protocol_info"},timeoutMs,!0,options),info=outcome.answer===void 0?void 0:parseHostProtocolInfo(outcome.answer);if(outcome.kept===void 0)return;let hold=holdAttachment(outcome.kept);if(info!==void 0)return{info,hold};hold.release()}async function probeSocketReachable(socketPath,timeoutMs,options={}){return(await connectAndAsk(socketPath,{id:PROBE_REQUEST_ID,type:"get_protocol_info"},timeoutMs,!1,options)).connected}async function probeSessionCount(socketPath,timeoutMs,options={}){return sessionCountOf(await requestOnSocket(socketPath,{type:"list_sessions",include_workers:!0},timeoutMs,options))}async function holdSessionCount(socketPath,timeoutMs,options={}){let outcome=await connectAndAsk(socketPath,{id:PROBE_REQUEST_ID,type:"list_sessions",include_workers:!0},timeoutMs,!0,options),kept=outcome.kept;if(kept===void 0)return;let sessions=sessionCountOf(outcome.answer);if(sessions===void 0){kept.destroy();return}let asked=0;return{sessions,recount:recountTimeoutMs=>askAgain(kept,`${PROBE_REQUEST_ID}-recount-${++asked}`,recountTimeoutMs),close:()=>kept.destroy()}}function askAgain(socket,id,timeoutMs){return new Promise(resolveCount=>{let buffer="",finish=count=>{clearTimeout(timeout),socket.off("data",onData),socket.off("close",onClose),resolveCount(count)},onClose=()=>finish(void 0),onData=chunk=>{buffer+=chunk.toString("utf8");for(let newline=buffer.indexOf(`
|
|
4
4
|
`);newline!==-1;newline=buffer.indexOf(`
|
|
5
5
|
`)){let answer=readAnswer(buffer.slice(0,newline),id);if(buffer=buffer.slice(newline+1),answer!==void 0){finish(sessionCountOf(answer));return}}},timeout=setTimeout(()=>finish(void 0),timeoutMs);if(socket.destroyed){finish(void 0);return}socket.on("data",onData),socket.once("close",onClose),socket.write(`${JSON.stringify({id,type:"list_sessions",include_workers:!0})}
|
|
6
|
-
`)})}function sessionCountOf(reply){return isRecord2(reply)&&Array.isArray(reply.sessions)?reply.sessions.length:void 0}var DEFAULT_STOP_TIMEOUT_MS=Math.max(CHILD_STOP_TIMEOUT_MS,childStalledStopMaxMs())+CHILD_KILL_EXIT_TIMEOUT_MS+5e3,SIGKILL_GRACE_MS=2e3,STOP_PROGRESS_FRESH_MS=1e4,AFTER_STALL_WAIT_GRACE_MS=CHILD_KILL_EXIT_TIMEOUT_MS+5e3,STOP_WAIT_BUDGET_MS=Math.max(DEFAULT_STOP_TIMEOUT_MS,childStalledStopMaxMs()+AFTER_STALL_WAIT_GRACE_MS)+SIGKILL_GRACE_MS;function ensureSender(){return{pid:process.pid,kind:"ensure"}}async function announceStop(target,targetPid){let intent={sender:target.sender,targetPid,reason:target.reason,signal:"SIGTERM",at:new Date().toISOString()};return await writeStopIntent(target.generation,intent),intent}async function matchesPidFileOrUnknown(pidFile,probe){try{return await processMatchesPidFile(pidFile,probe)}catch(error){if(error instanceof ProcessIdentityUnreadableError)return!1;throw error}}async function recordEscalation(target,announced){let current=await readStopIntent(target.generation).catch(()=>{});await noteEscalatedStop(target.daemonDir,target.instanceId,current??announced)}async function stopSpawnedChild(child,childExit,termTimeoutMs,target){let exited=()=>child.exitCode!==null||child.signalCode!==null,pid=child.pid;if(exited()||pid===void 0)return;let waitFor=async ms=>{let timer;try{return await Promise.race([childExit.then(()=>!0),new Promise(resolve2=>{timer=setTimeout(()=>resolve2(exited()),ms)})])}finally{clearTimeout(timer)}},announced=await announceStop(target,pid);if(signalPid(pid,"SIGTERM"),!await waitWhileSupervisorReports(target,termTimeoutMs,waitFor)){if(signalPid(pid,"SIGKILL"),!await waitFor(SIGKILL_GRACE_MS))throw new Error(`RPC socket host pid ${pid} remained alive after SIGKILL`);await recordEscalation(target,announced)}}async function stopManagedHost(pidFile,termTimeoutMs,target,readStartTime=readProcessStartTime){let announced=await announceStop(target,pidFile.pid);if(await signalValidated(pidFile,"SIGTERM",readStartTime),!await waitWhileSupervisorReports(target,termTimeoutMs,ms=>waitForGone(pidFile,ms,readStartTime))){if(await signalValidated(pidFile,"SIGKILL",readStartTime),!await waitForGone(pidFile,SIGKILL_GRACE_MS,readStartTime))throw new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);await recordEscalation(target,announced)}}async function waitWhileSupervisorReports(target,termTimeoutMs,waitFor){if(await waitFor(termTimeoutMs))return!0;let extended=!1;for(;;){let untilAt=await activeStopProgress(target.generation,Date.now(),STOP_PROGRESS_FRESH_MS);if(untilAt===void 0)return extended&&await waitFor(AFTER_STALL_WAIT_GRACE_MS);if(extended=!0,await waitFor(Math.min(STOP_PROGRESS_FRESH_MS,Math.max(1,untilAt-Date.now()))))return!0}}async function resolvePidFileOwnership(pidFile,readStartTime=readProcessStartTime){try{return await processMatchesPidFile(pidFile,readStartTime,processIsLive,{attempts:1})?"owns":"gone"}catch(error){if(error instanceof ProcessIdentityUnreadableError)return"unknown";throw error}}async function signalValidated(pidFile,signal,readStartTime=readProcessStartTime){await resolvePidFileOwnership(pidFile,readStartTime)==="owns"&&signalPid(pidFile.pid,signal)}async function waitForGone(pidFile,timeoutMs,readStartTime=readProcessStartTime){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){if(await resolvePidFileOwnership(pidFile,readStartTime)==="gone")return!0;await delay(50)}return await resolvePidFileOwnership(pidFile,readStartTime)==="gone"}function signalPid(pid,signal){try{process.kill(pid,signal)}catch(error){if(!isNodeErrorCode(error,"ESRCH"))throw error}}function delay(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}var STARTUP_DECISION_BUDGET_MS=18e4,STARTUP_PHASE_ALLOWANCE_MS={supervisor_bootstrap:3e4,supervisor_setup:15e3,host_bootstrap:3e4,host_runtime:6e4,opaque_host:9e4,internal_ready:15e3,observer_ready:15e3,public_published:15e3,presenter_resolved:3e4,public_verified:1e4},SHARED_PREFIX=["supervisor_bootstrap","supervisor_setup","host_bootstrap","host_runtime","internal_ready","observer_ready"],PHASE_PATH={initial:[...SHARED_PREFIX,"public_published","presenter_resolved","public_verified"],successor:[...SHARED_PREFIX,"presenter_resolved","public_published","public_verified"],legacy:["public_verified"]},OPAQUE_REPLACES=["host_bootstrap","host_runtime","internal_ready"];function startStartupController(options){let{clock}=options,budget=options.decisionBudgetMs??STARTUP_DECISION_BUDGET_MS,path=PHASE_PATH[options.launchKind],t0=clock.now(),ceiling=t0+budget,index=0,phaseLimit=0,deadline=0,timer,terminal,listeners=new Set,current=()=>path[Math.min(index,path.length-1)]??"supervisor_bootstrap",allowance=phase=>options.launchKind==="legacy"?budget:STARTUP_PHASE_ALLOWANCE_MS[phase],notify=()=>{for(let listener of[...listeners])listener()},settle=outcome=>{terminal||(terminal=outcome,clock.cancel(timer),notify())},expire=()=>{let phase=current();settle({kind:"timeout",phase,limit:phaseLimit<ceiling?"phase":"overall",phaseBudgetMs:allowance(phase),elapsedMs:deadline-t0})},enter=at=>{phaseLimit=at+allowance(current()),deadline=Math.min(phaseLimit,ceiling),timer=clock.scheduleAt(deadline,expire),notify()},expiredAt=now=>(!terminal&&now>=deadline&&expire(),terminal!==void 0),waitFor=check=>{let ready=check();return ready!==void 0?Promise.resolve(ready):new Promise(resolveWait=>{let listener=()=>{let value=check();value!==void 0&&(listeners.delete(listener),resolveWait(value))};listeners.add(listener)})};return enter(t0),{complete(requested){let now=clock.now();if(expiredAt(now))return"rejected";let phase=requested==="internal_ready"&¤t()==="opaque_host"?"opaque_host":requested,position=path.indexOf(phase);return position>=0&&position<index?"ignored":position!==index?"rejected":(clock.cancel(timer),index+=1,index===path.length?settle({kind:"ready",elapsedMs:now-t0}):enter(now),"accepted")},declareOpaqueHost(){if(expiredAt(clock.now())||options.launchKind!=="initial"||current()!=="supervisor_setup")return"rejected";let[before=[],after=[]]=[path.slice(0,index+1),path.slice(index+1)];return path=[...before,"opaque_host",...after.filter(phase=>!OPAQUE_REPLACES.includes(phase))],"accepted"},fail(cause){let now=clock.now();expiredAt(now)||settle({kind:"failed",phase:current(),cause,elapsedMs:now-t0})},get terminal(){return terminal},get phase(){return current()},remainingMs(){return terminal?0:Math.max(0,deadline-clock.now())},whenPhase(phase){return waitFor(()=>terminal?!1:current()===phase?!0:void 0)},settled(){return waitFor(()=>terminal)}}}var STOP_REAP_MARGIN_MS=5e3,FAILED_START_FINALIZE_MS=7e3,DISCOVERY_BUDGET_MS=9e4,LOCK_WAIT_HEADROOM_MS=1e4,HANDOVER_REPLY_MARGIN_MS=2e4;function startupEnvelope(stallMs=DEFAULT_CHILD_STALLED_STOP_MAX_MS){let decision=STARTUP_DECISION_BUDGET_MS,stop=Math.max(CHILD_STOP_TIMEOUT_MS,stallMs)+CHILD_KILL_EXIT_TIMEOUT_MS+STOP_REAP_MARGIN_MS+SIGKILL_GRACE_MS,cleanup=stop+FAILED_START_FINALIZE_MS,settled=decision+cleanup,holder=DISCOVERY_BUDGET_MS+stop+settled;return{decision,stop,cleanup,settled,discovery:DISCOVERY_BUDGET_MS,holder,lockWait:holder+LOCK_WAIT_HEADROOM_MS,handoverReply:2*holder+HANDOVER_REPLY_MARGIN_MS}}function ensureLockWaitDetail(lockWaitMs){return`RPC host ensure lock wait exceeded ${lockWaitMs}ms; startup not started by this caller`}function startupTimeoutDetail(outcome,cleanup){return[`RPC host startup timed out: phase=${outcome.phase}`,`limit=${outcome.limit}`,`phase_budget_ms=${outcome.phaseBudgetMs}`,`startup_budget_ms=${STARTUP_DECISION_BUDGET_MS}`,`elapsed_ms=${outcome.elapsedMs}`,`cleanup=${cleanup}`].join("; ")}import{randomUUID as randomUUID3}from"node:crypto";import{access,link,readdir as readdir2,rm}from"node:fs/promises";async function electPointer(paths,options={}){let text=await readFileOrUndefined(paths.pointerFile).catch(()=>{});if(text!==void 0){let instanceId=parseJson(text)?.instance_id;return typeof instanceId=="string"?{state:"current",instanceId,candidates:[]}:{state:"missing",instanceId:null,candidates:[]}}let candidates=await provenLiveGenerations(paths),ids=candidates.map(candidate=>candidate.instanceId),[only]=candidates;return only===void 0||candidates.length>1||options.write===!1?{state:"missing",instanceId:null,candidates:ids}:await linkPointer(paths,only)?{state:"elected",instanceId:only.instanceId,candidates:ids}:electPointer(paths,{write:!1})}async function provenLiveGenerations(paths){let found=[];for(let instanceId of(await readdir2(paths.generationsDir).catch(()=>[])).sort()){let generation=generationPaths(paths,instanceId);if(await exists(generation.stopIntentFile)||await exists(generation.drainRequestFile))continue;let text=await readFileOrUndefined(generation.pidFile).catch(()=>{}),record=text===void 0?void 0:parseDaemonPidFile(text),parsed=parseJson(text);if(record===void 0||record.processStartTime===null||parsed?.instance_id!==instanceId||typeof parsed.socket!="string"||!sameEndpoint(parsed.socket,paths.socket))continue;let identity={pid:record.pid,processStartTime:record.processStartTime};await processMatchesPidFile(identity,readProcessStartTime).catch(()=>!1)&&found.push({instanceId,writer:parsed.writer})}return found}async function linkPointer(paths,candidate){let staged=`${paths.pointerFile}.elect-${process.pid}-${randomUUID3()}.tmp`;try{return await writeStateFile(staged,{layout:HOST_DAEMON_LAYOUT,instance_id:candidate.instanceId,generation_dir:generationPaths(paths,candidate.instanceId).relativeDir,...isRecord(candidate.writer)&&{writer:candidate.writer}}),await link(staged,paths.pointerFile),!0}catch{return!1}finally{await rm(staged,{force:!0}).catch(()=>{})}}async function exists(path){return access(path).then(()=>!0,()=>!1)}import{dirname as dirname2,join as join2}from"node:path";var LEGACY_PROBE_TIMEOUT_MS=1e4,DRAIN_POLL_MS=50;async function retireIdleLegacyHost(paths,probe,drainTimeoutMs=LEGACY_PROBE_TIMEOUT_MS){let judged=await judgeLegacyHost(paths,probe);if(judged.verdict==="absent")return;if(judged.verdict==="held")return judged.detail;let{record,socket}=judged.host;if(!await drainAndWait(record,probe,drainTimeoutMs))return`pid ${record.pid} (${socket}) was asked to drain and is still running after ${drainTimeoutMs}ms; retry once it exits`}async function readLegacyHost(paths){let text=await readFileOrUndefined(paths.legacyPidFile),record=text===void 0?void 0:parseDaemonPidFile(text);if(record===void 0)return;let stamped=parseJson(text)?.socket;return{record,socket:typeof stamped=="string"?stamped:join2(dirname2(paths.flatDir),"rpc","rpc.sock")}}async function provenLegacyOwner(paths,socket){let legacy=await readLegacyHost(paths),startTime=legacy?.record.processStartTime;if(!legacy||startTime===null||startTime===void 0||!sameEndpoint(legacy.socket,socket))return;let identity={pid:legacy.record.pid,processStartTime:startTime};return await processMatchesPidFile(identity,readProcessStartTime).catch(error=>(error instanceof ProcessIdentityUnreadableError&&logUnknownHostIdentity("legacy_host.pid",identity.pid),!1))?identity:void 0}async function judgeLegacyHost(paths,probe,platform=process.platform){let host=await readLegacyHost(paths);if(host===void 0)return{verdict:"absent"};let{pid}=host.record,identity=await processMatchesPidFile(host.record,probe).then(owns=>owns?"proven":"gone",error=>{if(error instanceof ProcessIdentityUnreadableError)return"unreadable";throw error});if(identity==="gone")return{verdict:"absent"};let where=`pid ${pid} (${host.socket})`;if(identity==="unreadable")return logUnknownHostIdentity("legacy_host.pid",pid),{verdict:"held",detail:`${where} cannot be proven to be the process its record names; stop it by hand`};if(platform==="win32")return{verdict:"held",detail:`${where} cannot be drained on win32; it exits once idle, then retry`};let protocol=await probeProtocolInfo(host.socket,LEGACY_PROBE_TIMEOUT_MS);if(!protocol?.capabilities.includes(GENERATION_HANDOFF_CAPABILITY))return{verdict:"held",detail:`${where} ${protocol?"cannot drain":"does not answer"}; it is never signalled - stop it by hand, then retry`};let sessions=await probeSessionCount(host.socket,LEGACY_PROBE_TIMEOUT_MS);return sessions===0?{verdict:"idle",host}:{verdict:"held",detail:busyLegacyHostDetail(pid,host.socket,sessions)}}function busyLegacyHostDetail(pid,socket,sessions){return`pid ${pid} (${socket}) holds ${describeSessions(sessions)}; run \`${APP_NAME} host stop --drain --socket ${socket}\` to let it finish that work and exit, then retry`}function describeSessions(sessions){return sessions===void 0?"an unknown number of sessions":`${sessions} open session${sessions===1?"":"s"}`}async function drainAndWait(record,probe,timeoutMs){try{process.kill(record.pid,"SIGUSR1")}catch(error){if(!(error instanceof Error&&"code"in error&&error.code==="ESRCH"))throw error}let stillRunning=()=>processMatchesPidFile(record,probe,processIsLive,{attempts:1}).catch(error=>{if(error instanceof ProcessIdentityUnreadableError)return!0;throw error}),deadline=Date.now()+timeoutMs;for(;await stillRunning();){if(Date.now()>deadline)return!1;await new Promise(resolve2=>setTimeout(resolve2,DRAIN_POLL_MS))}return!0}var EXIT_POLL_MS=100;async function drainHost(paths,options){let named=options.instanceId;if(named!==void 0&&!isGenerationId(named))return{action:"refuse",reason:"unknown_owner"};let registered=named===void 0?await readHostRegistration(paths):await readGenerationRegistration(paths,named),owner=await provenOwner(registered,options.socket);if(owner!==void 0){let generation=generationPaths(paths,owner.instanceId);if((await readDrainWatch(generation.drainWatchFile))?.pid===owner.pid)return await writeDrainRequest(generation.drainRequestFile,{sender:{pid:process.pid,kind:"stop"},reason:"operator_drain"}).catch(cause=>{if(cause.code!=="ENOENT")throw cause}),drained({...owner},options.waitMs)}if(named!==void 0&&owner===void 0)return{action:"refuse",reason:"unknown_owner"};let pid=owner?.pid??(await provenLegacyOwner(paths,options.socket))?.pid;if(pid===void 0)return{action:"refuse",reason:"unknown_owner"};let host=await probeProtocolInfo(options.socket,options.timeoutMs??1e4),answersAsTarget=named===void 0||host?.instanceId===named;return!host?.capabilities.includes(GENERATION_HANDOFF_CAPABILITY)||!answersAsTarget||process.platform==="win32"?{action:"refuse",reason:"drain_unsupported"}:(signalDrain(pid),drained(owner??{pid,processStartTime:void 0},options.waitMs))}async function drained(owner,waitMs){let base={action:"drained",pid:owner.pid,...owner.instanceId&&{instanceId:owner.instanceId}};return waitMs===void 0?base:{...base,exited:await waitForExit(owner,waitMs)}}async function waitForExit(owner,waitMs){let deadline=Date.now()+waitMs;for(;;){if(await exitedNow(owner))return!0;if(Date.now()>=deadline)return!1;await new Promise(resolve2=>setTimeout(resolve2,EXIT_POLL_MS))}}async function exitedNow(owner){if(!processIsLive(owner.pid))return!0;if(owner.processStartTime===void 0)return!1;let identity={pid:owner.pid,processStartTime:owner.processStartTime};return processMatchesPidFile(identity,readProcessStartTime).then(same=>!same,error=>{if(error instanceof ProcessIdentityUnreadableError)return!1;throw error})}function signalDrain(pid){try{process.kill(pid,"SIGUSR1")}catch(error){if(error instanceof Error&&"code"in error&&error.code==="ESRCH")return;throw error}}import{stat as stat2}from"node:fs/promises";function processStartTimeIsUnreadable(){return process.platform==="win32"}var STOP_START_TIME_BUDGET_MS=1e4;function readLiveStartTime(pid){return readProcessStartTime(pid,process.platform,STOP_START_TIME_BUDGET_MS)}async function startedBeforeRegistration(pid,registrationFile){let live=await readLiveStartTime(pid).catch(()=>{}),startedMs=live===void 0?void 0:processStartTimeMs(live);if(startedMs===void 0)return!1;let written=await stat2(registrationFile).catch(()=>{});return written!==void 0&&startedMs<=written.mtimeMs}function parseHostStopGeneration(text){let value=parseJson(text);if(!(!value||typeof value.instanceId!="string"||value.instanceId.length===0||typeof value.generation!="number"||!Number.isSafeInteger(value.generation)||value.generation<0||typeof value.pid!="number"||!Number.isSafeInteger(value.pid)||value.pid<=0||!(value.processStartTime===null||typeof value.processStartTime=="string"&&value.processStartTime.length>0)))return{instanceId:value.instanceId,generation:value.generation,pid:value.pid,processStartTime:value.processStartTime}}function compareStopGeneration(registered,expected){if(registered?.instanceId!==expected.instanceId||registered.generation!==expected.generation||registered.record.pid!==expected.pid)return"stale";let recorded=registered.record.processStartTime;return recorded!==null&&expected.processStartTime!==null?recorded===expected.processStartTime?"proven":"stale":recorded===null&&expected.processStartTime===null&&processStartTimeIsUnreadable()?"bound_without_start_time":"unprovable"}async function stopHost(options){if(options.expectedGeneration!==void 0&&options.drain===!0)throw new TypeError("stopHost: expectedGeneration binds a hard stop and cannot be combined with drain");if(options.expectedGeneration===void 0)return stopHostLocked(options);let release=await acquireHostEnsureLock(options.socket,startupEnvelope(childStalledStopMaxMs()).lockWait);try{return await stopHostLocked(options)}finally{await release()}}async function stopHostLocked(options){let paths=createHostDaemonPaths({socket:options.socket,...options.agentDir?{agentDir:options.agentDir}:{}});await electPointer(paths);let expected=options.expectedGeneration;if(expected!==void 0){let refusal=generationRefusal(compareStopGeneration(await readHostRegistration(paths),expected));if(refusal!==void 0)return refusal}if(options.drain===!0)return drainHost(paths,{socket:options.socket,...options.instanceId!==void 0&&{instanceId:options.instanceId},...options.waitMs!==void 0&&{waitMs:options.waitMs},...options.timeoutMs!==void 0&&{timeoutMs:options.timeoutMs}});let owner=expected===void 0?await provenOwner(await readHostRegistration(paths),options.socket):void 0;if(expected===void 0&&!owner)return{action:"refuse",reason:"unknown_owner"};let host=await probeProtocolInfo(options.socket,options.timeoutMs??1e4),sessions=await probeSessionCount(options.socket,options.timeoutMs??1e4);if(options.force!==!0&&host!==void 0&&sessions!==void 0&&sessions>0)return{action:"refuse",reason:"sessions_live"};if(options.requireAnswer===!0&&(host===void 0||sessions===void 0))return{action:"refuse",reason:"sessions_live"};let target=expected===void 0?owner:await recheckedTarget(paths,options.socket,expected,host);return target===void 0?{action:"refuse",reason:"unknown_owner"}:"action"in target?target:(await writeStopIntent(generationPaths(paths,target.instanceId),{sender:{pid:process.pid,kind:"stop"},targetPid:target.pid,reason:"operator_stop",signal:"SIGTERM",at:new Date().toISOString()}),signalGeneration(target.pid,"SIGTERM")||await releaseGeneration(paths,{instanceId:target.instanceId,pid:target.pid}),{action:"stopped",pid:target.pid})}function generationRefusal(match){if(match==="stale")return{action:"refuse",reason:"stale_generation"};if(match==="unprovable")return{action:"refuse",reason:"unknown_owner"}}async function staleStopGeneration(socket,agentDir,expected){let paths=createHostDaemonPaths({socket,...agentDir?{agentDir}:{}});return generationRefusal(compareStopGeneration(await readHostRegistration(paths),expected))}async function recheckedTarget(paths,socket,expected,host){let current=await readHostRegistration(paths),match=compareStopGeneration(current,expected),refusal=generationRefusal(match);if(refusal!==void 0)return refusal;if(!(match==="proven"&&!await provenOwner(current,socket))){if(match==="bound_without_start_time"){if(current?.socket!==void 0&&!sameEndpoint(current.socket,socket)||!await startedBeforeRegistration(expected.pid,generationPaths(paths,expected.instanceId).pidFile))return;if(host?.instanceId!==void 0&&host.instanceId!==expected.instanceId)return{action:"refuse",reason:"stale_generation"}}return{pid:expected.pid,instanceId:expected.instanceId}}}function signalGeneration(pid,signal){try{return process.kill(pid,signal),!0}catch(error){if(error instanceof Error&&"code"in error&&error.code==="ESRCH")return!1;throw error}}import{lstat as lstat3,readdir as readdir5,rm as rm2,rmdir,unlink}from"node:fs/promises";import{dirname as dirname4,join as join5}from"node:path";import{lstat,readdir as readdir3}from"node:fs/promises";import{createConnection}from"node:net";import{basename as basename2,dirname as dirname3,join as join3}from"node:path";var SOCKET_PROBE_TIMEOUT_MS=2e3;async function endpointInUse(paths,socket){if(await anyGenerationLive(paths))return{inUse:"live_generation"};for(let claim of await readSessionPathClaims(paths.reservationsDir))if(await claimOwnerIsLive(claim.owner))return{inUse:"live_claim"};let silence=await socketSilence(socket);if(silence===void 0)return{inUse:"reachable"};let successors=(await socketSiblings(socket)).filter(name=>name.startsWith(`${basename2(socket)}.next-`)&&!isBindScratchName(name));for(let successor of successors)if(await socketSilence(join3(dirname3(socket),successor))===void 0)return{inUse:"reachable"};return{inUse:void 0,silence}}async function socketSiblings(socket){let name=basename2(socket);return(await readdir3(dirname3(socket)).catch(()=>[])).filter(entry=>entry.startsWith(`${name}.next-`)||entry.startsWith(`${name}.shield-`)||entry.startsWith(`${name}${BIND_SCRATCH_INFIX}`))}async function anyGenerationLive(paths){for(let instanceId of await readdir3(paths.generationsDir).catch(()=>[])){if(await hostChildAlive(generationPaths(paths,instanceId)))return!0;let record=generationRecord(await readFileOrUndefined(generationPaths(paths,instanceId).pidFile));if(record!==void 0&&await recordIsLive(record))return!0}return!1}function generationRecord(text){if(text===void 0)return;let record=parseDaemonPidFile(text);if(record!==void 0)return record;let pid=parseJson(text)?.pid;return typeof pid=="number"&&Number.isInteger(pid)&&pid>0?{pid,processStartTime:null}:void 0}async function recordIsLive(record){try{return await processMatchesPidFile(record)}catch(error){if(error instanceof ProcessIdentityUnreadableError)return!0;throw error}}async function socketSilence(socket){let secret;return process.platform==="win32"&&(secret=await readSocketSecret(socketSecretPath(socket)).catch(()=>{}),secret===void 0)?"socket_absent":new Promise(resolveProbe=>{let connection=createConnection(resolveSocketTransportAddress(socket,process.platform,secret)),finish=value=>{clearTimeout(timeout),connection.destroy(),resolveProbe(value)},timeout=setTimeout(()=>finish(void 0),SOCKET_PROBE_TIMEOUT_MS);connection.once("connect",()=>finish(void 0)),connection.once("error",error=>{error.code==="ECONNREFUSED"?refusedBySocket(socket).then(finish):error.code==="ENOENT"?finish("socket_absent"):finish(void 0)})})}async function refusedBySocket(socket){if(process.platform==="win32"||socket.startsWith("\0"))return"socket_refused";try{return(await lstat(socket)).isSocket()?"socket_refused":void 0}catch(error){return error instanceof Error&&"code"in error&&error.code==="ENOENT"?"socket_absent":void 0}}import{execFile}from"node:child_process";import{lstat as lstat2,readdir as readdir4,readFile as readFile2,readlink,realpath as realpath2,stat as stat3}from"node:fs/promises";import{join as join4}from"node:path";var LSOF_TIMEOUT_MS=2e3,LINUX_ACCEPTCON_FLAG=65536;async function publicNameMatcher(socket){let names=new Set([socket,await realpath2(socket).catch(()=>socket)]),published=await lstat2(socket).catch(()=>{}),seen=new Map;return async path=>{if(names.has(path))return!0;if(published===void 0||!path.startsWith("/"))return!1;let known=seen.get(path);if(known!==void 0)return known;let entry=await stat3(path).catch(()=>{}),same=entry!==void 0&&entry.dev===published.dev&&entry.ino===published.ino;return seen.set(path,same),same}}async function countPublicClients(supervisorPid,socket){let matches=await publicNameMatcher(socket),descriptors=process.platform==="darwin"?await darwinDescriptors(supervisorPid,matches):process.platform==="linux"?await linuxDescriptors(supervisorPid,matches):void 0;if(!(descriptors===void 0||descriptors.listeners===0))return descriptors.total-descriptors.listeners}async function darwinDescriptors(pid,matches){let stdout=await new Promise(resolve2=>{execFile("lsof",["-a","-p",String(pid),"-U","-w","-F","n"],{timeout:LSOF_TIMEOUT_MS},(error,out)=>resolve2(error?void 0:out))});if(stdout===void 0)return;let total=0;for(let line of stdout.split(`
|
|
6
|
+
`)})}function sessionCountOf(reply){return isRecord2(reply)&&Array.isArray(reply.sessions)?reply.sessions.length:void 0}var DEFAULT_STOP_TIMEOUT_MS=Math.max(CHILD_STOP_TIMEOUT_MS,childStalledStopMaxMs())+CHILD_KILL_EXIT_TIMEOUT_MS+5e3,SIGKILL_GRACE_MS=2e3,STOP_PROGRESS_FRESH_MS=1e4,AFTER_STALL_WAIT_GRACE_MS=CHILD_KILL_EXIT_TIMEOUT_MS+5e3,STOP_WAIT_BUDGET_MS=Math.max(DEFAULT_STOP_TIMEOUT_MS,childStalledStopMaxMs()+AFTER_STALL_WAIT_GRACE_MS)+SIGKILL_GRACE_MS;function ensureSender(){return{pid:process.pid,kind:"ensure"}}async function announceStop(target,targetPid){let intent={sender:target.sender,targetPid,reason:target.reason,signal:"SIGTERM",at:new Date().toISOString()};return await writeStopIntent(target.generation,intent),intent}async function matchesPidFileOrUnknown(pidFile,probe){try{return await processMatchesPidFile(pidFile,probe)}catch(error){if(error instanceof ProcessIdentityUnreadableError)return!1;throw error}}async function recordEscalation(target,announced){let current=await readStopIntent(target.generation).catch(()=>{});await noteEscalatedStop(target.daemonDir,target.instanceId,current??announced)}async function stopSpawnedChild(child,childExit,termTimeoutMs,target){let exited=()=>child.exitCode!==null||child.signalCode!==null,pid=child.pid;if(exited()||pid===void 0)return;let waitFor=async ms=>{let timer;try{return await Promise.race([childExit.then(()=>!0),new Promise(resolve2=>{timer=setTimeout(()=>resolve2(exited()),ms)})])}finally{clearTimeout(timer)}},announced=await announceStop(target,pid);if(signalPid(pid,"SIGTERM"),!await waitWhileSupervisorReports(target,termTimeoutMs,waitFor)){if(signalPid(pid,"SIGKILL"),!await waitFor(SIGKILL_GRACE_MS))throw new Error(`RPC socket host pid ${pid} remained alive after SIGKILL`);await recordEscalation(target,announced)}}async function stopManagedHost(pidFile,termTimeoutMs,target,readStartTime=readProcessStartTime){let announced=await announceStop(target,pidFile.pid);if(await signalValidated(pidFile,"SIGTERM",readStartTime),!await waitWhileSupervisorReports(target,termTimeoutMs,ms=>waitForGone(pidFile,ms,readStartTime))){if(await signalValidated(pidFile,"SIGKILL",readStartTime),!await waitForGone(pidFile,SIGKILL_GRACE_MS,readStartTime))throw new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);await recordEscalation(target,announced)}}async function waitWhileSupervisorReports(target,termTimeoutMs,waitFor){if(await waitFor(termTimeoutMs))return!0;let extended=!1;for(;;){let untilAt=await activeStopProgress(target.generation,Date.now(),STOP_PROGRESS_FRESH_MS);if(untilAt===void 0)return extended&&await waitFor(AFTER_STALL_WAIT_GRACE_MS);if(extended=!0,await waitFor(Math.min(STOP_PROGRESS_FRESH_MS,Math.max(1,untilAt-Date.now()))))return!0}}async function resolvePidFileOwnership(pidFile,readStartTime=readProcessStartTime){try{return await processMatchesPidFile(pidFile,readStartTime,processIsLive,{attempts:1})?"owns":"gone"}catch(error){if(error instanceof ProcessIdentityUnreadableError)return"unknown";throw error}}async function signalValidated(pidFile,signal,readStartTime=readProcessStartTime){await resolvePidFileOwnership(pidFile,readStartTime)==="owns"&&signalPid(pidFile.pid,signal)}async function waitForGone(pidFile,timeoutMs,readStartTime=readProcessStartTime){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){if(await resolvePidFileOwnership(pidFile,readStartTime)==="gone")return!0;await delay(50)}return await resolvePidFileOwnership(pidFile,readStartTime)==="gone"}function signalPid(pid,signal){try{process.kill(pid,signal)}catch(error){if(!isNodeErrorCode(error,"ESRCH"))throw error}}function delay(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}var STARTUP_DECISION_BUDGET_MS=18e4,STARTUP_PHASE_ALLOWANCE_MS={supervisor_bootstrap:3e4,supervisor_setup:15e3,host_bootstrap:3e4,host_runtime:6e4,opaque_host:9e4,internal_ready:15e3,observer_ready:15e3,public_published:15e3,presenter_resolved:3e4,public_verified:1e4},SHARED_PREFIX=["supervisor_bootstrap","supervisor_setup","host_bootstrap","host_runtime","internal_ready","observer_ready"],PHASE_PATH={initial:[...SHARED_PREFIX,"public_published","presenter_resolved","public_verified"],successor:[...SHARED_PREFIX,"presenter_resolved","public_published","public_verified"],legacy:["public_verified"]},OPAQUE_REPLACES=["host_bootstrap","host_runtime","internal_ready"];function startStartupController(options){let{clock}=options,budget=options.decisionBudgetMs??STARTUP_DECISION_BUDGET_MS,path=PHASE_PATH[options.launchKind],t0=clock.now(),ceiling=t0+budget,index=0,phaseLimit=0,deadline=0,timer,terminal,listeners=new Set,current=()=>path[Math.min(index,path.length-1)]??"supervisor_bootstrap",allowance=phase=>options.launchKind==="legacy"?budget:STARTUP_PHASE_ALLOWANCE_MS[phase],notify=()=>{for(let listener of[...listeners])listener()},settle=outcome=>{terminal||(terminal=outcome,clock.cancel(timer),notify())},expire=()=>{let phase=current();settle({kind:"timeout",phase,limit:phaseLimit<ceiling?"phase":"overall",phaseBudgetMs:allowance(phase),elapsedMs:deadline-t0})},enter=at=>{phaseLimit=at+allowance(current()),deadline=Math.min(phaseLimit,ceiling),timer=clock.scheduleAt(deadline,expire),notify()},expiredAt=now=>(!terminal&&now>=deadline&&expire(),terminal!==void 0),waitFor=check=>{let ready=check();return ready!==void 0?Promise.resolve(ready):new Promise(resolveWait=>{let listener=()=>{let value=check();value!==void 0&&(listeners.delete(listener),resolveWait(value))};listeners.add(listener)})};return enter(t0),{complete(requested){let now=clock.now();if(expiredAt(now))return"rejected";let phase=requested==="internal_ready"&¤t()==="opaque_host"?"opaque_host":requested,position=path.indexOf(phase);return position>=0&&position<index?"ignored":position!==index?"rejected":(clock.cancel(timer),index+=1,index===path.length?settle({kind:"ready",elapsedMs:now-t0}):enter(now),"accepted")},declareOpaqueHost(){if(expiredAt(clock.now())||options.launchKind!=="initial"||current()!=="supervisor_setup")return"rejected";let[before=[],after=[]]=[path.slice(0,index+1),path.slice(index+1)];return path=[...before,"opaque_host",...after.filter(phase=>!OPAQUE_REPLACES.includes(phase))],"accepted"},fail(cause){let now=clock.now();expiredAt(now)||settle({kind:"failed",phase:current(),cause,elapsedMs:now-t0})},get terminal(){return terminal},get phase(){return current()},remainingMs(){return terminal?0:Math.max(0,deadline-clock.now())},whenPhase(phase){return waitFor(()=>terminal?!1:current()===phase?!0:void 0)},settled(){return waitFor(()=>terminal)}}}var STOP_REAP_MARGIN_MS=5e3,FAILED_START_FINALIZE_MS=7e3,DISCOVERY_BUDGET_MS=9e4,LOCK_WAIT_HEADROOM_MS=1e4,HANDOVER_REPLY_MARGIN_MS=2e4;function startupEnvelope(stallMs=DEFAULT_CHILD_STALLED_STOP_MAX_MS){let decision=STARTUP_DECISION_BUDGET_MS,stop=Math.max(CHILD_STOP_TIMEOUT_MS,stallMs)+CHILD_KILL_EXIT_TIMEOUT_MS+STOP_REAP_MARGIN_MS+SIGKILL_GRACE_MS,cleanup=stop+FAILED_START_FINALIZE_MS,settled=decision+cleanup,holder=DISCOVERY_BUDGET_MS+stop+settled;return{decision,stop,cleanup,settled,discovery:DISCOVERY_BUDGET_MS,holder,lockWait:holder+LOCK_WAIT_HEADROOM_MS,handoverReply:2*holder+HANDOVER_REPLY_MARGIN_MS}}function ensureLockWaitDetail(lockWaitMs){return`RPC host ensure lock wait exceeded ${lockWaitMs}ms; startup not started by this caller`}function startupTimeoutDetail(outcome,cleanup){return[`RPC host startup timed out: phase=${outcome.phase}`,`limit=${outcome.limit}`,`phase_budget_ms=${outcome.phaseBudgetMs}`,`startup_budget_ms=${STARTUP_DECISION_BUDGET_MS}`,`elapsed_ms=${outcome.elapsedMs}`,`cleanup=${cleanup}`].join("; ")}import{randomUUID as randomUUID3}from"node:crypto";import{access,link,readdir as readdir2,rm}from"node:fs/promises";async function electPointer(paths,options={}){let text=await readFileOrUndefined(paths.pointerFile).catch(()=>{});if(text!==void 0){let instanceId=parseJson(text)?.instance_id;return typeof instanceId=="string"?{state:"current",instanceId,candidates:[]}:{state:"missing",instanceId:null,candidates:[]}}let candidates=await provenLiveGenerations(paths),ids=candidates.map(candidate=>candidate.instanceId),[only]=candidates;return only===void 0||candidates.length>1||options.write===!1?{state:"missing",instanceId:null,candidates:ids}:await linkPointer(paths,only)?{state:"elected",instanceId:only.instanceId,candidates:ids}:electPointer(paths,{write:!1})}async function provenLiveGenerations(paths){let found=[];for(let instanceId of(await readdir2(paths.generationsDir).catch(()=>[])).sort()){let generation=generationPaths(paths,instanceId);if(await exists(generation.stopIntentFile)||await exists(generation.drainRequestFile))continue;let text=await readFileOrUndefined(generation.pidFile).catch(()=>{}),record=text===void 0?void 0:parseDaemonPidFile(text),parsed=parseJson(text);if(record===void 0||record.processStartTime===null||parsed?.instance_id!==instanceId||typeof parsed.socket!="string"||!sameEndpoint(parsed.socket,paths.socket))continue;let identity={pid:record.pid,processStartTime:record.processStartTime};await processMatchesPidFile(identity,readProcessStartTime).catch(()=>!1)&&found.push({instanceId,writer:parsed.writer})}return found}async function linkPointer(paths,candidate){let staged=`${paths.pointerFile}.elect-${process.pid}-${randomUUID3()}.tmp`;try{return await writeStateFile(staged,{layout:HOST_DAEMON_LAYOUT,instance_id:candidate.instanceId,generation_dir:generationPaths(paths,candidate.instanceId).relativeDir,...isRecord(candidate.writer)&&{writer:candidate.writer}}),await link(staged,paths.pointerFile),!0}catch{return!1}finally{await rm(staged,{force:!0}).catch(()=>{})}}async function exists(path){return access(path).then(()=>!0,()=>!1)}import{dirname as dirname2,join as join2}from"node:path";var LEGACY_PROBE_TIMEOUT_MS=1e4,DRAIN_POLL_MS=50;async function retireIdleLegacyHost(paths,probe,drainTimeoutMs=LEGACY_PROBE_TIMEOUT_MS){let judged=await judgeLegacyHost(paths,probe);if(judged.verdict==="absent")return;if(judged.verdict==="held")return judged.detail;let{record,socket}=judged.host;if(!await drainAndWait(record,probe,drainTimeoutMs))return`pid ${record.pid} (${socket}) was asked to drain and is still running after ${drainTimeoutMs}ms; retry once it exits`}async function readLegacyHost(paths){let text=await readFileOrUndefined(paths.legacyPidFile),record=text===void 0?void 0:parseDaemonPidFile(text);if(record===void 0)return;let stamped=parseJson(text)?.socket;return{record,socket:typeof stamped=="string"?stamped:join2(dirname2(paths.flatDir),"rpc","rpc.sock")}}async function provenLegacyOwner(paths,socket){let legacy=await readLegacyHost(paths),startTime=legacy?.record.processStartTime;if(!legacy||startTime===null||startTime===void 0||!sameEndpoint(legacy.socket,socket))return;let identity={pid:legacy.record.pid,processStartTime:startTime};return await processMatchesPidFile(identity,readProcessStartTime).catch(error=>(error instanceof ProcessIdentityUnreadableError&&logUnknownHostIdentity("legacy_host.pid",identity.pid),!1))?identity:void 0}async function judgeLegacyHost(paths,probe,platform=process.platform){let host=await readLegacyHost(paths);if(host===void 0)return{verdict:"absent"};let{pid}=host.record,identity=await processMatchesPidFile(host.record,probe).then(owns=>owns?"proven":"gone",error=>{if(error instanceof ProcessIdentityUnreadableError)return"unreadable";throw error});if(identity==="gone")return{verdict:"absent"};let where=`pid ${pid} (${host.socket})`;if(identity==="unreadable")return logUnknownHostIdentity("legacy_host.pid",pid),{verdict:"held",detail:`${where} cannot be proven to be the process its record names; stop it by hand`};if(platform==="win32")return{verdict:"held",detail:`${where} cannot be drained on win32; it exits once idle, then retry`};let protocol=await probeProtocolInfo(host.socket,LEGACY_PROBE_TIMEOUT_MS);if(!protocol?.capabilities.includes(GENERATION_HANDOFF_CAPABILITY))return{verdict:"held",detail:`${where} ${protocol?"cannot drain":"does not answer"}; it is never signalled - stop it by hand, then retry`};let sessions=await probeSessionCount(host.socket,LEGACY_PROBE_TIMEOUT_MS);return sessions===0?{verdict:"idle",host}:{verdict:"held",detail:busyLegacyHostDetail(pid,host.socket,sessions)}}function busyLegacyHostDetail(pid,socket,sessions){return`pid ${pid} (${socket}) holds ${describeSessions(sessions)}; run \`${APP_NAME} host stop --drain --socket ${socket}\` to let it finish that work and exit, then retry`}function describeSessions(sessions){return sessions===void 0?"an unknown number of sessions":`${sessions} open session${sessions===1?"":"s"}`}async function drainAndWait(record,probe,timeoutMs){try{process.kill(record.pid,"SIGUSR1")}catch(error){if(!(error instanceof Error&&"code"in error&&error.code==="ESRCH"))throw error}let stillRunning=()=>processMatchesPidFile(record,probe,processIsLive,{attempts:1}).catch(error=>{if(error instanceof ProcessIdentityUnreadableError)return!0;throw error}),deadline=Date.now()+timeoutMs;for(;await stillRunning();){if(Date.now()>deadline)return!1;await new Promise(resolve2=>setTimeout(resolve2,DRAIN_POLL_MS))}return!0}var EXIT_POLL_MS=100;async function drainHost(paths,options){let named=options.instanceId;if(named!==void 0&&!isGenerationId(named))return{action:"refuse",reason:"unknown_owner"};let registered=named===void 0?await readHostRegistration(paths):await readGenerationRegistration(paths,named),owner=await provenOwner(registered,options.socket);if(owner!==void 0){let generation=generationPaths(paths,owner.instanceId);if((await readDrainWatch(generation.drainWatchFile))?.pid===owner.pid)return await writeDrainRequest(generation.drainRequestFile,{sender:{pid:process.pid,kind:"stop"},reason:"operator_drain"}).catch(cause=>{if(cause.code!=="ENOENT")throw cause}),drained({...owner},options.waitMs)}if(named!==void 0&&owner===void 0)return{action:"refuse",reason:"unknown_owner"};let pid=owner?.pid??(await provenLegacyOwner(paths,options.socket))?.pid;if(pid===void 0)return{action:"refuse",reason:"unknown_owner"};let host=await probeProtocolInfo(options.socket,options.timeoutMs??1e4),answersAsTarget=named===void 0||host?.instanceId===named;return!host?.capabilities.includes(GENERATION_HANDOFF_CAPABILITY)||!answersAsTarget||process.platform==="win32"?{action:"refuse",reason:"drain_unsupported"}:(signalDrain(pid),drained(owner??{pid,processStartTime:void 0},options.waitMs))}async function drained(owner,waitMs){let base={action:"drained",pid:owner.pid,...owner.instanceId&&{instanceId:owner.instanceId}};return waitMs===void 0?base:{...base,exited:await waitForExit(owner,waitMs)}}async function waitForExit(owner,waitMs){let deadline=Date.now()+waitMs;for(;;){if(await exitedNow(owner))return!0;if(Date.now()>=deadline)return!1;await new Promise(resolve2=>setTimeout(resolve2,EXIT_POLL_MS))}}async function exitedNow(owner){if(!processIsLive(owner.pid))return!0;if(owner.processStartTime===void 0)return!1;let identity={pid:owner.pid,processStartTime:owner.processStartTime};return processMatchesPidFile(identity,readProcessStartTime).then(same=>!same,error=>{if(error instanceof ProcessIdentityUnreadableError)return!1;throw error})}function signalDrain(pid){try{process.kill(pid,"SIGUSR1")}catch(error){if(error instanceof Error&&"code"in error&&error.code==="ESRCH")return;throw error}}import{stat as stat2}from"node:fs/promises";function processStartTimeIsUnreadable(){return process.platform==="win32"}var STOP_START_TIME_BUDGET_MS=1e4;function readLiveStartTime(pid){return readProcessStartTime(pid,process.platform,STOP_START_TIME_BUDGET_MS)}async function startedBeforeRegistration(pid,registrationFile){let live=await readLiveStartTime(pid).catch(()=>{}),startedMs=live===void 0?void 0:processStartTimeMs(live);if(startedMs===void 0)return!1;let written=await stat2(registrationFile).catch(()=>{});return written!==void 0&&startedMs<=written.mtimeMs}function parseHostStopGeneration(text){let value=parseJson(text);if(!(!value||typeof value.instanceId!="string"||value.instanceId.length===0||typeof value.generation!="number"||!Number.isSafeInteger(value.generation)||value.generation<0||typeof value.pid!="number"||!Number.isSafeInteger(value.pid)||value.pid<=0||!(value.processStartTime===null||typeof value.processStartTime=="string"&&value.processStartTime.length>0)))return{instanceId:value.instanceId,generation:value.generation,pid:value.pid,processStartTime:value.processStartTime}}function compareStopGeneration(registered,expected){if(registered?.instanceId!==expected.instanceId||registered.generation!==expected.generation||registered.record.pid!==expected.pid)return"stale";let recorded=registered.record.processStartTime;return recorded!==null&&expected.processStartTime!==null?recorded===expected.processStartTime?"proven":"stale":recorded===null&&expected.processStartTime===null&&processStartTimeIsUnreadable()?"bound_without_start_time":"unprovable"}async function stopHost(options){if(options.expectedGeneration!==void 0&&options.drain===!0)throw new TypeError("stopHost: expectedGeneration binds a hard stop and cannot be combined with drain");if(options.expectedGeneration===void 0)return stopHostLocked(options);let release=await acquireHostEnsureLock(options.socket,startupEnvelope(childStalledStopMaxMs()).lockWait);try{return await stopHostLocked(options)}finally{await release()}}async function stopHostLocked(options){let paths=createHostDaemonPaths({socket:options.socket,...options.agentDir?{agentDir:options.agentDir}:{}});await electPointer(paths);let expected=options.expectedGeneration;if(expected!==void 0){let refusal=generationRefusal(compareStopGeneration(await readHostRegistration(paths),expected));if(refusal!==void 0)return refusal}if(options.drain===!0)return drainHost(paths,{socket:options.socket,...options.instanceId!==void 0&&{instanceId:options.instanceId},...options.waitMs!==void 0&&{waitMs:options.waitMs},...options.timeoutMs!==void 0&&{timeoutMs:options.timeoutMs}});let owner=expected===void 0?await provenOwner(await readHostRegistration(paths),options.socket):void 0;if(expected===void 0&&!owner)return{action:"refuse",reason:"unknown_owner"};let host=await probeProtocolInfo(options.socket,options.timeoutMs??1e4),sessions=await probeSessionCount(options.socket,options.timeoutMs??1e4);if(options.force!==!0&&host!==void 0&&sessions!==void 0&&sessions>0)return{action:"refuse",reason:"sessions_live"};if(options.requireAnswer===!0&&(host===void 0||sessions===void 0))return{action:"refuse",reason:"sessions_live"};let target=expected===void 0?owner:await recheckedTarget(paths,options.socket,expected,host);return target===void 0?{action:"refuse",reason:"unknown_owner"}:"action"in target?target:(await writeStopIntent(generationPaths(paths,target.instanceId),{sender:{pid:process.pid,kind:"stop"},targetPid:target.pid,reason:"operator_stop",signal:"SIGTERM",at:new Date().toISOString()}),signalGeneration(target.pid,"SIGTERM")||await releaseGeneration(paths,{instanceId:target.instanceId,pid:target.pid}),{action:"stopped",pid:target.pid})}function generationRefusal(match){if(match==="stale")return{action:"refuse",reason:"stale_generation"};if(match==="unprovable")return{action:"refuse",reason:"unknown_owner"}}async function staleStopGeneration(socket,agentDir,expected){let paths=createHostDaemonPaths({socket,...agentDir?{agentDir}:{}});return generationRefusal(compareStopGeneration(await readHostRegistration(paths),expected))}async function recheckedTarget(paths,socket,expected,host){let current=await readHostRegistration(paths),match=compareStopGeneration(current,expected),refusal=generationRefusal(match);if(refusal!==void 0)return refusal;if(!(match==="proven"&&!await provenOwner(current,socket))){if(match==="bound_without_start_time"){if(current?.socket!==void 0&&!sameEndpoint(current.socket,socket)||!await startedBeforeRegistration(expected.pid,generationPaths(paths,expected.instanceId).pidFile))return;if(host?.instanceId!==void 0&&host.instanceId!==expected.instanceId)return{action:"refuse",reason:"stale_generation"}}return{pid:expected.pid,instanceId:expected.instanceId}}}function signalGeneration(pid,signal){try{return process.kill(pid,signal),!0}catch(error){if(error instanceof Error&&"code"in error&&error.code==="ESRCH")return!1;throw error}}import{lstat as lstat3,readdir as readdir5,rm as rm2,rmdir,unlink}from"node:fs/promises";import{dirname as dirname4,join as join5}from"node:path";import{lstat,readdir as readdir3}from"node:fs/promises";import{createConnection}from"node:net";import{basename as basename2,dirname as dirname3,join as join3}from"node:path";var SOCKET_PROBE_TIMEOUT_MS=2e3;async function endpointInUse(paths,socket){if(await anyGenerationLive(paths))return{inUse:"live_generation"};for(let claim of await readSessionPathClaims(paths.reservationsDir))if(await claimOwnerIsLive(claim.owner))return{inUse:"live_claim"};let silence=await socketSilence(socket);if(silence==="credential_unavailable")return{inUse:silence};if(silence===void 0)return{inUse:"reachable"};let successors=(await socketSiblings(socket)).filter(name=>name.startsWith(`${basename2(socket)}.next-`)&&!isBindScratchName(name));for(let successor of successors){let successorSilence=await socketSilence(join3(dirname3(socket),successor));if(successorSilence==="credential_unavailable")return{inUse:successorSilence};if(successorSilence===void 0)return{inUse:"reachable"}}return{inUse:void 0,silence}}async function socketSiblings(socket){let name=basename2(socket);return(await readdir3(dirname3(socket)).catch(()=>[])).filter(entry=>entry.startsWith(`${name}.next-`)||entry.startsWith(`${name}.shield-`)||entry.startsWith(`${name}${BIND_SCRATCH_INFIX}`))}async function anyGenerationLive(paths){for(let instanceId of await readdir3(paths.generationsDir).catch(()=>[])){if(await hostChildAlive(generationPaths(paths,instanceId)))return!0;let record=generationRecord(await readFileOrUndefined(generationPaths(paths,instanceId).pidFile));if(record!==void 0&&await recordIsLive(record))return!0}return!1}function generationRecord(text){if(text===void 0)return;let record=parseDaemonPidFile(text);if(record!==void 0)return record;let pid=parseJson(text)?.pid;return typeof pid=="number"&&Number.isInteger(pid)&&pid>0?{pid,processStartTime:null}:void 0}async function recordIsLive(record){try{return await processMatchesPidFile(record)}catch(error){if(error instanceof ProcessIdentityUnreadableError)return!0;throw error}}async function socketSilence(socket){let secret;return process.platform==="win32"&&(secret=await readSocketSecret(socketSecretPath(socket)).catch(()=>{}),secret===void 0)?"credential_unavailable":new Promise(resolveProbe=>{let connection=createConnection(resolveSocketTransportAddress(socket,process.platform,secret)),finish=value=>{clearTimeout(timeout),connection.destroy(),resolveProbe(value)},timeout=setTimeout(()=>finish(void 0),SOCKET_PROBE_TIMEOUT_MS);connection.once("connect",()=>finish(void 0)),connection.once("error",error=>{error.code==="ECONNREFUSED"?refusedBySocket(socket).then(finish):error.code==="ENOENT"?finish("socket_absent"):finish(void 0)})})}async function refusedBySocket(socket){if(process.platform==="win32"||socket.startsWith("\0"))return"socket_refused";try{return(await lstat(socket)).isSocket()?"socket_refused":void 0}catch(error){return error instanceof Error&&"code"in error&&error.code==="ENOENT"?"socket_absent":void 0}}import{execFile}from"node:child_process";import{lstat as lstat2,readdir as readdir4,readFile as readFile2,readlink,realpath as realpath2,stat as stat3}from"node:fs/promises";import{join as join4}from"node:path";var LSOF_TIMEOUT_MS=2e3,LINUX_ACCEPTCON_FLAG=65536;async function publicNameMatcher(socket){let names=new Set([socket,await realpath2(socket).catch(()=>socket)]),published=await lstat2(socket).catch(()=>{}),seen=new Map;return async path=>{if(names.has(path))return!0;if(published===void 0||!path.startsWith("/"))return!1;let known=seen.get(path);if(known!==void 0)return known;let entry=await stat3(path).catch(()=>{}),same=entry!==void 0&&entry.dev===published.dev&&entry.ino===published.ino;return seen.set(path,same),same}}async function countPublicClients(supervisorPid,socket){let matches=await publicNameMatcher(socket),descriptors=process.platform==="darwin"?await darwinDescriptors(supervisorPid,matches):process.platform==="linux"?await linuxDescriptors(supervisorPid,matches):void 0;if(!(descriptors===void 0||descriptors.listeners===0))return descriptors.total-descriptors.listeners}async function darwinDescriptors(pid,matches){let stdout=await new Promise(resolve2=>{execFile("lsof",["-a","-p",String(pid),"-U","-w","-F","n"],{timeout:LSOF_TIMEOUT_MS},(error,out)=>resolve2(error?void 0:out))});if(stdout===void 0)return;let total=0;for(let line of stdout.split(`
|
|
7
7
|
`))line.startsWith("n")&&await matches(line.slice(1))&&(total+=1);return{total,listeners:total>0?1:0}}async function linuxDescriptors(pid,matches){try{let inodes=new Set;for(let fd of await readdir4(join4("/proc",String(pid),"fd"))){let target=await readlink(join4("/proc",String(pid),"fd",fd)).catch(()=>""),inode=/^socket:\[(\d+)\]$/.exec(target)?.[1];inode!==void 0&&inodes.add(inode)}let total=0,listeners=0;for(let line of(await readFile2("/proc/net/unix","utf8")).split(`
|
|
8
|
-
`).slice(1)){let fields=line.trim().split(/\s+/),[flags,inode,path]=[fields[3],fields[6],fields[7]];inode===void 0||path===void 0||!inodes.has(inode)||!await matches(path)||(total+=1,(Number.parseInt(flags??"0",16)&LINUX_ACCEPTCON_FLAG)!==0&&(listeners+=1))}return{total,listeners}}catch{return}}var STALE_ENGINE_GRACE_MS=10*6e4,PROBE_TIMEOUT_MS=2e3;async function judgeStaleEngine(socket,agentDir,client,options={}){let paths=createHostDaemonPaths({socket,agentDir}),owner=await provenOwner(await readHostRegistration(paths),socket);if(owner===void 0)return keep("stale_unproven_owner");let clients=await countPublicClients(owner.pid,socket),info=await observeProtocolInfo(socket,PROBE_TIMEOUT_MS);if(info===void 0)return keep("stale_unreachable");let hostOrdinal=info.engineOrdinal,hostIdentity=hostOrdinal===void 0?void 0:{text:info.engineVersion??"",ordinal:hostOrdinal,scheme:hostOrdinal[4]>0?"epoch":"nodef"};if(hostIdentity===void 0||compareEngineOrdinal(hostIdentity,client)!==-1)return keep("stale_not_older");let policy=resolveHostPolicy(parseJson(await readFileOrUndefined(paths.settingsFile)),{});if(policy.coldStart==="persistent")return keep("stale_persistent");if(await observedSessionCount(socket)!==0)return keep("stale_sessions");if((await readSessionPathClaims(paths.reservationsDir)).length>0)return keep("stale_claims");let startedAt=processStartTimeMs(owner.processStartTime),now=(options.now??Date.now)();return startedAt===void 0||now-startedAt<policy.idleExitMs+(options.graceMs??STALE_ENGINE_GRACE_MS)?keep("stale_young"):clients!==0?keep("stale_clients"):{stale:!0,pid:owner.pid}}function keep(reason){return{stale:!1,reason}}async function observedSessionCount(socket){let reply=await requestOnSocket(socket,{type:"list_sessions",include_workers:!0,[OBSERVE_REQUEST_FIELD]:!0},PROBE_TIMEOUT_MS);if(typeof reply!="object"||reply===null)return;let sessions=Reflect.get(reply,"sessions");return Array.isArray(sessions)?sessions.length:void 0}var GC_LOCK_WAIT_MS=2e3;async function gcHostEndpoints(agentDir,options={}){process.platform!=="win32"&&await gcRpcPublicKeys(agentDir);let removed=[],stopped=[],kept=[],wanted=kind=>options.kinds===void 0||options.kinds.includes(kind),legacy=wanted("rpc_host")?await legacyFlatDirectory(agentDir):void 0;legacy!==void 0&&kept.push({socket:null,dir:legacy,reason:"legacy_layout"});for(let endpoint of await listHostEndpoints(agentDir)){if(!wanted(endpoint.endpoint_kind))continue;if(endpoint.socket===null){kept.push({socket:null,dir:endpoint.dir,reason:"unknown_identity"});continue}let entry={socket:endpoint.socket,dir:endpoint.dir};try{let outcome=await gcEndpoint(endpoint.socket,endpoint.dir,options);"stopped"in outcome?stopped.push({...entry,reason:"stale_old_engine",pid:outcome.pid}):outcome.removed?outcome.skipped.length===0?removed.push({...entry,reason:outcome.reason}):removed.push({...entry,reason:outcome.reason,skipped:outcome.skipped}):kept.push({...entry,reason:outcome.reason})}catch(error){kept.push({...entry,reason:"failed",error:error instanceof Error?error.message:String(error)})}}return stopped.length===0?{removed,kept}:{removed,stopped,kept}}async function gcEndpoint(socket,dir,options){let release=await acquireEnsureLock(socket);if(release===void 0)return{removed:!1,reason:"locked"};try{await options._test?.afterLockAcquired?.(socket);let evidence=await endpointInUse(hostDaemonDirectoryPaths(dir),socket);if(evidence.inUse==="live_claim"||evidence.inUse!==void 0&&options.staleEngine===void 0)return{removed:!1,reason:evidence.inUse};if(evidence.inUse!==void 0)return await stopIfStale(socket,dir,evidence.inUse,options.staleEngine??{});let skipped=await removeEndpoint(socket,dir);return{removed:!0,reason:evidence.silence,skipped}}finally{await release()}}var NOT_A_CANDIDATE=new Set(["stale_unproven_owner","stale_unreachable","stale_not_older"]),STALE_STOP_TIMEOUT_MS=2e3;async function stopIfStale(socket,dir,inUse,options){let agentDir=dirname4(dirname4(dir)),verdict=await judgeStaleEngine(socket,agentDir,options.client??engineBuildIdentity(),options);if(!verdict.stale)return{removed:!1,reason:NOT_A_CANDIDATE.has(verdict.reason)?inUse:verdict.reason};let result=await stopHost({socket,agentDir,timeoutMs:STALE_STOP_TIMEOUT_MS,requireAnswer:!0});return result.action==="refuse"?{removed:!1,reason:result.reason==="unknown_owner"?"stale_unproven_owner":"stale_sessions"}:{removed:!1,stopped:!0,pid:result.pid}}async function removeEndpoint(socket,dir){let skipped=[];for(let sibling of await socketSiblings(socket)){let path=join5(dirname4(socket),sibling),entry=await lstat3(path).catch(error=>{if(!(error instanceof Error&&"code"in error&&error.code==="ENOENT"))throw error});entry?.isDirectory()?(!isBindScratchName(sibling)||!await reclaimBindDir(path))&&skipped.push({path,type:"directory"}):entry!==void 0&&await rm2(path,{force:!0})}return await rm2(socket,{force:!0}),await rm2(dir,{recursive:!0,force:!0}),skipped}async function reclaimBindDir(path){try{let inside=await readdir5(path);if(inside.some(name=>name!==BIND_SCRATCH_SOCKET))return!1;let bound=join5(path,BIND_SCRATCH_SOCKET);if(inside.length>0){if(!(await lstat3(bound)).isSocket())return!1;await unlink(bound)}return await rmdir(path),!0}catch{return!1}}async function acquireEnsureLock(socket){return acquireHostEnsureLock(socket,GC_LOCK_WAIT_MS).catch(()=>{})}async function legacyFlatDirectory(agentDir){let flatDir=join5(agentDir,"rpc-host-daemon");return parseJson(await readFileOrUndefined(join5(flatDir,"layout.json")).catch(()=>{}))?.layout===HOST_DAEMON_LAYOUT?void 0:await readFileOrUndefined(join5(flatDir,"host.pid")).catch(()=>{})!==void 0?flatDir:void 0}export{engineBuildIdentityFrom,engineBuildIdentity,compareEngineOrdinal,CUSTOM_UNSUPPORTED_CAPABILITY,EXTENSION_EVENTS_CAPABILITY,EVAL_CELLS_CAPABILITY,AUTO_TITLE_SESSIONS_CAPABILITY,CONTINUE_FROM_LEAF_CAPABILITY,SESSION_HELD_CAPABILITY,RESUME_SESSION_CAPABILITY,ATTENDED_CAPABILITY,MEDIA_PLACEHOLDERS_CAPABILITY,COMPACT_EVENTS_CAPABILITY,QUESTION_CAPABILITY,TOOL_BARRIER_CAPABILITY,SESSION_RELOCATION_CAPABILITY,RETAIN_ON_DISCONNECT_CAPABILITY,DELEGATED_CONNECTION_CAPABILITY,DELEGATION_ROLES_CAPABILITY,PEER_AGENT_ROLE_CAPABILITY,SAME_ACCOUNT_DELEGATION_CAPABILITY,RAISER_NOTICE_CAPABILITY,DETACH_CONNECTION_CAPABILITY,INTERACTIVE_USER_CAPABILITY,OBSERVE_READS_CAPABILITY,ENTRIES_TAIL_CAPABILITY,TUI_OBSERVER_CAPABILITY,TASK_PARENT_CAPABILITY,SESSION_CONTEXT_CAPABILITY,SESSION_KIND_CAPABILITY,SESSION_STATUS_CAPABILITY,AUTO_TITLE_PER_SESSION_CAPABILITY,WARM_CAPABILITY,PARK_CLAIM_CAPABILITY,DURABLE_SESSION_ID_CAPABILITY,HAND_SELECTION_CAPABILITY,MOVED_PATH_GUARD_CAPABILITY,PROMPT_SURFACE_CAPABILITY,BROWSER_ENGINE_CAPABILITY,SESSION_ENV_CAPABILITY,RETRY_FALLBACK_PROFILE_CAPABILITY,RETRY_FALLBACK_COMMAND_CAPABILITY,PROMPT_SURFACE_CHAT_CAPABILITY,RPC_CLIENT_CAPABILITIES_ENV,parseClientCapabilities,DEFAULT_CUSTOM_EXTENSION_LABEL,buildCustomUnsupportedRequest,AUTH_KEY_VERIFY_CAPABILITY,PROVIDER_ACCOUNT_TEST_CAPABILITY,ACCOUNT_RESET_CAPABILITY,QUEUED_MESSAGE_REVISION_CAPABILITY,pathIdentityResolverStatus,RUNTIME_IDENTITY_HANDOVER_CAPABILITY,computeRuntimeBuildId,parseHostProtocolInfo,HOST_PROTOCOL_VERSION,GENERATION_HANDOFF_CAPABILITY,ENDPOINT_OWNER_FENCE_CAPABILITY,REQUIRED_HOST_CAPABILITIES,HostEnsureRefusedError,decideHostAction,RPC_METHOD_CLASSES_VERSION,RPC_METHOD_CLASS,classifyRpcMethod,RPC_METHOD_CLASSES_DIGEST,describeRpcMethodClasses,hostInstanceId,hostGeneration,hostLaunchProfile,socketHostLaunchProfile,protocolIdentity,DEFAULT_PROBE_TIMEOUT_MS,probeHost,probeProtocolInfo,observeProtocolInfo,holdProtocolInfo,probeSocketReachable,holdSessionCount,DEFAULT_STOP_TIMEOUT_MS,SIGKILL_GRACE_MS,STOP_WAIT_BUDGET_MS,ensureSender,announceStop,matchesPidFileOrUnknown,recordEscalation,stopSpawnedChild,stopManagedHost,resolvePidFileOwnership,signalPid,delay,isNodeErrorCode,STARTUP_DECISION_BUDGET_MS,startStartupController,startupEnvelope,ensureLockWaitDetail,startupTimeoutDetail,anyGenerationLive,judgeStaleEngine,electPointer,retireIdleLegacyHost,provenLegacyOwner,busyLegacyHostDetail,describeSessions,parseHostStopGeneration,stopHost,staleStopGeneration,signalGeneration,gcHostEndpoints,gcEndpoint};
|
|
8
|
+
`).slice(1)){let fields=line.trim().split(/\s+/),[flags,inode,path]=[fields[3],fields[6],fields[7]];inode===void 0||path===void 0||!inodes.has(inode)||!await matches(path)||(total+=1,(Number.parseInt(flags??"0",16)&LINUX_ACCEPTCON_FLAG)!==0&&(listeners+=1))}return{total,listeners}}catch{return}}var STALE_ENGINE_GRACE_MS=10*6e4,PROBE_TIMEOUT_MS=2e3;async function judgeStaleEngine(socket,agentDir,client,options={}){let paths=createHostDaemonPaths({socket,agentDir}),owner=await provenOwner(await readHostRegistration(paths),socket);if(owner===void 0)return keep("stale_unproven_owner");let clients=await countPublicClients(owner.pid,socket),info=await observeProtocolInfo(socket,PROBE_TIMEOUT_MS);if(info===void 0)return keep("stale_unreachable");let hostOrdinal=info.engineOrdinal,hostIdentity=hostOrdinal===void 0?void 0:{text:info.engineVersion??"",ordinal:hostOrdinal,scheme:hostOrdinal[4]>0?"epoch":"nodef"};if(hostIdentity===void 0||compareEngineOrdinal(hostIdentity,client)!==-1)return keep("stale_not_older");let policy=resolveHostPolicy(parseJson(await readFileOrUndefined(paths.settingsFile)),{});if(policy.coldStart==="persistent")return keep("stale_persistent");if(await observedSessionCount(socket)!==0)return keep("stale_sessions");if((await readSessionPathClaims(paths.reservationsDir)).length>0)return keep("stale_claims");let startedAt=processStartTimeMs(owner.processStartTime),now=(options.now??Date.now)();return startedAt===void 0||now-startedAt<policy.idleExitMs+(options.graceMs??STALE_ENGINE_GRACE_MS)?keep("stale_young"):clients!==0?keep("stale_clients"):{stale:!0,pid:owner.pid}}function keep(reason){return{stale:!1,reason}}async function observedSessionCount(socket){let reply=await requestOnSocket(socket,{type:"list_sessions",include_workers:!0,[OBSERVE_REQUEST_FIELD]:!0},PROBE_TIMEOUT_MS);if(typeof reply!="object"||reply===null)return;let sessions=Reflect.get(reply,"sessions");return Array.isArray(sessions)?sessions.length:void 0}var GC_LOCK_WAIT_MS=2e3;async function gcHostEndpoints(agentDir,options={}){process.platform!=="win32"&&await gcRpcPublicKeys(agentDir);let removed=[],stopped=[],kept=[],wanted=kind=>options.kinds===void 0||options.kinds.includes(kind),legacy=wanted("rpc_host")?await legacyFlatDirectory(agentDir):void 0;legacy!==void 0&&kept.push({socket:null,dir:legacy,reason:"legacy_layout"});for(let endpoint of await listHostEndpoints(agentDir)){if(!wanted(endpoint.endpoint_kind))continue;if(endpoint.socket===null){kept.push({socket:null,dir:endpoint.dir,reason:"unknown_identity"});continue}let entry={socket:endpoint.socket,dir:endpoint.dir};try{let outcome=await gcEndpoint(endpoint.socket,endpoint.dir,options);"stopped"in outcome?stopped.push({...entry,reason:"stale_old_engine",pid:outcome.pid}):outcome.removed?outcome.skipped.length===0?removed.push({...entry,reason:outcome.reason}):removed.push({...entry,reason:outcome.reason,skipped:outcome.skipped}):kept.push({...entry,reason:outcome.reason})}catch(error){kept.push({...entry,reason:"failed",error:error instanceof Error?error.message:String(error)})}}return stopped.length===0?{removed,kept}:{removed,stopped,kept}}async function gcEndpoint(socket,dir,options){let release=await acquireEnsureLock(socket);if(release===void 0)return{removed:!1,reason:"locked"};try{await options._test?.afterLockAcquired?.(socket);let evidence=await endpointInUse(hostDaemonDirectoryPaths(dir),socket);if(evidence.inUse==="live_claim"||evidence.inUse==="credential_unavailable"||evidence.inUse!==void 0&&options.staleEngine===void 0)return{removed:!1,reason:evidence.inUse};if(evidence.inUse!==void 0)return await stopIfStale(socket,dir,evidence.inUse,options.staleEngine??{});let skipped=await removeEndpoint(socket,dir);return{removed:!0,reason:evidence.silence,skipped}}finally{await release()}}var NOT_A_CANDIDATE=new Set(["stale_unproven_owner","stale_unreachable","stale_not_older"]),STALE_STOP_TIMEOUT_MS=2e3;async function stopIfStale(socket,dir,inUse,options){let agentDir=dirname4(dirname4(dir)),verdict=await judgeStaleEngine(socket,agentDir,options.client??engineBuildIdentity(),options);if(!verdict.stale)return{removed:!1,reason:NOT_A_CANDIDATE.has(verdict.reason)?inUse:verdict.reason};let result=await stopHost({socket,agentDir,timeoutMs:STALE_STOP_TIMEOUT_MS,requireAnswer:!0});return result.action==="refuse"?{removed:!1,reason:result.reason==="unknown_owner"?"stale_unproven_owner":"stale_sessions"}:{removed:!1,stopped:!0,pid:result.pid}}async function removeEndpoint(socket,dir){let skipped=[];for(let sibling of await socketSiblings(socket)){let path=join5(dirname4(socket),sibling),entry=await lstat3(path).catch(error=>{if(!(error instanceof Error&&"code"in error&&error.code==="ENOENT"))throw error});entry?.isDirectory()?(!isBindScratchName(sibling)||!await reclaimBindDir(path))&&skipped.push({path,type:"directory"}):entry!==void 0&&await rm2(path,{force:!0})}return await rm2(socket,{force:!0}),await rm2(dir,{recursive:!0,force:!0}),skipped}async function reclaimBindDir(path){try{let inside=await readdir5(path);if(inside.some(name=>name!==BIND_SCRATCH_SOCKET))return!1;let bound=join5(path,BIND_SCRATCH_SOCKET);if(inside.length>0){if(!(await lstat3(bound)).isSocket())return!1;await unlink(bound)}return await rmdir(path),!0}catch{return!1}}async function acquireEnsureLock(socket){return acquireHostEnsureLock(socket,GC_LOCK_WAIT_MS).catch(()=>{})}async function legacyFlatDirectory(agentDir){let flatDir=join5(agentDir,"rpc-host-daemon");return parseJson(await readFileOrUndefined(join5(flatDir,"layout.json")).catch(()=>{}))?.layout===HOST_DAEMON_LAYOUT?void 0:await readFileOrUndefined(join5(flatDir,"host.pid")).catch(()=>{})!==void 0?flatDir:void 0}export{engineBuildIdentityFrom,engineBuildIdentity,compareEngineOrdinal,CUSTOM_UNSUPPORTED_CAPABILITY,EXTENSION_EVENTS_CAPABILITY,EVAL_CELLS_CAPABILITY,AUTO_TITLE_SESSIONS_CAPABILITY,CONTINUE_FROM_LEAF_CAPABILITY,SESSION_HELD_CAPABILITY,RESUME_SESSION_CAPABILITY,ATTENDED_CAPABILITY,MEDIA_PLACEHOLDERS_CAPABILITY,COMPACT_EVENTS_CAPABILITY,QUESTION_CAPABILITY,TOOL_BARRIER_CAPABILITY,SESSION_RELOCATION_CAPABILITY,RETAIN_ON_DISCONNECT_CAPABILITY,DELEGATED_CONNECTION_CAPABILITY,DELEGATION_ROLES_CAPABILITY,PEER_AGENT_ROLE_CAPABILITY,SAME_ACCOUNT_DELEGATION_CAPABILITY,RAISER_NOTICE_CAPABILITY,DETACH_CONNECTION_CAPABILITY,INTERACTIVE_USER_CAPABILITY,OBSERVE_READS_CAPABILITY,ENTRIES_TAIL_CAPABILITY,TUI_OBSERVER_CAPABILITY,TASK_PARENT_CAPABILITY,SESSION_CONTEXT_CAPABILITY,SESSION_KIND_CAPABILITY,SESSION_STATUS_CAPABILITY,AUTO_TITLE_PER_SESSION_CAPABILITY,WARM_CAPABILITY,PARK_CLAIM_CAPABILITY,DURABLE_SESSION_ID_CAPABILITY,HAND_SELECTION_CAPABILITY,MOVED_PATH_GUARD_CAPABILITY,PROMPT_SURFACE_CAPABILITY,BROWSER_ENGINE_CAPABILITY,SESSION_ENV_CAPABILITY,RETRY_FALLBACK_PROFILE_CAPABILITY,RETRY_FALLBACK_COMMAND_CAPABILITY,PROMPT_SURFACE_CHAT_CAPABILITY,RPC_CLIENT_CAPABILITIES_ENV,parseClientCapabilities,DEFAULT_CUSTOM_EXTENSION_LABEL,buildCustomUnsupportedRequest,AUTH_KEY_VERIFY_CAPABILITY,PROVIDER_ACCOUNT_TEST_CAPABILITY,ACCOUNT_RESET_CAPABILITY,QUEUED_MESSAGE_REVISION_CAPABILITY,pathIdentityResolverStatus,RUNTIME_IDENTITY_HANDOVER_CAPABILITY,computeRuntimeBuildId,parseHostProtocolInfo,HOST_PROTOCOL_VERSION,GENERATION_HANDOFF_CAPABILITY,ENDPOINT_OWNER_FENCE_CAPABILITY,REQUIRED_HOST_CAPABILITIES,HostEnsureRefusedError,decideHostAction,RPC_METHOD_CLASSES_VERSION,RPC_METHOD_CLASS,classifyRpcMethod,RPC_METHOD_CLASSES_DIGEST,describeRpcMethodClasses,hostInstanceId,hostGeneration,hostLaunchProfile,socketHostLaunchProfile,protocolIdentity,DEFAULT_PROBE_TIMEOUT_MS,probeHost,probeProtocolInfo,observeProtocolInfo,holdProtocolInfo,probeSocketReachable,holdSessionCount,DEFAULT_STOP_TIMEOUT_MS,SIGKILL_GRACE_MS,STOP_WAIT_BUDGET_MS,ensureSender,announceStop,matchesPidFileOrUnknown,recordEscalation,stopSpawnedChild,stopManagedHost,resolvePidFileOwnership,signalPid,delay,isNodeErrorCode,STARTUP_DECISION_BUDGET_MS,startStartupController,startupEnvelope,ensureLockWaitDetail,startupTimeoutDetail,anyGenerationLive,judgeStaleEngine,electPointer,retireIdleLegacyHost,provenLegacyOwner,busyLegacyHostDetail,describeSessions,parseHostStopGeneration,stopHost,staleStopGeneration,signalGeneration,gcHostEndpoints,gcEndpoint};
|