@code-yeongyu/senpi 2026.9.18-3 → 2026.9.18-5

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/bundle/chunks/{app-server-command-SXMDPCO3.js → app-server-command-YD42KU4X.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-KFIHWCMN.js → chunk-AQS2EBFQ.js} +1 -1
  4. package/dist/bundle/chunks/chunk-CXHQ4ABL.js +8 -0
  5. package/dist/bundle/chunks/{chunk-2LSXWYT5.js → chunk-SY2KKJ4R.js} +2 -2
  6. package/dist/bundle/chunks/{chunk-FYFLULRU.js → chunk-XBOKWPXY.js} +7 -7
  7. package/dist/bundle/chunks/{cli-main-ACDTRT2W.js → cli-main-6IY36LF7.js} +1 -1
  8. package/dist/bundle/chunks/{host-command-NU5TYUOI.js → host-command-NXX3ORNL.js} +1 -1
  9. package/dist/bundle/chunks/{interactive-host-runtime-FAKC3LHA.js → interactive-host-runtime-D45DGSLG.js} +1 -1
  10. package/dist/bundle/chunks/{interactive-mode-4YCBGS3E.js → interactive-mode-CBLQGJOC.js} +1 -1
  11. package/dist/bundle/chunks/{multi-session-host-HBY64KFU.js → multi-session-host-ZCLTJMBL.js} +1 -1
  12. package/dist/bundle/chunks/{package-manager-cli-23TYIHVC.js → package-manager-cli-D2KVIUEQ.js} +1 -1
  13. package/dist/bundle/chunks/{rpc-mode-ARUKMUCA.js → rpc-mode-B5DSVT2G.js} +1 -1
  14. package/dist/bundle/chunks/session-worker.js +59 -59
  15. package/dist/bundle/cli.js +1 -1
  16. package/dist/bundle/index.js +1 -1
  17. package/dist/bundle/rpc-entry.js +1 -1
  18. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts +1 -1
  19. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js +2 -17
  21. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js.map +1 -1
  22. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.d.ts +3 -0
  23. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.d.ts.map +1 -0
  24. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.js +5 -0
  25. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.js.map +1 -0
  26. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.d.ts +3 -0
  27. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.d.ts.map +1 -0
  28. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.js +28 -0
  29. package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.js.map +1 -0
  30. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/prompt-preset/presets.js +21 -1
  32. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  33. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  34. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  36. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  37. package/dist/modes/rpc/host-decision.d.ts +1 -1
  38. package/dist/modes/rpc/host-decision.d.ts.map +1 -1
  39. package/dist/modes/rpc/host-decision.js +2 -0
  40. package/dist/modes/rpc/host-decision.js.map +1 -1
  41. package/dist/modes/rpc/host-ensure.js +8 -1
  42. package/dist/modes/rpc/host-ensure.js.map +1 -1
  43. package/dist/modes/rpc/host-probe.d.ts +7 -0
  44. package/dist/modes/rpc/host-probe.d.ts.map +1 -1
  45. package/dist/modes/rpc/host-probe.js +15 -4
  46. package/dist/modes/rpc/host-probe.js.map +1 -1
  47. package/docs/settings.md +1 -1
  48. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  49. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  50. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  51. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +0 -2
  52. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  53. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +0 -2
  54. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  55. package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts +150 -7
  56. package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
  57. package/node_modules/@earendil-works/pi-ai/dist/providers/all.js +15 -3
  58. package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
  59. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  60. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  61. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts +142 -1
  62. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts.map +1 -1
  63. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js +156 -3
  64. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js.map +1 -1
  65. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  66. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  67. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  68. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  69. package/package.json +7 -7
  70. package/dist/bundle/chunks/chunk-MA3Z67LK.js +0 -8
  71. package/node_modules/@earendil-works/pi-ai/dist/providers/data/kimi-coding.json +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"host-decision.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-decision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a client does when it finds - or does not find - a host on the shared RPC socket.\n *\n * One machine-wide host serves several client surfaces, so the process on the socket is\n * usually NOT this process's child. Two invariants follow, and this module is where they\n * are decided once instead of in every client:\n *\n * I1 - never terminate, signal or replace a host you did not start. The only sanctioned\n * replacement is the drain-based generation handoff, which ends no running work.\n * I2 - compatibility is `protocolVersion` + capabilities, NEVER a version-string compare,\n * and \"is my build newer?\" is `compareEngineOrdinal`, never `serverVersion`. An\n * ordinal that cannot be compared (a build without git metadata, a host that reports\n * none) is EQUAL, and since a handoff needs STRICTLY greater, such a pair attaches.\n *\n * The profile rule is the third guard: a handoff additionally requires the candidate's\n * extension set to be a SUPERSET of the running host's. Handing off to a narrower generation\n * would silently drop whatever the running host loaded (an editor plugin, a member bundle)\n * from every session that reopens in the new generation - so a narrower candidate attaches\n * and says so instead.\n */\nimport {\n\tcompareEngineOrdinal,\n\ttype EngineBuildIdentity,\n\ttype EngineOrdinal,\n} from \"../../core/engine-build-identity.ts\";\nimport {\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n} from \"./custom-capability.ts\";\nimport type { HostProtocolInfo } from \"./host-protocol-info.ts\";\nimport type { RpcLaunchProfile } from \"./rpc-types.ts\";\n\nexport { type HostProtocolInfo, parseHostProtocolInfo } from \"./host-protocol-info.ts\";\n\n/** The wire protocol this build speaks. A host answering with any other number is refused, not replaced. */\nexport const HOST_PROTOCOL_VERSION = 1;\n\n/**\n * HOST capability: this host drains into a successor generation on SIGUSR1 instead of dying with\n * its sessions. A host without it is attach-only - it is never renamed, signalled or upgraded.\n */\nexport const GENERATION_HANDOFF_CAPABILITY = \"generation_handoff\";\n\n/** What a daemon session needs from a host before any client may attach to it. */\nexport const REQUIRED_HOST_CAPABILITIES = [\n\t\"multi_session\",\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n] as const;\n\nexport interface HostDecisionClient {\n\treadonly protocolVersion: number;\n\treadonly requiredCapabilities: readonly string[];\n\treadonly identity: EngineBuildIdentity;\n\t/**\n\t * What THIS client would launch a host with. Absent = no launch spec (a bare CLI attaching to\n\t * whatever is running): such a client can prove nothing about extensions, so it never hands off.\n\t */\n\treadonly launchProfile?: RpcLaunchProfile;\n\t/** True when the running host's pidfile records this process as its writer (the I1 question). */\n\treadonly startedByUs: boolean;\n\t/** The platform both sides run on - one daemon is machine-local, so the host shares it. */\n\treadonly platform: NodeJS.Platform;\n}\n\n/** `upgrade` may hand off, `fallback` prefers no host over a mismatched one, `never` only attaches. */\nexport type HostDecisionPolicy = \"upgrade\" | \"fallback\" | \"never\";\n\nexport type HostAction = HostDecision[\"action\"];\n\n/** Attached anyway, with something the caller should say out loud. */\nexport type HostDecisionWarning =\n\t/** This client's extension set is missing something the host loads; upgrading would drop it. */\n\t| \"profile_narrower_attached\"\n\t/** The profiles differ (or cannot be compared) and no upgrade was possible. */\n\t| \"profile_mismatch_attached\";\n\n/** Every action carries only the reasons that can produce it, so an impossible pair cannot be built. */\nexport type HostDecision =\n\t| {\n\t\t\treadonly action: \"start\";\n\t\t\t/** `no_host`: nothing answered. `restart_own_host`: nothing answered and the pidfile is ours. */\n\t\t\treadonly reason: \"no_host\" | \"restart_own_host\";\n\t\t\treadonly upgradeable: false;\n\t }\n\t| { readonly action: \"refuse\"; readonly reason: \"protocol\" | \"capability\"; readonly upgradeable: false }\n\t| {\n\t\t\treadonly action: \"fallback\";\n\t\t\treadonly reason: \"capability\" | \"engine_mismatch\";\n\t\t\treadonly upgradeable: boolean;\n\t }\n\t| {\n\t\t\treadonly action: \"reuse\";\n\t\t\t/** `handoff_unsupported`: the host predates the drain handoff. `win32_attach_only`: named pipes cannot be renamed and win32 has no SIGUSR1. */\n\t\t\treadonly reason: \"compatible\" | \"handoff_unsupported\" | \"win32_attach_only\";\n\t\t\t/** Whether a generation handoff against this host is possible at all, independent of the policy. */\n\t\t\treadonly upgradeable: boolean;\n\t\t\treadonly warning?: HostDecisionWarning;\n\t }\n\t| { readonly action: \"handoff\"; readonly reason: \"newer_engine\" | \"profile\"; readonly upgradeable: true };\n\n/** What a `never` policy can answer: it attaches, starts or refuses, and never touches a running host. */\nexport type AttachOnlyDecision = Extract<HostDecision, { action: \"start\" | \"reuse\" | \"refuse\" }>;\n\n/** Why an ensure refused. The decision's own refusals plus the one only a pidfile can produce. */\nexport type HostRefusalReason = \"protocol\" | \"capability\" | \"foreign_writer\" | \"legacy_host\";\n\n/**\n * An ensure that found an unusable host and refused to act on it. A refusal is FINAL by design:\n * this process may not signal a host it did not start (I1), and starting a second host on an\n * endpoint another process already owns is exactly the failure the refusal exists to prevent.\n */\nexport class HostEnsureRefusedError extends Error {\n\treadonly socket: string;\n\treadonly reason: HostRefusalReason;\n\n\tconstructor(socket: string, reason: HostRefusalReason, host: HostProtocolInfo | undefined) {\n\t\tsuper(`RPC socket ${socket} refused: ${reason} - ${refusalDetail(reason, host)}`);\n\t\tthis.name = \"HostEnsureRefusedError\";\n\t\tthis.socket = socket;\n\t\tthis.reason = reason;\n\t}\n}\n\nfunction refusalDetail(reason: HostRefusalReason, host: HostProtocolInfo | undefined): string {\n\tswitch (reason) {\n\t\tcase \"protocol\":\n\t\t\treturn `the running host speaks protocol version ${host?.protocolVersion ?? \"unknown\"}, this build speaks ${HOST_PROTOCOL_VERSION}`;\n\t\tcase \"capability\": {\n\t\t\tconst missing = REQUIRED_HOST_CAPABILITIES.filter((capability) => !host?.capabilities.includes(capability));\n\t\t\treturn `the running host is missing ${JSON.stringify(missing)}; it owns the socket, so no second host is started`;\n\t\t}\n\t\tcase \"foreign_writer\":\n\t\t\treturn \"its pidfile was written by another process, so this one may not signal it; stop that host explicitly instead\";\n\t\tcase \"legacy_host\":\n\t\t\treturn \"a host from before the per-socket daemon directory is still running on this endpoint; it is never signalled, and no second host is started beside it\";\n\t\tdefault:\n\t\t\treturn assertNever(reason);\n\t}\n}\n\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: \"never\",\n): AttachOnlyDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision {\n\tif (host === undefined) {\n\t\treturn { action: \"start\", reason: client.startedByUs ? \"restart_own_host\" : \"no_host\", upgradeable: false };\n\t}\n\tif (host.protocolVersion !== client.protocolVersion) {\n\t\treturn { action: \"refuse\", reason: \"protocol\", upgradeable: false };\n\t}\n\tif (!client.requiredCapabilities.every((capability) => host.capabilities.includes(capability))) {\n\t\t// A host that cannot serve this client still OWNS the socket: starting a second one would\n\t\t// bind over an endpoint another process is serving. Refuse, or fall back to no host at all.\n\t\tif (policy === \"fallback\") return { action: \"fallback\", reason: \"capability\", upgradeable: false };\n\t\treturn { action: \"refuse\", reason: \"capability\", upgradeable: false };\n\t}\n\tconst upgradeable = client.platform !== \"win32\" && host.capabilities.includes(GENERATION_HANDOFF_CAPABILITY);\n\tif (policy === \"fallback\" && host.engineVersion !== client.identity.text) {\n\t\treturn { action: \"fallback\", reason: \"engine_mismatch\", upgradeable };\n\t}\n\tconst warning = profileWarning(client, host);\n\tif (client.platform === \"win32\") {\n\t\treturn { action: \"reuse\", reason: \"win32_attach_only\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (!upgradeable) {\n\t\treturn { action: \"reuse\", reason: \"handoff_unsupported\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (policy === \"upgrade\" && client.launchProfile && host.launch_profile && supersedes(client, host)) {\n\t\treturn {\n\t\t\taction: \"handoff\",\n\t\t\treason: sameRelease(client.identity.ordinal, host.engineOrdinal) ? \"profile\" : \"newer_engine\",\n\t\t\tupgradeable: true,\n\t\t};\n\t}\n\treturn { action: \"reuse\", reason: \"compatible\", upgradeable, ...(warning && { warning }) };\n}\n\n/** A strictly newer build whose extensions cover everything the host loads - the only handoff candidate. */\nfunction supersedes(client: HostDecisionClient, host: HostProtocolInfo): boolean {\n\tif (host.engineOrdinal === undefined) return false;\n\tconst hostIdentity: EngineBuildIdentity = {\n\t\ttext: host.engineVersion ?? \"\",\n\t\tordinal: host.engineOrdinal,\n\t\tscheme: host.engineOrdinal[4] > 0 ? \"epoch\" : \"nodef\",\n\t};\n\tif (compareEngineOrdinal(client.identity, hostIdentity) <= 0) return false;\n\treturn covers(client.launchProfile, host.launch_profile);\n}\n\nfunction profileWarning(client: HostDecisionClient, host: HostProtocolInfo): HostDecisionWarning | undefined {\n\tif (client.launchProfile === undefined || host.launch_profile === undefined) return \"profile_mismatch_attached\";\n\tif (client.launchProfile.profile_id === host.launch_profile.profile_id) return undefined;\n\treturn covers(client.launchProfile, host.launch_profile) ? \"profile_mismatch_attached\" : \"profile_narrower_attached\";\n}\n\nfunction covers(candidate: RpcLaunchProfile | undefined, running: RpcLaunchProfile | undefined): boolean {\n\tif (candidate === undefined || running === undefined) return false;\n\treturn running.core.extensions.every((extension) => candidate.core.extensions.includes(extension));\n}\n\n/** True when both builds are the same released version and only their build epoch differs. */\nfunction sameRelease(candidate: EngineOrdinal, running: EngineOrdinal | undefined): boolean {\n\treturn running !== undefined && candidate.slice(0, 4).every((part, index) => part === running[index]);\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
1
+ {"version":3,"file":"host-decision.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-decision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a client does when it finds - or does not find - a host on the shared RPC socket.\n *\n * One machine-wide host serves several client surfaces, so the process on the socket is\n * usually NOT this process's child. Two invariants follow, and this module is where they\n * are decided once instead of in every client:\n *\n * I1 - never terminate, signal or replace a host you did not start. The only sanctioned\n * replacement is the drain-based generation handoff, which ends no running work.\n * I2 - compatibility is `protocolVersion` + capabilities, NEVER a version-string compare,\n * and \"is my build newer?\" is `compareEngineOrdinal`, never `serverVersion`. An\n * ordinal that cannot be compared (a build without git metadata, a host that reports\n * none) is EQUAL, and since a handoff needs STRICTLY greater, such a pair attaches.\n *\n * The profile rule is the third guard: a handoff additionally requires the candidate's\n * extension set to be a SUPERSET of the running host's. Handing off to a narrower generation\n * would silently drop whatever the running host loaded (an editor plugin, a member bundle)\n * from every session that reopens in the new generation - so a narrower candidate attaches\n * and says so instead.\n */\nimport {\n\tcompareEngineOrdinal,\n\ttype EngineBuildIdentity,\n\ttype EngineOrdinal,\n} from \"../../core/engine-build-identity.ts\";\nimport {\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n} from \"./custom-capability.ts\";\nimport type { HostProtocolInfo } from \"./host-protocol-info.ts\";\nimport type { RpcLaunchProfile } from \"./rpc-types.ts\";\n\nexport { type HostProtocolInfo, parseHostProtocolInfo } from \"./host-protocol-info.ts\";\n\n/** The wire protocol this build speaks. A host answering with any other number is refused, not replaced. */\nexport const HOST_PROTOCOL_VERSION = 1;\n\n/**\n * HOST capability: this host drains into a successor generation on SIGUSR1 instead of dying with\n * its sessions. A host without it is attach-only - it is never renamed, signalled or upgraded.\n */\nexport const GENERATION_HANDOFF_CAPABILITY = \"generation_handoff\";\n\n/** What a daemon session needs from a host before any client may attach to it. */\nexport const REQUIRED_HOST_CAPABILITIES = [\n\t\"multi_session\",\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n] as const;\n\nexport interface HostDecisionClient {\n\treadonly protocolVersion: number;\n\treadonly requiredCapabilities: readonly string[];\n\treadonly identity: EngineBuildIdentity;\n\t/**\n\t * What THIS client would launch a host with. Absent = no launch spec (a bare CLI attaching to\n\t * whatever is running): such a client can prove nothing about extensions, so it never hands off.\n\t */\n\treadonly launchProfile?: RpcLaunchProfile;\n\t/** True when the running host's pidfile records this process as its writer (the I1 question). */\n\treadonly startedByUs: boolean;\n\t/** The platform both sides run on - one daemon is machine-local, so the host shares it. */\n\treadonly platform: NodeJS.Platform;\n}\n\n/** `upgrade` may hand off, `fallback` prefers no host over a mismatched one, `never` only attaches. */\nexport type HostDecisionPolicy = \"upgrade\" | \"fallback\" | \"never\";\n\nexport type HostAction = HostDecision[\"action\"];\n\n/** Attached anyway, with something the caller should say out loud. */\nexport type HostDecisionWarning =\n\t/** This client's extension set is missing something the host loads; upgrading would drop it. */\n\t| \"profile_narrower_attached\"\n\t/** The profiles differ (or cannot be compared) and no upgrade was possible. */\n\t| \"profile_mismatch_attached\";\n\n/** Every action carries only the reasons that can produce it, so an impossible pair cannot be built. */\nexport type HostDecision =\n\t| {\n\t\t\treadonly action: \"start\";\n\t\t\t/** `no_host`: nothing answered. `restart_own_host`: nothing answered and the pidfile is ours. */\n\t\t\treadonly reason: \"no_host\" | \"restart_own_host\";\n\t\t\treadonly upgradeable: false;\n\t }\n\t| { readonly action: \"refuse\"; readonly reason: \"protocol\" | \"capability\"; readonly upgradeable: false }\n\t| {\n\t\t\treadonly action: \"fallback\";\n\t\t\treadonly reason: \"capability\" | \"engine_mismatch\";\n\t\t\treadonly upgradeable: boolean;\n\t }\n\t| {\n\t\t\treadonly action: \"reuse\";\n\t\t\t/** `handoff_unsupported`: the host predates the drain handoff. `win32_attach_only`: named pipes cannot be renamed and win32 has no SIGUSR1. */\n\t\t\treadonly reason: \"compatible\" | \"handoff_unsupported\" | \"win32_attach_only\";\n\t\t\t/** Whether a generation handoff against this host is possible at all, independent of the policy. */\n\t\t\treadonly upgradeable: boolean;\n\t\t\treadonly warning?: HostDecisionWarning;\n\t }\n\t| { readonly action: \"handoff\"; readonly reason: \"newer_engine\" | \"profile\"; readonly upgradeable: true };\n\n/** What a `never` policy can answer: it attaches, starts or refuses, and never touches a running host. */\nexport type AttachOnlyDecision = Extract<HostDecision, { action: \"start\" | \"reuse\" | \"refuse\" }>;\n\n/** Why an ensure refused. The decision's own refusals plus the one only a pidfile can produce. */\nexport type HostRefusalReason = \"protocol\" | \"capability\" | \"foreign_writer\" | \"legacy_host\" | \"host_busy\";\n\n/**\n * An ensure that found an unusable host and refused to act on it. A refusal is FINAL by design:\n * this process may not signal a host it did not start (I1), and starting a second host on an\n * endpoint another process already owns is exactly the failure the refusal exists to prevent.\n */\nexport class HostEnsureRefusedError extends Error {\n\treadonly socket: string;\n\treadonly reason: HostRefusalReason;\n\n\tconstructor(socket: string, reason: HostRefusalReason, host: HostProtocolInfo | undefined) {\n\t\tsuper(`RPC socket ${socket} refused: ${reason} - ${refusalDetail(reason, host)}`);\n\t\tthis.name = \"HostEnsureRefusedError\";\n\t\tthis.socket = socket;\n\t\tthis.reason = reason;\n\t}\n}\n\nfunction refusalDetail(reason: HostRefusalReason, host: HostProtocolInfo | undefined): string {\n\tswitch (reason) {\n\t\tcase \"protocol\":\n\t\t\treturn `the running host speaks protocol version ${host?.protocolVersion ?? \"unknown\"}, this build speaks ${HOST_PROTOCOL_VERSION}`;\n\t\tcase \"capability\": {\n\t\t\tconst missing = REQUIRED_HOST_CAPABILITIES.filter((capability) => !host?.capabilities.includes(capability));\n\t\t\treturn `the running host is missing ${JSON.stringify(missing)}; it owns the socket, so no second host is started`;\n\t\t}\n\t\tcase \"foreign_writer\":\n\t\t\treturn \"its pidfile was written by another process, so this one may not signal it; stop that host explicitly instead\";\n\t\tcase \"legacy_host\":\n\t\t\treturn \"a host from before the per-socket daemon directory is still running on this endpoint; it is never signalled, and no second host is started beside it\";\n\t\tcase \"host_busy\":\n\t\t\treturn \"its socket accepts connections but did not answer inside the probe budget: a live host under load, which is never ended to make room for a replacement\";\n\t\tdefault:\n\t\t\treturn assertNever(reason);\n\t}\n}\n\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: \"never\",\n): AttachOnlyDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision {\n\tif (host === undefined) {\n\t\treturn { action: \"start\", reason: client.startedByUs ? \"restart_own_host\" : \"no_host\", upgradeable: false };\n\t}\n\tif (host.protocolVersion !== client.protocolVersion) {\n\t\treturn { action: \"refuse\", reason: \"protocol\", upgradeable: false };\n\t}\n\tif (!client.requiredCapabilities.every((capability) => host.capabilities.includes(capability))) {\n\t\t// A host that cannot serve this client still OWNS the socket: starting a second one would\n\t\t// bind over an endpoint another process is serving. Refuse, or fall back to no host at all.\n\t\tif (policy === \"fallback\") return { action: \"fallback\", reason: \"capability\", upgradeable: false };\n\t\treturn { action: \"refuse\", reason: \"capability\", upgradeable: false };\n\t}\n\tconst upgradeable = client.platform !== \"win32\" && host.capabilities.includes(GENERATION_HANDOFF_CAPABILITY);\n\tif (policy === \"fallback\" && host.engineVersion !== client.identity.text) {\n\t\treturn { action: \"fallback\", reason: \"engine_mismatch\", upgradeable };\n\t}\n\tconst warning = profileWarning(client, host);\n\tif (client.platform === \"win32\") {\n\t\treturn { action: \"reuse\", reason: \"win32_attach_only\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (!upgradeable) {\n\t\treturn { action: \"reuse\", reason: \"handoff_unsupported\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (policy === \"upgrade\" && client.launchProfile && host.launch_profile && supersedes(client, host)) {\n\t\treturn {\n\t\t\taction: \"handoff\",\n\t\t\treason: sameRelease(client.identity.ordinal, host.engineOrdinal) ? \"profile\" : \"newer_engine\",\n\t\t\tupgradeable: true,\n\t\t};\n\t}\n\treturn { action: \"reuse\", reason: \"compatible\", upgradeable, ...(warning && { warning }) };\n}\n\n/** A strictly newer build whose extensions cover everything the host loads - the only handoff candidate. */\nfunction supersedes(client: HostDecisionClient, host: HostProtocolInfo): boolean {\n\tif (host.engineOrdinal === undefined) return false;\n\tconst hostIdentity: EngineBuildIdentity = {\n\t\ttext: host.engineVersion ?? \"\",\n\t\tordinal: host.engineOrdinal,\n\t\tscheme: host.engineOrdinal[4] > 0 ? \"epoch\" : \"nodef\",\n\t};\n\tif (compareEngineOrdinal(client.identity, hostIdentity) <= 0) return false;\n\treturn covers(client.launchProfile, host.launch_profile);\n}\n\nfunction profileWarning(client: HostDecisionClient, host: HostProtocolInfo): HostDecisionWarning | undefined {\n\tif (client.launchProfile === undefined || host.launch_profile === undefined) return \"profile_mismatch_attached\";\n\tif (client.launchProfile.profile_id === host.launch_profile.profile_id) return undefined;\n\treturn covers(client.launchProfile, host.launch_profile) ? \"profile_mismatch_attached\" : \"profile_narrower_attached\";\n}\n\nfunction covers(candidate: RpcLaunchProfile | undefined, running: RpcLaunchProfile | undefined): boolean {\n\tif (candidate === undefined || running === undefined) return false;\n\treturn running.core.extensions.every((extension) => candidate.core.extensions.includes(extension));\n}\n\n/** True when both builds are the same released version and only their build epoch differs. */\nfunction sameRelease(candidate: EngineOrdinal, running: EngineOrdinal | undefined): boolean {\n\treturn running !== undefined && candidate.slice(0, 4).every((part, index) => part === running[index]);\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
@@ -14,7 +14,7 @@ import { decideHostAction, HOST_PROTOCOL_VERSION, HostEnsureRefusedError, REQUIR
14
14
  import { handoffHost } from "./host-handoff.js";
15
15
  import { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from "./host-launch.js";
16
16
  import { DEFAULT_HOST_IDLE_EXIT_MS } from "./host-lifecycle.js";
17
- import { probeProtocolInfo } from "./host-probe.js";
17
+ import { probeProtocolInfo, probeSocketReachable } from "./host-probe.js";
18
18
  import { acquireOwnershipSafeLock } from "./ownership-safe-lock.js";
19
19
  import { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from "./protocol-identity.js";
20
20
  import { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from "./socket-transport.js";
@@ -96,6 +96,13 @@ async function ensureHostLocked(paths, socket, options) {
96
96
  // this record may end it - anyone else refuses rather than signalling somebody else's host.
97
97
  if (!startedByUs)
98
98
  throw new HostEnsureRefusedError(socket, "foreign_writer", protocol);
99
+ // ... and silent is not the same as gone. A host serving many sessions can miss a probe
100
+ // budget while its event loop is busy; its socket still ACCEPTS the connection. Ending it
101
+ // then would destroy every live session to replace a host that was never broken, so a
102
+ // reachable socket is refused instead of signalled - the caller retries or falls back.
103
+ if (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {
104
+ throw new HostEnsureRefusedError(socket, "host_busy", protocol);
105
+ }
99
106
  await stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);
100
107
  }
101
108
  // A host from before this layout registered itself in the FLAT directory. Its files are another
@@ -1 +1 @@
1
- {"version":3,"file":"host-ensure.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { ENV_AGENT_DIR, getAgentDir } from \"../../config.ts\";\nimport { engineBuildIdentity } from \"../../core/engine-build-identity.ts\";\nimport {\n\ttype DaemonPidFile,\n\tProcessIdentityUnreadableError,\n\tprocessIsLive,\n\tprocessMatchesPidFile,\n\treadProcessStartTime,\n\twaitForStartTime,\n} from \"../app-server/daemon/process.ts\";\nimport { RPC_CLIENT_CAPABILITIES_ENV } from \"./custom-capability.ts\";\nimport {\n\tcreateDaemonDirectories,\n\tcreateHostDaemonPaths,\n\tHOST_DAEMON_DIR_ENV,\n\ttype HostDaemonPaths,\n} from \"./host-daemon-paths.ts\";\nimport {\n\tclearHostRegistration,\n\tlegacyHostIsLive,\n\ttype RegisteredHost,\n\treadHostRegistration,\n\twriteHostRegistration,\n\twrittenByThisProcess,\n} from \"./host-daemon-registration.ts\";\nimport { writeHostSettings } from \"./host-daemon-state.ts\";\nimport {\n\tdecideHostAction,\n\tHOST_PROTOCOL_VERSION,\n\ttype HostDecision,\n\ttype HostDecisionClient,\n\tHostEnsureRefusedError,\n\ttype HostProtocolInfo,\n\tREQUIRED_HOST_CAPABILITIES,\n} from \"./host-decision.ts\";\nimport { handoffHost } from \"./host-handoff.ts\";\nimport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nimport { DEFAULT_HOST_IDLE_EXIT_MS, type HostColdStart, type HostLifecyclePolicyInput } from \"./host-lifecycle.ts\";\nimport { probeProtocolInfo } from \"./host-probe.ts\";\nimport { acquireOwnershipSafeLock } from \"./ownership-safe-lock.ts\";\nimport { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from \"./protocol-identity.ts\";\nimport { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from \"./socket-transport.ts\";\n\nexport {\n\tcreateHostDaemonPaths,\n\tdaemonDirectoryName,\n\ttype HostDaemonPaths,\n\tHostDaemonStateError,\n\ttype HostGenerationPaths,\n} from \"./host-daemon-paths.ts\";\nexport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nexport { type ProbeHostOptions, probeHost } from \"./host-probe.ts\";\nexport type { HostColdStart, HostLifecyclePolicyInput };\n\n/**\n * What an ensure may do to a host that is already running.\n *\n * `never` (the default) attaches or starts, and touches nothing that is already serving the\n * socket. `if-engine-differs` additionally allows a GENERATION HANDOFF when `decideHostAction`\n * finds this build strictly newer and its extension set a superset of the running host's - the\n * running host then drains instead of dying, so no session is ever ended by an upgrade.\n */\nexport type HostUpgradePolicy = \"never\" | \"if-engine-differs\";\n\nexport interface EnsureHostOptions {\n\treadonly socket: string;\n\treadonly agentDir?: string;\n\t/** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */\n\treadonly policy?: HostLifecyclePolicyInput;\n\t/** Extra CLI arguments forwarded through the supervisor to the host process. */\n\treadonly hostArgs?: readonly string[];\n\t/** Environment for the spawned host; a `null` value removes an inherited variable. */\n\treadonly env?: Readonly<Record<string, string | null>>;\n\t/** Whether a newer build may take the socket over from the running host. Defaults to `never`. */\n\treadonly upgrade?: HostUpgradePolicy;\n\treadonly _test?: {\n\t\treadonly readinessTimeoutMs?: number;\n\t\treadonly stopTimeoutMs?: number;\n\t\treadonly spawn?: { readonly command: string; readonly args: readonly string[] };\n\t\t/** Builds the spawnable command from supervisor argv; tests point it at the source entry. */\n\t\treadonly launch?: (args: readonly string[]) => { readonly command: string; readonly args: readonly string[] };\n\t\t/** Runs after endpoint ownership is locked; deterministic concurrency-test gate. */\n\t\treadonly afterLockAcquired?: () => Promise<void>;\n\t\t/**\n\t\t * Runs after the child is spawned but before its pidfile is registered, so a\n\t\t * test can force the startup failure a loaded runner produces without having\n\t\t * to stall the real process-identity probe.\n\t\t */\n\t\treadonly beforePidFileWrite?: () => Promise<void>;\n\t\t/** Overrides the process-identity probe so a test can force its failure. */\n\t\treadonly readProcessStartTime?: (pid: number) => Promise<string | undefined>;\n\t};\n}\n\nexport interface EnsuredHost {\n\treadonly pid: number;\n\treadonly socket: string;\n\treadonly reused: boolean;\n}\n\nconst SPAWNED_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst EXISTING_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst DEFAULT_READINESS_TIMEOUT_MS = 10_000;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst SIGKILL_GRACE_MS = 2_000;\n/**\n * A lock waiter must outlast the longest critical section a holder can run:\n * probing an existing host, stopping an incompatible one (SIGTERM wait plus the\n * SIGKILL grace), then spawning the replacement and waiting for it to answer.\n * Each SQLite busy wait stays short because it blocks the event loop; this\n * cumulative budget is what covers the whole section, with headroom for a slow\n * runner. A waiter that gives up early surfaces as a raw \"database is locked\"\n * failure on the second of two concurrent starts.\n */\nconst ENSURE_LOCK_WAIT_MS =\n\tEXISTING_HOST_PROBE_TIMEOUT_MS + DEFAULT_STOP_TIMEOUT_MS + SIGKILL_GRACE_MS + DEFAULT_READINESS_TIMEOUT_MS + 10_000;\nconst LOCK_BUSY_WAIT_MS = 100;\nconst lockOptions = {\n\tretries: { retries: ENSURE_LOCK_WAIT_MS / LOCK_BUSY_WAIT_MS, minTimeout: 20, maxTimeout: LOCK_BUSY_WAIT_MS },\n} as const;\nexport async function ensureHost(options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst socket = normalizeSocketPath(options.socket);\n\tconst paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });\n\tawait createDaemonDirectories(paths);\n\t// The public socket is the shared resource; agent directories are not a\n\t// sufficient lock scope when two installations target the same endpoint.\n\tconst lockTarget = join(tmpdir(), \"senpi-rpc-host-locks\", createSocketLockName(socket));\n\tawait mkdir(dirname(lockTarget), { recursive: true });\n\tawait writeFile(lockTarget, \"\", { flag: \"a\", mode: 0o600 });\n\t// Opportunistic GC of other installs' leftovers stays OUTSIDE the endpoint lock.\n\t// Its cost scales with the whole tmpdir and, on win32, adds a ~1s process probe per\n\t// candidate; inside the critical section that inflated the hold for every concurrent\n\t// ensureHost until a waiter exhausted its budget and surfaced a raw \"database is\n\t// locked\". Its own guards (60s age, dead owner pid) already make it safe unlocked.\n\tawait reapOrphanedInternalHostDirs();\n\tconst release = await acquireOwnershipSafeLock(`${lockTarget}.lock`, lockOptions);\n\ttry {\n\t\tawait options._test?.afterLockAcquired?.();\n\t\treturn await ensureHostLocked(paths, socket, options);\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function ensureHostLocked(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\tconst registered = await readHostRegistration(paths);\n\t// A record naming ANOTHER endpoint is not about this ensure's host. The per-socket directory\n\t// makes that structural, and the field stays as the second guard for a directory that was\n\t// somehow reused: a second socket must never read the first socket's daemon as its own.\n\tconst registeredHere = registersSocket(registered, socket);\n\tconst protocol = await probeProtocolInfo(socket, EXISTING_HOST_PROBE_TIMEOUT_MS);\n\tconst startedByUs = registeredHere && (await writtenByThisProcess(registered?.writer));\n\tconst attachedPid = registeredHere ? (registered?.record.pid ?? 0) : 0;\n\tconst decision = decide(options, startedByUs, protocol);\n\tswitch (decision.action) {\n\t\tcase \"reuse\":\n\t\t\t// A compatible socket is attachable even when another client surface\n\t\t\t// started it. Only hosts we spawned are eligible for lifecycle management.\n\t\t\treturn { pid: attachedPid, socket, reused: true };\n\t\tcase \"refuse\":\n\t\t\tthrow new HostEnsureRefusedError(socket, decision.reason, protocol);\n\t\tcase \"handoff\":\n\t\t\treturn upgradeGeneration(paths, socket, options, attachedPid);\n\t\tcase \"start\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn assertNever(decision);\n\t}\n\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\tconst pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;\n\tif (registered && pidMatches) {\n\t\t// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE\n\t\t// this record may end it - anyone else refuses rather than signalling somebody else's host.\n\t\tif (!startedByUs) throw new HostEnsureRefusedError(socket, \"foreign_writer\", protocol);\n\t\tawait stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);\n\t}\n\t// A host from before this layout registered itself in the FLAT directory. Its files are another\n\t// process's state: never read as ours, never signalled, never removed - and while it is alive,\n\t// this ensure refuses instead of binding a socket it may still be serving.\n\tif (await legacyHostIsLive(paths, probe)) throw new HostEnsureRefusedError(socket, \"legacy_host\", protocol);\n\tif (registeredHere) await clearHostRegistration(paths);\n\treturn startHost(paths, socket, options);\n}\n\n/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */\nfunction decide(\n\toptions: EnsureHostOptions,\n\tstartedByUs: boolean,\n\tprotocol: HostProtocolInfo | undefined,\n): Exclude<HostDecision, { action: \"fallback\" }> {\n\tif (options.upgrade !== \"if-engine-differs\") {\n\t\treturn decideHostAction(ensureClient(options, startedByUs), protocol, \"never\");\n\t}\n\tconst decision = decideHostAction(ensureClient(options, startedByUs), protocol, \"upgrade\");\n\treturn decision.action === \"fallback\" ? { action: \"reuse\", reason: \"compatible\", upgradeable: false } : decision;\n}\n\n/**\n * The upgrade, when the decision allows one: a new generation takes the socket and the running\n * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.\n */\nasync function upgradeGeneration(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tattachedPid: number,\n): Promise<EnsuredHost> {\n\tconst result = await handoffHost({\n\t\tsocket,\n\t\tagentDir: options.agentDir ?? getAgentDir(),\n\t\thostArgs: options.hostArgs ?? [],\n\t\t...(options.env ? { env: options.env } : {}),\n\t\t...(options.policy ? { policy: options.policy } : {}),\n\t\t_test: {\n\t\t\t...(options._test?.launch ? { launch: options._test.launch } : {}),\n\t\t\t...(options._test?.readinessTimeoutMs ? { readinessTimeoutMs: options._test.readinessTimeoutMs } : {}),\n\t\t},\n\t});\n\tif (result.action === \"handoff\") return { pid: result.pid, socket, reused: false };\n\tawait appendStderr(\n\t\tpaths,\n\t\t`generation handoff refused: ${result.reason}${result.detail ? ` (${result.detail})` : \"\"}`,\n\t);\n\treturn { pid: attachedPid, socket, reused: true };\n}\n\n/** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */\nfunction ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient {\n\treturn {\n\t\tprotocolVersion: HOST_PROTOCOL_VERSION,\n\t\trequiredCapabilities: REQUIRED_HOST_CAPABILITIES,\n\t\tidentity: engineBuildIdentity(),\n\t\t// An ensure that may upgrade has to say what it would launch: the superset rule refuses to\n\t\t// hand off to a generation that would drop what the running host loads. An ensure that may\n\t\t// not upgrade declares nothing, and therefore can never be the newer candidate.\n\t\t...(options.upgrade === \"if-engine-differs\"\n\t\t\t? { launchProfile: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()) }\n\t\t\t: {}),\n\t\tstartedByUs,\n\t\tplatform: process.platform,\n\t};\n}\n\n/** The argv the supervisor gives its host child; the launch profile has to describe THAT host. */\nfunction hostChildArgv(hostArgs: readonly string[]): string[] {\n\treturn [\"--mode\", \"rpc\", \"--multi-session\", ...hostArgs];\n}\n\n/** Whether a registration is about this endpoint. A record written before the field existed is. */\nfunction registersSocket(registered: RegisteredHost | undefined, socket: string): boolean {\n\tif (registered === undefined) return false;\n\treturn registered.socket === undefined || registered.socket === socket;\n}\n\nasync function startHost(paths: HostDaemonPaths, socket: string, options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\t// The generation this ensure is about to spawn, chosen HERE so its directory exists before the\n\t// host boots and the pointer can name it the moment the host is registered.\n\tconst instanceId = randomUUID();\n\t// The settings file must exist before the supervisor reads it at boot, so it\n\t// records the policy before the spawn instead of beside the pidfile.\n\tif (process.platform === \"win32\") await createSocketSecret(socketSecretPath(socket));\n\tawait writeHostSettings(paths, {\n\t\tsocket,\n\t\tcapabilities: PINNED_HOST_CLIENT_CAPABILITIES,\n\t\tcoldStart: options.policy?.coldStart ?? \"transient\",\n\t\tidleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,\n\t\tgeneration: 0,\n\t\tinstanceId,\n\t});\n\tconst stderr = await open(paths.stderrLog, \"w\", 0o600);\n\tlet pidFile: DaemonPidFile | undefined;\n\tlet child: ReturnType<typeof spawn> | undefined;\n\tlet exitedEarly: ChildExit | undefined;\n\tlet childExit: Promise<ChildExit> | undefined;\n\ttry {\n\t\tconst supervisorArgs = [\"--socket\", socket, ...(options.hostArgs ?? [])];\n\t\tconst launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);\n\t\tchild = spawn(launch.command, [...launch.args], {\n\t\t\tdetached: true,\n\t\t\twindowsHide: true,\n\t\t\tenv: hostEnv(options, { paths, instanceId }),\n\t\t\tstdio: [\"ignore\", \"ignore\", stderr.fd],\n\t\t});\n\t\tchildExit = new Promise((resolveExit) => {\n\t\t\tchild!.once(\"exit\", (code, signal) => {\n\t\t\t\texitedEarly = { code, signal };\n\t\t\t\tresolveExit(exitedEarly);\n\t\t\t});\n\t\t});\n\t\tif (child.pid === undefined) throw new Error(\"failed to spawn RPC socket host\");\n\t\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\t\tconst observedStartTime = await Promise.race([\n\t\t\twaitForStartTime(child.pid, 10_000, probe),\n\t\t\tchildExit.then(() => {\n\t\t\t\tthrow new Error(\"RPC socket host exited before its start time could be read\");\n\t\t\t}),\n\t\t]);\n\t\t// UNKNOWN identity on a live child: the probe was starved, not the host. Give the CIM table\n\t\t// one unhurried read (the per-attempt win32 default is 1s, which a loaded runner exceeds on\n\t\t// every attempt) before deciding.\n\t\tconst unhurriedProbe = testOptions?.readProcessStartTime\n\t\t\t? testOptions.readProcessStartTime\n\t\t\t: (pid: number) => readProcessStartTime(pid, process.platform, 15_000);\n\t\tconst processStartTime = observedStartTime ?? (await unhurriedProbe(child.pid).catch(() => undefined));\n\t\t// Still unreadable: the host is ours, alive, and about to prove itself on the socket, so it is\n\t\t// registered WITHOUT an ownership guard instead of being torn down for a starved probe. A\n\t\t// guard-less record never claims ownership and never authorizes a signal - every later caller\n\t\t// reads it as unknown - so the worst case is a fresh host next time, not a killed healthy one.\n\t\tpidFile = { pid: child.pid, processStartTime: processStartTime ?? null };\n\t\tawait testOptions?.beforePidFileWrite?.();\n\t\tawait writeHostRegistration(paths, {\n\t\t\trecord: pidFile,\n\t\t\tsocket,\n\t\t\tinstanceId,\n\t\t\tgeneration: 0,\n\t\t\tlaunchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,\n\t\t});\n\t\tchild.unref();\n\t} catch (error: unknown) {\n\t\t// Whether the child died on its own decides which diagnostic is true, and the\n\t\t// cleanup kill below records an `exitedEarly` indistinguishable from a real\n\t\t// self-exit. Latch it before killing, or the catch reports the SIGTERM it is\n\t\t// about to send and discards the actual startup failure.\n\t\tconst exitedBeforeCleanup = exitedEarly;\n\t\t// Keep the ChildProcess handle owned until registration succeeds. If startup\n\t\t// fails before the pidfile is written, terminate this exact child through\n\t\t// its still-attached handle rather than leaving an unmanaged daemon behind.\n\t\tif (!exitedBeforeCleanup && child && child.exitCode === null && child.signalCode === null) {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {}\n\t\t\tif (childExit) await Promise.race([childExit, delay(2_000)]);\n\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\tif (!exitedBeforeCleanup) {\n\t\t\tawait clearHostRegistration(paths);\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostic = await appendStderr(\n\t\t\tpaths,\n\t\t\t`RPC socket host exited with code ${exitedBeforeCleanup.code ?? \"null\"}${exitedBeforeCleanup.signal ? ` (${exitedBeforeCleanup.signal})` : \"\"} before answering get_protocol_info`,\n\t\t);\n\t\tawait clearHostRegistration(paths);\n\t\tthrow new Error(diagnostic);\n\t} finally {\n\t\tawait stderr.close();\n\t}\n\tconst readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;\n\tconst result = await pollProtocolInfo(socket, readinessTimeoutMs, childExit);\n\tif (isCompatible(result.protocol)) return { pid: pidFile.pid, socket, reused: false };\n\t// Teardown runs for the diagnostic's sake, so it must never replace it: a stop\n\t// failure here (unreadable identity, a host that outlives SIGKILL) would other-\n\t// wise propagate instead of the readiness message and skip cleanupState below,\n\t// leaving the pidfile and socket behind. Record it and keep going.\n\t// This child is ours and its handle is still attached: stop it through the handle.\n\t// Validating ownership through the pidfile would re-run the identity probe whose\n\t// failure is the very thing a loaded runner produces here.\n\tconst stopFailure = await stopSpawnedChild(\n\t\tchild,\n\t\tchildExit,\n\t\ttestOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS,\n\t).then(\n\t\t() => undefined,\n\t\t(error: unknown) => (error instanceof Error ? error.message : String(error)),\n\t);\n\tconst message = result.protocol\n\t\t? `RPC socket host answered get_protocol_info with protocolVersion ${result.protocol.protocolVersion}, serverVersion ${result.protocol.serverVersion} and capabilities ${JSON.stringify(result.protocol.capabilities)}, but is incompatible with protocol version ${HOST_PROTOCOL_VERSION} and required capabilities ${JSON.stringify(REQUIRED_HOST_CAPABILITIES)}`\n\t\t: result.exited\n\t\t\t? `RPC socket host exited with code ${result.exited.code ?? \"null\"}${result.exited.signal ? ` (${result.exited.signal})` : \"\"} before answering get_protocol_info`\n\t\t\t: `spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`;\n\tconst diagnostic = await appendStderr(\n\t\tpaths,\n\t\tstopFailure === undefined ? message : `${message} (teardown also reported: ${stopFailure})`,\n\t);\n\tawait clearHostRegistration(paths);\n\t// The supervisor may have failed before binding, or another owner may have\n\t// appeared while readiness was being checked. Never unlink an endpoint we\n\t// cannot prove this start owned.\n\tthrow new Error(diagnostic);\n}\n\n/**\n * Ownership for the reuse decision. An identity we cannot read proves nothing: it can neither\n * claim the host nor authorize a kill, so it reads as \"not ours\" and the caller starts fresh\n * rather than failing the whole ensure on an observation gap.\n */\nasync function matchesPidFileOrUnknown(\n\tpidFile: DaemonPidFile,\n\tprobe: (pid: number) => Promise<string | undefined>,\n): Promise<boolean> {\n\ttry {\n\t\treturn await processMatchesPidFile(pidFile, probe);\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return false;\n\t\tthrow error;\n\t}\n}\n\nasync function stopSpawnedChild(\n\tchild: ChildProcess,\n\tchildExit: Promise<ChildExit>,\n\ttermTimeoutMs: number,\n): Promise<void> {\n\tconst exited = () => child.exitCode !== null || child.signalCode !== null;\n\tif (exited()) return;\n\tconst signal = (name: NodeJS.Signals) => {\n\t\ttry {\n\t\t\tchild.kill(name);\n\t\t} catch (error: unknown) {\n\t\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t\t}\n\t};\n\tconst waitFor = (ms: number) => Promise.race([childExit.then(() => true), delay(ms).then(() => exited())]);\n\tsignal(\"SIGTERM\");\n\tif (await waitFor(termTimeoutMs)) return;\n\tsignal(\"SIGKILL\");\n\tif (!(await waitFor(SIGKILL_GRACE_MS))) {\n\t\tthrow new Error(`RPC socket host pid ${child.pid ?? \"?\"} remained alive after SIGKILL`);\n\t}\n}\n\nasync function stopManagedHost(\n\tpidFile: DaemonPidFile,\n\ttermTimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tawait signalValidated(pidFile, \"SIGTERM\", readStartTime);\n\tif (await waitForGone(pidFile, termTimeoutMs, readStartTime)) return;\n\tawait signalValidated(pidFile, \"SIGKILL\", readStartTime);\n\tif (!(await waitForGone(pidFile, SIGKILL_GRACE_MS, readStartTime))) {\n\t\tthrow new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);\n\t}\n}\n\ntype PidFileOwnership = \"owns\" | \"gone\" | \"unknown\";\n\n// One probe per call: the teardown loops below are themselves the retry, so the\n// budget inside processMatchesPidFile would only multiply their wall time. A probe\n// that fails against a LIVE pid is \"unknown\" — it proves nothing about ownership, so\n// signalling on it would be unsafe and treating it as \"gone\" would abandon a host that\n// may still be running. A failed probe against a dead pid is \"gone\".\nasync function resolvePidFileOwnership(\n\tpidFile: DaemonPidFile,\n\treadStartTime: (pid: number) => Promise<string | undefined>,\n): Promise<PidFileOwnership> {\n\ttry {\n\t\treturn (await processMatchesPidFile(pidFile, readStartTime, processIsLive, { attempts: 1 })) ? \"owns\" : \"gone\";\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return \"unknown\";\n\t\tthrow error;\n\t}\n}\n\nasync function signalValidated(\n\tpidFile: DaemonPidFile,\n\tsignal: NodeJS.Signals,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) !== \"owns\") return;\n\ttry {\n\t\tprocess.kill(pidFile.pid, signal);\n\t} catch (error: unknown) {\n\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t}\n}\n\nasync function waitForGone(\n\tpidFile: DaemonPidFile,\n\ttimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\") return true;\n\t\tawait delay(50);\n\t}\n\treturn (await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\";\n}\n\ntype ChildExit = { readonly code: number | null; readonly signal: NodeJS.Signals | null };\n\ntype ProtocolPollResult = { readonly protocol?: HostProtocolInfo; readonly exited?: ChildExit };\n\nasync function pollProtocolInfo(\n\tsocket: string,\n\ttimeoutMs: number,\n\tchildExit?: Promise<ChildExit>,\n): Promise<ProtocolPollResult> {\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastProtocol: HostProtocolInfo | undefined;\n\twhile (Date.now() <= deadline) {\n\t\tconst probe = probeProtocolInfo(\n\t\t\tsocket,\n\t\t\tMath.min(SPAWNED_HOST_PROBE_TIMEOUT_MS, Math.max(1, deadline - Date.now())),\n\t\t);\n\t\tconst raced = childExit ? await Promise.race([probe, childExit]) : await probe;\n\t\tif (isChildExit(raced)) {\n\t\t\t// A supervisor exit can be triggered by the Windows identity watchdog\n\t\t\t// while a named-pipe client is still composing its protocol reply. Do\n\t\t\t// not terminate the host based solely on that exit until this probe has\n\t\t\t// had a chance to deliver an answer. A host that never answers still\n\t\t\t// resolves through probeProtocolInfo's bounded timeout/close handling.\n\t\t\tconst info = await probe;\n\t\t\tif (info) {\n\t\t\t\tlastProtocol = info;\n\t\t\t\tif (isCompatible(info)) return { protocol: info };\n\t\t\t} else {\n\t\t\t\treturn { protocol: lastProtocol, exited: raced };\n\t\t\t}\n\t\t} else if (raced) {\n\t\t\tlastProtocol = raced;\n\t\t\tif (isCompatible(raced)) return { protocol: raced };\n\t\t}\n\t\tawait delay(50);\n\t}\n\treturn { protocol: lastProtocol };\n}\n\nfunction isChildExit(value: HostProtocolInfo | ChildExit | undefined): value is ChildExit {\n\treturn !!value && \"code\" in value && \"signal\" in value;\n}\n\n/**\n * Compatibility, for the attach decision and the readiness gate alike: a host is compatible exactly\n * when a client that is forbidden to upgrade would attach to it. Never a version-string comparison (I2).\n */\nfunction isCompatible(protocol: HostProtocolInfo | undefined): boolean {\n\treturn decideHostAction(ensureClient({ socket: \"\" }, false), protocol, \"never\").action === \"reuse\";\n}\n\n/** The environment a spawned host inherits: this process's, the caller's overrides, then the fixed wiring. */\nfunction hostEnv(\n\toptions: EnsureHostOptions,\n\tgeneration: { readonly paths: HostDaemonPaths; readonly instanceId: string },\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const [key, value] of Object.entries(options.env ?? {})) {\n\t\tif (value === null) delete env[key];\n\t\telse env[key] = value;\n\t}\n\tenv[ENV_AGENT_DIR] = options.agentDir ?? getAgentDir();\n\tenv[RPC_CLIENT_CAPABILITIES_ENV] = PINNED_HOST_CLIENT_CAPABILITIES.join(\",\");\n\t// Always SET, never inherited: an ensure run from inside a daemon session would otherwise hand\n\t// its own host's identity to the one it spawns, and two hosts claiming one instance id make a\n\t// handoff - which completes exactly when the instance id changes - impossible to observe.\n\tenv[HOST_INSTANCE_ID_ENV] = generation.instanceId;\n\tenv[HOST_GENERATION_ENV] = \"0\";\n\tenv[HOST_DAEMON_DIR_ENV] = generation.paths.dir;\n\treturn env;\n}\n\nasync function reapOrphanedInternalHostDirs(): Promise<void> {\n\ttry {\n\t\tconst entries = await readdir(tmpdir(), { withFileTypes: true });\n\t\tawait Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.isDirectory() && entry.name.startsWith(\"senpi-rpc-host-internal-\"))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst owner = JSON.parse(await readFile(join(tmpdir(), entry.name, \".owner\"), \"utf8\")) as {\n\t\t\t\t\t\t\tpid?: unknown;\n\t\t\t\t\t\t\tprocessStartTime?: unknown;\n\t\t\t\t\t\t\tcreatedAt?: unknown;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof owner.pid === \"number\" &&\n\t\t\t\t\t\t\ttypeof owner.processStartTime === \"string\" &&\n\t\t\t\t\t\t\ttypeof owner.createdAt === \"number\" &&\n\t\t\t\t\t\t\towner.processStartTime.length > 0 &&\n\t\t\t\t\t\t\towner.createdAt < Date.now() - 60_000 &&\n\t\t\t\t\t\t\t(await readdir(join(tmpdir(), entry.name))).length === 1 &&\n\t\t\t\t\t\t\t!(await processMatchesPidFile({ pid: owner.pid, processStartTime: owner.processStartTime }).catch(\n\t\t\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\t\t\t// Unreadable but live: assume the owner is alive rather than steal its lock.\n\t\t\t\t\t\t\t\t\tif (error instanceof ProcessIdentityUnreadableError) return true;\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tawait rm(join(tmpdir(), entry.name), { recursive: true, force: true });\n\t\t\t\t\t} catch {}\n\t\t\t\t}),\n\t\t);\n\t} catch {}\n}\n\nasync function appendStderr(paths: HostDaemonPaths, message: string): Promise<string> {\n\ttry {\n\t\tconst stderr = (await readFile(paths.stderrLog, \"utf8\")).trim();\n\t\treturn stderr ? `${message}\\n${stderr}` : message;\n\t} catch (error: unknown) {\n\t\tif (isNodeErrorCode(error, \"ENOENT\")) return message;\n\t\tthrow error;\n\t}\n}\n\nfunction createSocketLockName(socket: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(resolveSocketTransportAddress(socket, process.platform), \"utf8\")\n\t\t.digest(\"hex\")\n\t\t.slice(0, 32);\n}\n\nfunction normalizeSocketPath(value: string): string {\n\tif (value.startsWith(\"unix://\")) return value.slice(\"unix://\".length);\n\treturn value;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction isNodeErrorCode(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
1
+ {"version":3,"file":"host-ensure.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { ENV_AGENT_DIR, getAgentDir } from \"../../config.ts\";\nimport { engineBuildIdentity } from \"../../core/engine-build-identity.ts\";\nimport {\n\ttype DaemonPidFile,\n\tProcessIdentityUnreadableError,\n\tprocessIsLive,\n\tprocessMatchesPidFile,\n\treadProcessStartTime,\n\twaitForStartTime,\n} from \"../app-server/daemon/process.ts\";\nimport { RPC_CLIENT_CAPABILITIES_ENV } from \"./custom-capability.ts\";\nimport {\n\tcreateDaemonDirectories,\n\tcreateHostDaemonPaths,\n\tHOST_DAEMON_DIR_ENV,\n\ttype HostDaemonPaths,\n} from \"./host-daemon-paths.ts\";\nimport {\n\tclearHostRegistration,\n\tlegacyHostIsLive,\n\ttype RegisteredHost,\n\treadHostRegistration,\n\twriteHostRegistration,\n\twrittenByThisProcess,\n} from \"./host-daemon-registration.ts\";\nimport { writeHostSettings } from \"./host-daemon-state.ts\";\nimport {\n\tdecideHostAction,\n\tHOST_PROTOCOL_VERSION,\n\ttype HostDecision,\n\ttype HostDecisionClient,\n\tHostEnsureRefusedError,\n\ttype HostProtocolInfo,\n\tREQUIRED_HOST_CAPABILITIES,\n} from \"./host-decision.ts\";\nimport { handoffHost } from \"./host-handoff.ts\";\nimport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nimport { DEFAULT_HOST_IDLE_EXIT_MS, type HostColdStart, type HostLifecyclePolicyInput } from \"./host-lifecycle.ts\";\nimport { probeProtocolInfo, probeSocketReachable } from \"./host-probe.ts\";\nimport { acquireOwnershipSafeLock } from \"./ownership-safe-lock.ts\";\nimport { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from \"./protocol-identity.ts\";\nimport { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from \"./socket-transport.ts\";\n\nexport {\n\tcreateHostDaemonPaths,\n\tdaemonDirectoryName,\n\ttype HostDaemonPaths,\n\tHostDaemonStateError,\n\ttype HostGenerationPaths,\n} from \"./host-daemon-paths.ts\";\nexport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nexport { type ProbeHostOptions, probeHost } from \"./host-probe.ts\";\nexport type { HostColdStart, HostLifecyclePolicyInput };\n\n/**\n * What an ensure may do to a host that is already running.\n *\n * `never` (the default) attaches or starts, and touches nothing that is already serving the\n * socket. `if-engine-differs` additionally allows a GENERATION HANDOFF when `decideHostAction`\n * finds this build strictly newer and its extension set a superset of the running host's - the\n * running host then drains instead of dying, so no session is ever ended by an upgrade.\n */\nexport type HostUpgradePolicy = \"never\" | \"if-engine-differs\";\n\nexport interface EnsureHostOptions {\n\treadonly socket: string;\n\treadonly agentDir?: string;\n\t/** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */\n\treadonly policy?: HostLifecyclePolicyInput;\n\t/** Extra CLI arguments forwarded through the supervisor to the host process. */\n\treadonly hostArgs?: readonly string[];\n\t/** Environment for the spawned host; a `null` value removes an inherited variable. */\n\treadonly env?: Readonly<Record<string, string | null>>;\n\t/** Whether a newer build may take the socket over from the running host. Defaults to `never`. */\n\treadonly upgrade?: HostUpgradePolicy;\n\treadonly _test?: {\n\t\treadonly readinessTimeoutMs?: number;\n\t\treadonly stopTimeoutMs?: number;\n\t\treadonly spawn?: { readonly command: string; readonly args: readonly string[] };\n\t\t/** Builds the spawnable command from supervisor argv; tests point it at the source entry. */\n\t\treadonly launch?: (args: readonly string[]) => { readonly command: string; readonly args: readonly string[] };\n\t\t/** Runs after endpoint ownership is locked; deterministic concurrency-test gate. */\n\t\treadonly afterLockAcquired?: () => Promise<void>;\n\t\t/**\n\t\t * Runs after the child is spawned but before its pidfile is registered, so a\n\t\t * test can force the startup failure a loaded runner produces without having\n\t\t * to stall the real process-identity probe.\n\t\t */\n\t\treadonly beforePidFileWrite?: () => Promise<void>;\n\t\t/** Overrides the process-identity probe so a test can force its failure. */\n\t\treadonly readProcessStartTime?: (pid: number) => Promise<string | undefined>;\n\t};\n}\n\nexport interface EnsuredHost {\n\treadonly pid: number;\n\treadonly socket: string;\n\treadonly reused: boolean;\n}\n\nconst SPAWNED_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst EXISTING_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst DEFAULT_READINESS_TIMEOUT_MS = 10_000;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst SIGKILL_GRACE_MS = 2_000;\n/**\n * A lock waiter must outlast the longest critical section a holder can run:\n * probing an existing host, stopping an incompatible one (SIGTERM wait plus the\n * SIGKILL grace), then spawning the replacement and waiting for it to answer.\n * Each SQLite busy wait stays short because it blocks the event loop; this\n * cumulative budget is what covers the whole section, with headroom for a slow\n * runner. A waiter that gives up early surfaces as a raw \"database is locked\"\n * failure on the second of two concurrent starts.\n */\nconst ENSURE_LOCK_WAIT_MS =\n\tEXISTING_HOST_PROBE_TIMEOUT_MS + DEFAULT_STOP_TIMEOUT_MS + SIGKILL_GRACE_MS + DEFAULT_READINESS_TIMEOUT_MS + 10_000;\nconst LOCK_BUSY_WAIT_MS = 100;\nconst lockOptions = {\n\tretries: { retries: ENSURE_LOCK_WAIT_MS / LOCK_BUSY_WAIT_MS, minTimeout: 20, maxTimeout: LOCK_BUSY_WAIT_MS },\n} as const;\nexport async function ensureHost(options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst socket = normalizeSocketPath(options.socket);\n\tconst paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });\n\tawait createDaemonDirectories(paths);\n\t// The public socket is the shared resource; agent directories are not a\n\t// sufficient lock scope when two installations target the same endpoint.\n\tconst lockTarget = join(tmpdir(), \"senpi-rpc-host-locks\", createSocketLockName(socket));\n\tawait mkdir(dirname(lockTarget), { recursive: true });\n\tawait writeFile(lockTarget, \"\", { flag: \"a\", mode: 0o600 });\n\t// Opportunistic GC of other installs' leftovers stays OUTSIDE the endpoint lock.\n\t// Its cost scales with the whole tmpdir and, on win32, adds a ~1s process probe per\n\t// candidate; inside the critical section that inflated the hold for every concurrent\n\t// ensureHost until a waiter exhausted its budget and surfaced a raw \"database is\n\t// locked\". Its own guards (60s age, dead owner pid) already make it safe unlocked.\n\tawait reapOrphanedInternalHostDirs();\n\tconst release = await acquireOwnershipSafeLock(`${lockTarget}.lock`, lockOptions);\n\ttry {\n\t\tawait options._test?.afterLockAcquired?.();\n\t\treturn await ensureHostLocked(paths, socket, options);\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function ensureHostLocked(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\tconst registered = await readHostRegistration(paths);\n\t// A record naming ANOTHER endpoint is not about this ensure's host. The per-socket directory\n\t// makes that structural, and the field stays as the second guard for a directory that was\n\t// somehow reused: a second socket must never read the first socket's daemon as its own.\n\tconst registeredHere = registersSocket(registered, socket);\n\tconst protocol = await probeProtocolInfo(socket, EXISTING_HOST_PROBE_TIMEOUT_MS);\n\tconst startedByUs = registeredHere && (await writtenByThisProcess(registered?.writer));\n\tconst attachedPid = registeredHere ? (registered?.record.pid ?? 0) : 0;\n\tconst decision = decide(options, startedByUs, protocol);\n\tswitch (decision.action) {\n\t\tcase \"reuse\":\n\t\t\t// A compatible socket is attachable even when another client surface\n\t\t\t// started it. Only hosts we spawned are eligible for lifecycle management.\n\t\t\treturn { pid: attachedPid, socket, reused: true };\n\t\tcase \"refuse\":\n\t\t\tthrow new HostEnsureRefusedError(socket, decision.reason, protocol);\n\t\tcase \"handoff\":\n\t\t\treturn upgradeGeneration(paths, socket, options, attachedPid);\n\t\tcase \"start\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn assertNever(decision);\n\t}\n\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\tconst pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;\n\tif (registered && pidMatches) {\n\t\t// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE\n\t\t// this record may end it - anyone else refuses rather than signalling somebody else's host.\n\t\tif (!startedByUs) throw new HostEnsureRefusedError(socket, \"foreign_writer\", protocol);\n\t\t// ... and silent is not the same as gone. A host serving many sessions can miss a probe\n\t\t// budget while its event loop is busy; its socket still ACCEPTS the connection. Ending it\n\t\t// then would destroy every live session to replace a host that was never broken, so a\n\t\t// reachable socket is refused instead of signalled - the caller retries or falls back.\n\t\tif (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {\n\t\t\tthrow new HostEnsureRefusedError(socket, \"host_busy\", protocol);\n\t\t}\n\t\tawait stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);\n\t}\n\t// A host from before this layout registered itself in the FLAT directory. Its files are another\n\t// process's state: never read as ours, never signalled, never removed - and while it is alive,\n\t// this ensure refuses instead of binding a socket it may still be serving.\n\tif (await legacyHostIsLive(paths, probe)) throw new HostEnsureRefusedError(socket, \"legacy_host\", protocol);\n\tif (registeredHere) await clearHostRegistration(paths);\n\treturn startHost(paths, socket, options);\n}\n\n/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */\nfunction decide(\n\toptions: EnsureHostOptions,\n\tstartedByUs: boolean,\n\tprotocol: HostProtocolInfo | undefined,\n): Exclude<HostDecision, { action: \"fallback\" }> {\n\tif (options.upgrade !== \"if-engine-differs\") {\n\t\treturn decideHostAction(ensureClient(options, startedByUs), protocol, \"never\");\n\t}\n\tconst decision = decideHostAction(ensureClient(options, startedByUs), protocol, \"upgrade\");\n\treturn decision.action === \"fallback\" ? { action: \"reuse\", reason: \"compatible\", upgradeable: false } : decision;\n}\n\n/**\n * The upgrade, when the decision allows one: a new generation takes the socket and the running\n * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.\n */\nasync function upgradeGeneration(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tattachedPid: number,\n): Promise<EnsuredHost> {\n\tconst result = await handoffHost({\n\t\tsocket,\n\t\tagentDir: options.agentDir ?? getAgentDir(),\n\t\thostArgs: options.hostArgs ?? [],\n\t\t...(options.env ? { env: options.env } : {}),\n\t\t...(options.policy ? { policy: options.policy } : {}),\n\t\t_test: {\n\t\t\t...(options._test?.launch ? { launch: options._test.launch } : {}),\n\t\t\t...(options._test?.readinessTimeoutMs ? { readinessTimeoutMs: options._test.readinessTimeoutMs } : {}),\n\t\t},\n\t});\n\tif (result.action === \"handoff\") return { pid: result.pid, socket, reused: false };\n\tawait appendStderr(\n\t\tpaths,\n\t\t`generation handoff refused: ${result.reason}${result.detail ? ` (${result.detail})` : \"\"}`,\n\t);\n\treturn { pid: attachedPid, socket, reused: true };\n}\n\n/** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */\nfunction ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient {\n\treturn {\n\t\tprotocolVersion: HOST_PROTOCOL_VERSION,\n\t\trequiredCapabilities: REQUIRED_HOST_CAPABILITIES,\n\t\tidentity: engineBuildIdentity(),\n\t\t// An ensure that may upgrade has to say what it would launch: the superset rule refuses to\n\t\t// hand off to a generation that would drop what the running host loads. An ensure that may\n\t\t// not upgrade declares nothing, and therefore can never be the newer candidate.\n\t\t...(options.upgrade === \"if-engine-differs\"\n\t\t\t? { launchProfile: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()) }\n\t\t\t: {}),\n\t\tstartedByUs,\n\t\tplatform: process.platform,\n\t};\n}\n\n/** The argv the supervisor gives its host child; the launch profile has to describe THAT host. */\nfunction hostChildArgv(hostArgs: readonly string[]): string[] {\n\treturn [\"--mode\", \"rpc\", \"--multi-session\", ...hostArgs];\n}\n\n/** Whether a registration is about this endpoint. A record written before the field existed is. */\nfunction registersSocket(registered: RegisteredHost | undefined, socket: string): boolean {\n\tif (registered === undefined) return false;\n\treturn registered.socket === undefined || registered.socket === socket;\n}\n\nasync function startHost(paths: HostDaemonPaths, socket: string, options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\t// The generation this ensure is about to spawn, chosen HERE so its directory exists before the\n\t// host boots and the pointer can name it the moment the host is registered.\n\tconst instanceId = randomUUID();\n\t// The settings file must exist before the supervisor reads it at boot, so it\n\t// records the policy before the spawn instead of beside the pidfile.\n\tif (process.platform === \"win32\") await createSocketSecret(socketSecretPath(socket));\n\tawait writeHostSettings(paths, {\n\t\tsocket,\n\t\tcapabilities: PINNED_HOST_CLIENT_CAPABILITIES,\n\t\tcoldStart: options.policy?.coldStart ?? \"transient\",\n\t\tidleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,\n\t\tgeneration: 0,\n\t\tinstanceId,\n\t});\n\tconst stderr = await open(paths.stderrLog, \"w\", 0o600);\n\tlet pidFile: DaemonPidFile | undefined;\n\tlet child: ReturnType<typeof spawn> | undefined;\n\tlet exitedEarly: ChildExit | undefined;\n\tlet childExit: Promise<ChildExit> | undefined;\n\ttry {\n\t\tconst supervisorArgs = [\"--socket\", socket, ...(options.hostArgs ?? [])];\n\t\tconst launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);\n\t\tchild = spawn(launch.command, [...launch.args], {\n\t\t\tdetached: true,\n\t\t\twindowsHide: true,\n\t\t\tenv: hostEnv(options, { paths, instanceId }),\n\t\t\tstdio: [\"ignore\", \"ignore\", stderr.fd],\n\t\t});\n\t\tchildExit = new Promise((resolveExit) => {\n\t\t\tchild!.once(\"exit\", (code, signal) => {\n\t\t\t\texitedEarly = { code, signal };\n\t\t\t\tresolveExit(exitedEarly);\n\t\t\t});\n\t\t});\n\t\tif (child.pid === undefined) throw new Error(\"failed to spawn RPC socket host\");\n\t\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\t\tconst observedStartTime = await Promise.race([\n\t\t\twaitForStartTime(child.pid, 10_000, probe),\n\t\t\tchildExit.then(() => {\n\t\t\t\tthrow new Error(\"RPC socket host exited before its start time could be read\");\n\t\t\t}),\n\t\t]);\n\t\t// UNKNOWN identity on a live child: the probe was starved, not the host. Give the CIM table\n\t\t// one unhurried read (the per-attempt win32 default is 1s, which a loaded runner exceeds on\n\t\t// every attempt) before deciding.\n\t\tconst unhurriedProbe = testOptions?.readProcessStartTime\n\t\t\t? testOptions.readProcessStartTime\n\t\t\t: (pid: number) => readProcessStartTime(pid, process.platform, 15_000);\n\t\tconst processStartTime = observedStartTime ?? (await unhurriedProbe(child.pid).catch(() => undefined));\n\t\t// Still unreadable: the host is ours, alive, and about to prove itself on the socket, so it is\n\t\t// registered WITHOUT an ownership guard instead of being torn down for a starved probe. A\n\t\t// guard-less record never claims ownership and never authorizes a signal - every later caller\n\t\t// reads it as unknown - so the worst case is a fresh host next time, not a killed healthy one.\n\t\tpidFile = { pid: child.pid, processStartTime: processStartTime ?? null };\n\t\tawait testOptions?.beforePidFileWrite?.();\n\t\tawait writeHostRegistration(paths, {\n\t\t\trecord: pidFile,\n\t\t\tsocket,\n\t\t\tinstanceId,\n\t\t\tgeneration: 0,\n\t\t\tlaunchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,\n\t\t});\n\t\tchild.unref();\n\t} catch (error: unknown) {\n\t\t// Whether the child died on its own decides which diagnostic is true, and the\n\t\t// cleanup kill below records an `exitedEarly` indistinguishable from a real\n\t\t// self-exit. Latch it before killing, or the catch reports the SIGTERM it is\n\t\t// about to send and discards the actual startup failure.\n\t\tconst exitedBeforeCleanup = exitedEarly;\n\t\t// Keep the ChildProcess handle owned until registration succeeds. If startup\n\t\t// fails before the pidfile is written, terminate this exact child through\n\t\t// its still-attached handle rather than leaving an unmanaged daemon behind.\n\t\tif (!exitedBeforeCleanup && child && child.exitCode === null && child.signalCode === null) {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {}\n\t\t\tif (childExit) await Promise.race([childExit, delay(2_000)]);\n\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\tif (!exitedBeforeCleanup) {\n\t\t\tawait clearHostRegistration(paths);\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostic = await appendStderr(\n\t\t\tpaths,\n\t\t\t`RPC socket host exited with code ${exitedBeforeCleanup.code ?? \"null\"}${exitedBeforeCleanup.signal ? ` (${exitedBeforeCleanup.signal})` : \"\"} before answering get_protocol_info`,\n\t\t);\n\t\tawait clearHostRegistration(paths);\n\t\tthrow new Error(diagnostic);\n\t} finally {\n\t\tawait stderr.close();\n\t}\n\tconst readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;\n\tconst result = await pollProtocolInfo(socket, readinessTimeoutMs, childExit);\n\tif (isCompatible(result.protocol)) return { pid: pidFile.pid, socket, reused: false };\n\t// Teardown runs for the diagnostic's sake, so it must never replace it: a stop\n\t// failure here (unreadable identity, a host that outlives SIGKILL) would other-\n\t// wise propagate instead of the readiness message and skip cleanupState below,\n\t// leaving the pidfile and socket behind. Record it and keep going.\n\t// This child is ours and its handle is still attached: stop it through the handle.\n\t// Validating ownership through the pidfile would re-run the identity probe whose\n\t// failure is the very thing a loaded runner produces here.\n\tconst stopFailure = await stopSpawnedChild(\n\t\tchild,\n\t\tchildExit,\n\t\ttestOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS,\n\t).then(\n\t\t() => undefined,\n\t\t(error: unknown) => (error instanceof Error ? error.message : String(error)),\n\t);\n\tconst message = result.protocol\n\t\t? `RPC socket host answered get_protocol_info with protocolVersion ${result.protocol.protocolVersion}, serverVersion ${result.protocol.serverVersion} and capabilities ${JSON.stringify(result.protocol.capabilities)}, but is incompatible with protocol version ${HOST_PROTOCOL_VERSION} and required capabilities ${JSON.stringify(REQUIRED_HOST_CAPABILITIES)}`\n\t\t: result.exited\n\t\t\t? `RPC socket host exited with code ${result.exited.code ?? \"null\"}${result.exited.signal ? ` (${result.exited.signal})` : \"\"} before answering get_protocol_info`\n\t\t\t: `spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`;\n\tconst diagnostic = await appendStderr(\n\t\tpaths,\n\t\tstopFailure === undefined ? message : `${message} (teardown also reported: ${stopFailure})`,\n\t);\n\tawait clearHostRegistration(paths);\n\t// The supervisor may have failed before binding, or another owner may have\n\t// appeared while readiness was being checked. Never unlink an endpoint we\n\t// cannot prove this start owned.\n\tthrow new Error(diagnostic);\n}\n\n/**\n * Ownership for the reuse decision. An identity we cannot read proves nothing: it can neither\n * claim the host nor authorize a kill, so it reads as \"not ours\" and the caller starts fresh\n * rather than failing the whole ensure on an observation gap.\n */\nasync function matchesPidFileOrUnknown(\n\tpidFile: DaemonPidFile,\n\tprobe: (pid: number) => Promise<string | undefined>,\n): Promise<boolean> {\n\ttry {\n\t\treturn await processMatchesPidFile(pidFile, probe);\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return false;\n\t\tthrow error;\n\t}\n}\n\nasync function stopSpawnedChild(\n\tchild: ChildProcess,\n\tchildExit: Promise<ChildExit>,\n\ttermTimeoutMs: number,\n): Promise<void> {\n\tconst exited = () => child.exitCode !== null || child.signalCode !== null;\n\tif (exited()) return;\n\tconst signal = (name: NodeJS.Signals) => {\n\t\ttry {\n\t\t\tchild.kill(name);\n\t\t} catch (error: unknown) {\n\t\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t\t}\n\t};\n\tconst waitFor = (ms: number) => Promise.race([childExit.then(() => true), delay(ms).then(() => exited())]);\n\tsignal(\"SIGTERM\");\n\tif (await waitFor(termTimeoutMs)) return;\n\tsignal(\"SIGKILL\");\n\tif (!(await waitFor(SIGKILL_GRACE_MS))) {\n\t\tthrow new Error(`RPC socket host pid ${child.pid ?? \"?\"} remained alive after SIGKILL`);\n\t}\n}\n\nasync function stopManagedHost(\n\tpidFile: DaemonPidFile,\n\ttermTimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tawait signalValidated(pidFile, \"SIGTERM\", readStartTime);\n\tif (await waitForGone(pidFile, termTimeoutMs, readStartTime)) return;\n\tawait signalValidated(pidFile, \"SIGKILL\", readStartTime);\n\tif (!(await waitForGone(pidFile, SIGKILL_GRACE_MS, readStartTime))) {\n\t\tthrow new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);\n\t}\n}\n\ntype PidFileOwnership = \"owns\" | \"gone\" | \"unknown\";\n\n// One probe per call: the teardown loops below are themselves the retry, so the\n// budget inside processMatchesPidFile would only multiply their wall time. A probe\n// that fails against a LIVE pid is \"unknown\" — it proves nothing about ownership, so\n// signalling on it would be unsafe and treating it as \"gone\" would abandon a host that\n// may still be running. A failed probe against a dead pid is \"gone\".\nasync function resolvePidFileOwnership(\n\tpidFile: DaemonPidFile,\n\treadStartTime: (pid: number) => Promise<string | undefined>,\n): Promise<PidFileOwnership> {\n\ttry {\n\t\treturn (await processMatchesPidFile(pidFile, readStartTime, processIsLive, { attempts: 1 })) ? \"owns\" : \"gone\";\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return \"unknown\";\n\t\tthrow error;\n\t}\n}\n\nasync function signalValidated(\n\tpidFile: DaemonPidFile,\n\tsignal: NodeJS.Signals,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) !== \"owns\") return;\n\ttry {\n\t\tprocess.kill(pidFile.pid, signal);\n\t} catch (error: unknown) {\n\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t}\n}\n\nasync function waitForGone(\n\tpidFile: DaemonPidFile,\n\ttimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\") return true;\n\t\tawait delay(50);\n\t}\n\treturn (await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\";\n}\n\ntype ChildExit = { readonly code: number | null; readonly signal: NodeJS.Signals | null };\n\ntype ProtocolPollResult = { readonly protocol?: HostProtocolInfo; readonly exited?: ChildExit };\n\nasync function pollProtocolInfo(\n\tsocket: string,\n\ttimeoutMs: number,\n\tchildExit?: Promise<ChildExit>,\n): Promise<ProtocolPollResult> {\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastProtocol: HostProtocolInfo | undefined;\n\twhile (Date.now() <= deadline) {\n\t\tconst probe = probeProtocolInfo(\n\t\t\tsocket,\n\t\t\tMath.min(SPAWNED_HOST_PROBE_TIMEOUT_MS, Math.max(1, deadline - Date.now())),\n\t\t);\n\t\tconst raced = childExit ? await Promise.race([probe, childExit]) : await probe;\n\t\tif (isChildExit(raced)) {\n\t\t\t// A supervisor exit can be triggered by the Windows identity watchdog\n\t\t\t// while a named-pipe client is still composing its protocol reply. Do\n\t\t\t// not terminate the host based solely on that exit until this probe has\n\t\t\t// had a chance to deliver an answer. A host that never answers still\n\t\t\t// resolves through probeProtocolInfo's bounded timeout/close handling.\n\t\t\tconst info = await probe;\n\t\t\tif (info) {\n\t\t\t\tlastProtocol = info;\n\t\t\t\tif (isCompatible(info)) return { protocol: info };\n\t\t\t} else {\n\t\t\t\treturn { protocol: lastProtocol, exited: raced };\n\t\t\t}\n\t\t} else if (raced) {\n\t\t\tlastProtocol = raced;\n\t\t\tif (isCompatible(raced)) return { protocol: raced };\n\t\t}\n\t\tawait delay(50);\n\t}\n\treturn { protocol: lastProtocol };\n}\n\nfunction isChildExit(value: HostProtocolInfo | ChildExit | undefined): value is ChildExit {\n\treturn !!value && \"code\" in value && \"signal\" in value;\n}\n\n/**\n * Compatibility, for the attach decision and the readiness gate alike: a host is compatible exactly\n * when a client that is forbidden to upgrade would attach to it. Never a version-string comparison (I2).\n */\nfunction isCompatible(protocol: HostProtocolInfo | undefined): boolean {\n\treturn decideHostAction(ensureClient({ socket: \"\" }, false), protocol, \"never\").action === \"reuse\";\n}\n\n/** The environment a spawned host inherits: this process's, the caller's overrides, then the fixed wiring. */\nfunction hostEnv(\n\toptions: EnsureHostOptions,\n\tgeneration: { readonly paths: HostDaemonPaths; readonly instanceId: string },\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const [key, value] of Object.entries(options.env ?? {})) {\n\t\tif (value === null) delete env[key];\n\t\telse env[key] = value;\n\t}\n\tenv[ENV_AGENT_DIR] = options.agentDir ?? getAgentDir();\n\tenv[RPC_CLIENT_CAPABILITIES_ENV] = PINNED_HOST_CLIENT_CAPABILITIES.join(\",\");\n\t// Always SET, never inherited: an ensure run from inside a daemon session would otherwise hand\n\t// its own host's identity to the one it spawns, and two hosts claiming one instance id make a\n\t// handoff - which completes exactly when the instance id changes - impossible to observe.\n\tenv[HOST_INSTANCE_ID_ENV] = generation.instanceId;\n\tenv[HOST_GENERATION_ENV] = \"0\";\n\tenv[HOST_DAEMON_DIR_ENV] = generation.paths.dir;\n\treturn env;\n}\n\nasync function reapOrphanedInternalHostDirs(): Promise<void> {\n\ttry {\n\t\tconst entries = await readdir(tmpdir(), { withFileTypes: true });\n\t\tawait Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.isDirectory() && entry.name.startsWith(\"senpi-rpc-host-internal-\"))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst owner = JSON.parse(await readFile(join(tmpdir(), entry.name, \".owner\"), \"utf8\")) as {\n\t\t\t\t\t\t\tpid?: unknown;\n\t\t\t\t\t\t\tprocessStartTime?: unknown;\n\t\t\t\t\t\t\tcreatedAt?: unknown;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof owner.pid === \"number\" &&\n\t\t\t\t\t\t\ttypeof owner.processStartTime === \"string\" &&\n\t\t\t\t\t\t\ttypeof owner.createdAt === \"number\" &&\n\t\t\t\t\t\t\towner.processStartTime.length > 0 &&\n\t\t\t\t\t\t\towner.createdAt < Date.now() - 60_000 &&\n\t\t\t\t\t\t\t(await readdir(join(tmpdir(), entry.name))).length === 1 &&\n\t\t\t\t\t\t\t!(await processMatchesPidFile({ pid: owner.pid, processStartTime: owner.processStartTime }).catch(\n\t\t\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\t\t\t// Unreadable but live: assume the owner is alive rather than steal its lock.\n\t\t\t\t\t\t\t\t\tif (error instanceof ProcessIdentityUnreadableError) return true;\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tawait rm(join(tmpdir(), entry.name), { recursive: true, force: true });\n\t\t\t\t\t} catch {}\n\t\t\t\t}),\n\t\t);\n\t} catch {}\n}\n\nasync function appendStderr(paths: HostDaemonPaths, message: string): Promise<string> {\n\ttry {\n\t\tconst stderr = (await readFile(paths.stderrLog, \"utf8\")).trim();\n\t\treturn stderr ? `${message}\\n${stderr}` : message;\n\t} catch (error: unknown) {\n\t\tif (isNodeErrorCode(error, \"ENOENT\")) return message;\n\t\tthrow error;\n\t}\n}\n\nfunction createSocketLockName(socket: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(resolveSocketTransportAddress(socket, process.platform), \"utf8\")\n\t\t.digest(\"hex\")\n\t\t.slice(0, 32);\n}\n\nfunction normalizeSocketPath(value: string): string {\n\tif (value.startsWith(\"unix://\")) return value.slice(\"unix://\".length);\n\treturn value;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction isNodeErrorCode(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
@@ -8,6 +8,13 @@ export interface ProbeHostOptions {
8
8
  /** The running host's identity, or `undefined` when nothing is serving the endpoint. */
9
9
  export declare function probeHost(options: ProbeHostOptions): Promise<HostProtocolInfo | undefined>;
10
10
  export declare function probeProtocolInfo(socketPath: string, timeoutMs: number): Promise<HostProtocolInfo | undefined>;
11
+ /**
12
+ * Whether the socket ACCEPTED a connection during the last probe, regardless of whether an answer
13
+ * arrived in time. A host under load can miss a probe budget while it is perfectly alive, and the
14
+ * difference decides whether an ensure may end it: a silent-but-connectable socket has an owner
15
+ * serving sessions behind it, and killing that owner destroys every one of them.
16
+ */
17
+ export declare function probeSocketReachable(socketPath: string, timeoutMs: number): Promise<boolean>;
11
18
  /**
12
19
  * How many sessions the host holds right now, worker sessions included, or `undefined` when it
13
20
  * does not answer. A stop decision needs the number the host reports, not the one a client remembers.
@@ -1 +1 @@
1
- {"version":3,"file":"host-probe.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-probe.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,gBAAgB,EAAyB,MAAM,oBAAoB,CAAC;AAQlF,sGAAsG;AACtG,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAG/C,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAE1F;AAED,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAGpH;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI1G;AAED,uGAAuG;AACvG,wBAAgB,eAAe,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAElB"}
1
+ {"version":3,"file":"host-probe.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-probe.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,gBAAgB,EAAyB,MAAM,oBAAoB,CAAC;AAQlF,sGAAsG;AACtG,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAG/C,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAE1F;AAED,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAGpH;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElG;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAI1G;AAED,uGAAuG;AACvG,wBAAsB,eAAe,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAElB"}
@@ -21,6 +21,15 @@ export async function probeProtocolInfo(socketPath, timeoutMs) {
21
21
  const reply = await requestOnSocket(socketPath, { type: "get_protocol_info" }, timeoutMs);
22
22
  return reply === undefined ? undefined : parseHostProtocolInfo(reply);
23
23
  }
24
+ /**
25
+ * Whether the socket ACCEPTED a connection during the last probe, regardless of whether an answer
26
+ * arrived in time. A host under load can miss a probe budget while it is perfectly alive, and the
27
+ * difference decides whether an ensure may end it: a silent-but-connectable socket has an owner
28
+ * serving sessions behind it, and killing that owner destroys every one of them.
29
+ */
30
+ export async function probeSocketReachable(socketPath, timeoutMs) {
31
+ return (await connectAndAsk(socketPath, { id: PROBE_REQUEST_ID, type: "get_protocol_info" }, timeoutMs)).connected;
32
+ }
24
33
  /**
25
34
  * How many sessions the host holds right now, worker sessions included, or `undefined` when it
26
35
  * does not answer. A stop decision needs the number the host reports, not the one a client remembers.
@@ -32,8 +41,8 @@ export async function probeSessionCount(socketPath, timeoutMs) {
32
41
  return reply.sessions.length;
33
42
  }
34
43
  /** Sends one command and returns its `data`, or `undefined` for any failure to get a usable answer. */
35
- export function requestOnSocket(socketPath, command, timeoutMs) {
36
- return connectAndAsk(socketPath, { id: PROBE_REQUEST_ID, ...command }, timeoutMs);
44
+ export async function requestOnSocket(socketPath, command, timeoutMs) {
45
+ return (await connectAndAsk(socketPath, { id: PROBE_REQUEST_ID, ...command }, timeoutMs)).answer;
37
46
  }
38
47
  async function connectAndAsk(socketPath, request, timeoutMs) {
39
48
  let secret;
@@ -42,23 +51,25 @@ async function connectAndAsk(socketPath, request, timeoutMs) {
42
51
  secret = await readSocketSecret(socketSecretPath(socketPath));
43
52
  }
44
53
  catch {
45
- return undefined;
54
+ return { connected: false, answer: undefined };
46
55
  }
47
56
  }
48
57
  return new Promise((resolveProbe) => {
49
58
  const socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));
50
59
  let buffer = "";
51
60
  let settled = false;
61
+ let connected = false;
52
62
  const finish = (value) => {
53
63
  if (settled)
54
64
  return;
55
65
  settled = true;
56
66
  clearTimeout(timeout);
57
67
  socket.destroy();
58
- resolveProbe(value);
68
+ resolveProbe({ connected, answer: value });
59
69
  };
60
70
  const timeout = setTimeout(() => finish(), timeoutMs);
61
71
  socket.once("connect", () => {
72
+ connected = true;
62
73
  socket.write(`${JSON.stringify(request)}\n`);
63
74
  });
64
75
  socket.on("data", (chunk) => {
@@ -1 +1 @@
1
- {"version":3,"file":"host-probe.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-probe.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One question asked over a shared RPC socket: \"who is serving this endpoint, and what is it holding?\"\n *\n * Every client decision - attach, refuse, hand off, stop - starts here, and the answer is always\n * the host's own reply rather than anything a caller recorded earlier: files describe the past,\n * the socket describes the present. A probe never throws for an absent or silent endpoint,\n * because \"nobody is there\" is a legitimate answer that callers act on; a malformed answer is\n * dropped for the same reason.\n */\nimport { createConnection } from \"node:net\";\nimport { type HostProtocolInfo, parseHostProtocolInfo } from \"./host-decision.ts\";\nimport {\n\treadSocketSecret,\n\tresolveSocketTransportAddress,\n\tsendSocketHandshake,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\n\n/** Default probe budget: long enough for a host under load, short enough to keep an ensure moving. */\nexport const DEFAULT_PROBE_TIMEOUT_MS = 10_000;\nconst PROBE_REQUEST_ID = \"ensure-host-probe\";\n\nexport interface ProbeHostOptions {\n\treadonly socket: string;\n\treadonly timeoutMs?: number;\n}\n\n/** The running host's identity, or `undefined` when nothing is serving the endpoint. */\nexport function probeHost(options: ProbeHostOptions): Promise<HostProtocolInfo | undefined> {\n\treturn probeProtocolInfo(options.socket, options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS);\n}\n\nexport async function probeProtocolInfo(socketPath: string, timeoutMs: number): Promise<HostProtocolInfo | undefined> {\n\tconst reply = await requestOnSocket(socketPath, { type: \"get_protocol_info\" }, timeoutMs);\n\treturn reply === undefined ? undefined : parseHostProtocolInfo(reply);\n}\n\n/**\n * How many sessions the host holds right now, worker sessions included, or `undefined` when it\n * does not answer. A stop decision needs the number the host reports, not the one a client remembers.\n */\nexport async function probeSessionCount(socketPath: string, timeoutMs: number): Promise<number | undefined> {\n\tconst reply = await requestOnSocket(socketPath, { type: \"list_sessions\", include_workers: true }, timeoutMs);\n\tif (!isRecord(reply) || !Array.isArray(reply.sessions)) return undefined;\n\treturn reply.sessions.length;\n}\n\n/** Sends one command and returns its `data`, or `undefined` for any failure to get a usable answer. */\nexport function requestOnSocket(\n\tsocketPath: string,\n\tcommand: Readonly<Record<string, unknown>>,\n\ttimeoutMs: number,\n): Promise<unknown> {\n\treturn connectAndAsk(socketPath, { id: PROBE_REQUEST_ID, ...command }, timeoutMs);\n}\n\nasync function connectAndAsk(\n\tsocketPath: string,\n\trequest: Readonly<Record<string, unknown>>,\n\ttimeoutMs: number,\n): Promise<unknown> {\n\tlet secret: Buffer | undefined;\n\tif (process.platform === \"win32\") {\n\t\ttry {\n\t\t\tsecret = await readSocketSecret(socketSecretPath(socketPath));\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn new Promise((resolveProbe) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));\n\t\tlet buffer = \"\";\n\t\tlet settled = false;\n\t\tconst finish = (value?: unknown): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\tsocket.destroy();\n\t\t\tresolveProbe(value);\n\t\t};\n\t\tconst timeout = setTimeout(() => finish(), timeoutMs);\n\t\tsocket.once(\"connect\", () => {\n\t\t\tsocket.write(`${JSON.stringify(request)}\\n`);\n\t\t});\n\t\tsocket.on(\"data\", (chunk) => {\n\t\t\tbuffer += chunk.toString(\"utf8\");\n\t\t\tfor (;;) {\n\t\t\t\tconst newline = buffer.indexOf(\"\\n\");\n\t\t\t\tif (newline === -1) return;\n\t\t\t\tconst line = buffer.slice(0, newline);\n\t\t\t\tbuffer = buffer.slice(newline + 1);\n\t\t\t\t// A host broadcasts lifecycle records to every connection, so the reply to THIS\n\t\t\t\t// request is the line carrying its id - not simply the first line that arrives.\n\t\t\t\tconst answer = readAnswer(line);\n\t\t\t\tif (answer !== undefined) return finish(answer);\n\t\t\t}\n\t\t});\n\t\tsocket.once(\"error\", () => finish());\n\t\tsocket.once(\"close\", () => finish());\n\t\t// Register the error listener before sending the Windows named-pipe handshake.\n\t\t// When an idle host has already removed its pipe, the handshake write can\n\t\t// surface ENOENT immediately; without the listener this probe escapes instead\n\t\t// of becoming the expected \"no existing host\" result for the next ensure.\n\t\tif (secret) sendSocketHandshake(socket, secret);\n\t});\n}\n\nfunction readAnswer(text: string): unknown {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!isRecord(parsed) || parsed.id !== PROBE_REQUEST_ID || parsed.success !== true) return undefined;\n\treturn parsed.data ?? {};\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
1
+ {"version":3,"file":"host-probe.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-probe.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One question asked over a shared RPC socket: \"who is serving this endpoint, and what is it holding?\"\n *\n * Every client decision - attach, refuse, hand off, stop - starts here, and the answer is always\n * the host's own reply rather than anything a caller recorded earlier: files describe the past,\n * the socket describes the present. A probe never throws for an absent or silent endpoint,\n * because \"nobody is there\" is a legitimate answer that callers act on; a malformed answer is\n * dropped for the same reason.\n */\nimport { createConnection } from \"node:net\";\nimport { type HostProtocolInfo, parseHostProtocolInfo } from \"./host-decision.ts\";\nimport {\n\treadSocketSecret,\n\tresolveSocketTransportAddress,\n\tsendSocketHandshake,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\n\n/** Default probe budget: long enough for a host under load, short enough to keep an ensure moving. */\nexport const DEFAULT_PROBE_TIMEOUT_MS = 10_000;\nconst PROBE_REQUEST_ID = \"ensure-host-probe\";\n\nexport interface ProbeHostOptions {\n\treadonly socket: string;\n\treadonly timeoutMs?: number;\n}\n\n/** The running host's identity, or `undefined` when nothing is serving the endpoint. */\nexport function probeHost(options: ProbeHostOptions): Promise<HostProtocolInfo | undefined> {\n\treturn probeProtocolInfo(options.socket, options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS);\n}\n\nexport async function probeProtocolInfo(socketPath: string, timeoutMs: number): Promise<HostProtocolInfo | undefined> {\n\tconst reply = await requestOnSocket(socketPath, { type: \"get_protocol_info\" }, timeoutMs);\n\treturn reply === undefined ? undefined : parseHostProtocolInfo(reply);\n}\n\n/**\n * Whether the socket ACCEPTED a connection during the last probe, regardless of whether an answer\n * arrived in time. A host under load can miss a probe budget while it is perfectly alive, and the\n * difference decides whether an ensure may end it: a silent-but-connectable socket has an owner\n * serving sessions behind it, and killing that owner destroys every one of them.\n */\nexport async function probeSocketReachable(socketPath: string, timeoutMs: number): Promise<boolean> {\n\treturn (await connectAndAsk(socketPath, { id: PROBE_REQUEST_ID, type: \"get_protocol_info\" }, timeoutMs)).connected;\n}\n\n/**\n * How many sessions the host holds right now, worker sessions included, or `undefined` when it\n * does not answer. A stop decision needs the number the host reports, not the one a client remembers.\n */\nexport async function probeSessionCount(socketPath: string, timeoutMs: number): Promise<number | undefined> {\n\tconst reply = await requestOnSocket(socketPath, { type: \"list_sessions\", include_workers: true }, timeoutMs);\n\tif (!isRecord(reply) || !Array.isArray(reply.sessions)) return undefined;\n\treturn reply.sessions.length;\n}\n\n/** Sends one command and returns its `data`, or `undefined` for any failure to get a usable answer. */\nexport async function requestOnSocket(\n\tsocketPath: string,\n\tcommand: Readonly<Record<string, unknown>>,\n\ttimeoutMs: number,\n): Promise<unknown> {\n\treturn (await connectAndAsk(socketPath, { id: PROBE_REQUEST_ID, ...command }, timeoutMs)).answer;\n}\n\n/** One probe's outcome: whether the socket accepted a connection, and the answer if one arrived. */\ninterface ProbeOutcome {\n\treadonly connected: boolean;\n\treadonly answer: unknown;\n}\n\nasync function connectAndAsk(\n\tsocketPath: string,\n\trequest: Readonly<Record<string, unknown>>,\n\ttimeoutMs: number,\n): Promise<ProbeOutcome> {\n\tlet secret: Buffer | undefined;\n\tif (process.platform === \"win32\") {\n\t\ttry {\n\t\t\tsecret = await readSocketSecret(socketSecretPath(socketPath));\n\t\t} catch {\n\t\t\treturn { connected: false, answer: undefined };\n\t\t}\n\t}\n\treturn new Promise((resolveProbe) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));\n\t\tlet buffer = \"\";\n\t\tlet settled = false;\n\t\tlet connected = false;\n\t\tconst finish = (value?: unknown): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeout);\n\t\t\tsocket.destroy();\n\t\t\tresolveProbe({ connected, answer: value });\n\t\t};\n\t\tconst timeout = setTimeout(() => finish(), timeoutMs);\n\t\tsocket.once(\"connect\", () => {\n\t\t\tconnected = true;\n\t\t\tsocket.write(`${JSON.stringify(request)}\\n`);\n\t\t});\n\t\tsocket.on(\"data\", (chunk) => {\n\t\t\tbuffer += chunk.toString(\"utf8\");\n\t\t\tfor (;;) {\n\t\t\t\tconst newline = buffer.indexOf(\"\\n\");\n\t\t\t\tif (newline === -1) return;\n\t\t\t\tconst line = buffer.slice(0, newline);\n\t\t\t\tbuffer = buffer.slice(newline + 1);\n\t\t\t\t// A host broadcasts lifecycle records to every connection, so the reply to THIS\n\t\t\t\t// request is the line carrying its id - not simply the first line that arrives.\n\t\t\t\tconst answer = readAnswer(line);\n\t\t\t\tif (answer !== undefined) return finish(answer);\n\t\t\t}\n\t\t});\n\t\tsocket.once(\"error\", () => finish());\n\t\tsocket.once(\"close\", () => finish());\n\t\t// Register the error listener before sending the Windows named-pipe handshake.\n\t\t// When an idle host has already removed its pipe, the handshake write can\n\t\t// surface ENOENT immediately; without the listener this probe escapes instead\n\t\t// of becoming the expected \"no existing host\" result for the next ensure.\n\t\tif (secret) sendSocketHandshake(socket, secret);\n\t});\n}\n\nfunction readAnswer(text: string): unknown {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (!isRecord(parsed) || parsed.id !== PROBE_REQUEST_ID || parsed.success !== true) return undefined;\n\treturn parsed.data ?? {};\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
package/docs/settings.md CHANGED
@@ -88,7 +88,7 @@ Permission rules are a confirmation policy, not a sandbox. Senpi, extensions, pa
88
88
  | `modelThinkingLevels` | object | - | Per-model reasoning effort memory (`"provider/id": "level"`) |
89
89
  | `modelLastOnThinkingLevels` | object | - | Per-model last non-off reasoning level, used by `/reasoning on` to restore the previous effort |
90
90
  | `modelServiceTiers` | object | - | Per-model service tier memory (`"provider/id": "auto" \| "priority"`) |
91
- | `promptPreset` | string | `"auto"` | Force a system prompt preset: `"auto"`, `"kimi-k2-6"`, `"kimi-k2-7"`, `"kimi-k3"`, `"glm-5.2"`, `"glm-5.3"`, `"grok-4.5"`, `"grok-4.6"`, `"claude-fable-5"`, `"claude-fable-5-1"`, `"claude-opus-5"`, `"claude-opus-4-5"`, `"claude-opus-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"`, `"deepseek-v4-flash"`, `"deepseek-v4-flash-0731"`, `"deepseek-v4-1-flash"`, `"deepseek-v4-pro"`, `"gpt-5"`, `"gpt-5.2"`, `"gpt-5.3-codex"`, `"gpt-5.4"`, `"gpt-5.5"`, `"gpt-5.6"`, or `"gpt-6-astra"` |
91
+ | `promptPreset` | string | `"auto"` | Force a system prompt preset: `"auto"`, `"kimi-k2-6"`, `"kimi-k2-7"`, `"kimi-k2-8"`, `"kimi-k3"`, `"glm-5.2"`, `"glm-5.3"`, `"grok-4.5"`, `"grok-4.6"`, `"claude-fable-5"`, `"claude-fable-5-1"`, `"claude-opus-5"`, `"claude-opus-4-5"`, `"claude-opus-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"`, `"deepseek-v4-flash"`, `"deepseek-v4-flash-0731"`, `"deepseek-v4-1-flash"`, `"deepseek-v4-pro"`, `"gpt-5"`, `"gpt-5.2"`, `"gpt-5.3-codex"`, `"gpt-5.4"`, `"gpt-5.5"`, `"gpt-5.6"`, or `"gpt-6-astra"` |
92
92
  | `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
93
93
  | `showCacheMissNotices` | boolean | `false` | Show transcript notices for significant prompt-cache misses, compaction or branch-summary usage, and provider recovery diagnostics such as dropped Anthropic thinking blocks |
94
94
  | `thinkingBudgets` | object | - | Custom token budgets per thinking level. Anthropic, Google, and Bedrock use these natively. OpenAI-compatible models use them when `compat.thinkingTokenBudgetField` (or `supportsThinkingTokenBudget`) is set. |
@@ -12,6 +12,30 @@
12
12
 
13
13
  ### Removed
14
14
 
15
+ ## [2026.9.18-5] - 2026-09-18
16
+
17
+ ### Breaking Changes
18
+
19
+ ### Added
20
+
21
+ ### Changed
22
+
23
+ ### Fixed
24
+
25
+ ### Removed
26
+
27
+ ## [2026.9.18-4] - 2026-09-18
28
+
29
+ ### Breaking Changes
30
+
31
+ ### Added
32
+
33
+ ### Changed
34
+
35
+ ### Fixed
36
+
37
+ ### Removed
38
+
15
39
  ## [2026.9.18-3] - 2026-09-18
16
40
 
17
41
  ### Breaking Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.9.18-3",
3
+ "version": "2026.9.18-5",
4
4
  "private": true,
5
5
  "description": "Source-only senpi extension package for codemode evaluation tools",
6
6
  "type": "module",
@@ -31,14 +31,14 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@babel/parser": "8.0.4",
34
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.18-3",
34
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.18-5",
35
35
  "typebox": "1.3.27"
36
36
  },
37
37
  "peerDependencies": {
38
- "@code-yeongyu/senpi": "2026.9.18-3"
38
+ "@code-yeongyu/senpi": "2026.9.18-5"
39
39
  },
40
40
  "devDependencies": {
41
- "@code-yeongyu/senpi": "2026.9.18-3"
41
+ "@code-yeongyu/senpi": "2026.9.18-5"
42
42
  },
43
43
  "keywords": [
44
44
  "senpi",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-agent-core",
3
- "version": "2026.9.18-3",
3
+ "version": "2026.9.18-5",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -57,8 +57,8 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "@earendil-works/chord": "0.85.1",
60
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.18-3",
61
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.18-3",
60
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.18-5",
61
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.18-5",
62
62
  "diff": "9.0.0",
63
63
  "ignore": "7.0.8",
64
64
  "typebox": "1.3.27",
@@ -15,7 +15,6 @@ import { GOOGLE_MODELS } from "./providers/google.models.ts";
15
15
  import { GOOGLE_VERTEX_MODELS } from "./providers/google-vertex.models.ts";
16
16
  import { GROQ_MODELS } from "./providers/groq.models.ts";
17
17
  import { HUGGINGFACE_MODELS } from "./providers/huggingface.models.ts";
18
- import { KIMI_CODING_MODELS } from "./providers/kimi-coding.models.ts";
19
18
  import { MINIMAX_MODELS } from "./providers/minimax.models.ts";
20
19
  import { MINIMAX_CN_MODELS } from "./providers/minimax-cn.models.ts";
21
20
  import { MISTRAL_MODELS } from "./providers/mistral.models.ts";
@@ -59,7 +58,6 @@ export declare const MODELS: {
59
58
  readonly "google-vertex": typeof GOOGLE_VERTEX_MODELS;
60
59
  readonly "groq": typeof GROQ_MODELS;
61
60
  readonly "huggingface": typeof HUGGINGFACE_MODELS;
62
- readonly "kimi-coding": typeof KIMI_CODING_MODELS;
63
61
  readonly "minimax": typeof MINIMAX_MODELS;
64
62
  readonly "minimax-cn": typeof MINIMAX_CN_MODELS;
65
63
  readonly "mistral": typeof MISTRAL_MODELS;
@@ -1 +1 @@
1
- {"version":3,"file":"models.generated.d.ts","sourceRoot":"","sources":["../src/models.generated.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"models.generated.d.ts","sourceRoot":"","sources":["../src/models.generated.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAC;AAC3F,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,iCAAiC,EAAE,MAAM,kDAAkD,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAC;AAC3F,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E,eAAO,MAAM,MAAM,EAAE;IACpB,QAAQ,CAAC,oBAAoB,EAAE,OAAO,yBAAyB,CAAC;IAChE,QAAQ,CAAC,gBAAgB,EAAE,OAAO,qBAAqB,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,OAAO,eAAe,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,OAAO,gBAAgB,CAAC;IAC9C,QAAQ,CAAC,wBAAwB,EAAE,OAAO,6BAA6B,CAAC;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,OAAO,cAAc,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,OAAO,eAAe,CAAC;IAC5C,QAAQ,CAAC,uBAAuB,EAAE,OAAO,4BAA4B,CAAC;IACtE,QAAQ,CAAC,uBAAuB,EAAE,OAAO,4BAA4B,CAAC;IACtE,QAAQ,CAAC,UAAU,EAAE,OAAO,eAAe,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,OAAO,gBAAgB,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,OAAO,qBAAqB,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,OAAO,aAAa,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,OAAO,oBAAoB,CAAC;IACtD,QAAQ,CAAC,MAAM,EAAE,OAAO,WAAW,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,kBAAkB,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,OAAO,cAAc,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,OAAO,iBAAiB,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,OAAO,cAAc,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,OAAO,iBAAiB,CAAC;IAChD,QAAQ,CAAC,eAAe,EAAE,OAAO,oBAAoB,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,OAAO,aAAa,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,OAAO,aAAa,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,OAAO,mBAAmB,CAAC;IACpD,QAAQ,CAAC,UAAU,EAAE,OAAO,eAAe,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,OAAO,kBAAkB,CAAC;IAClD,QAAQ,CAAC,aAAa,EAAE,OAAO,kBAAkB,CAAC;IAClD,QAAQ,CAAC,YAAY,EAAE,OAAO,iBAAiB,CAAC;IAChD,QAAQ,CAAC,iBAAiB,EAAE,OAAO,sBAAsB,CAAC;IAC1D,QAAQ,CAAC,oBAAoB,EAAE,OAAO,yBAAyB,CAAC;IAChE,QAAQ,CAAC,4BAA4B,EAAE,OAAO,iCAAiC,CAAC;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,eAAe,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,OAAO,aAAa,CAAC;IACxC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,wBAAwB,CAAC;IAC9D,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,aAAa,CAAC;IACxC,QAAQ,CAAC,uBAAuB,EAAE,OAAO,4BAA4B,CAAC;IACtE,QAAQ,CAAC,sBAAsB,EAAE,OAAO,2BAA2B,CAAC;IACpE,QAAQ,CAAC,uBAAuB,EAAE,OAAO,4BAA4B,CAAC;IACtE,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,CAAC;IAClC,QAAQ,CAAC,eAAe,EAAE,OAAO,oBAAoB,CAAC;CA4CtD,CAAC"}