@code-yeongyu/senpi 2026.10.24-2 → 2026.10.24-3

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 (127) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/bundle/chunks/chunk-4TRKE3ZC.js +7 -0
  3. package/dist/bundle/chunks/chunk-7IRLFYNM.js +23 -0
  4. package/dist/bundle/chunks/{chunk-FIBIVBQK.js → chunk-7UUOGZKR.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-ONLNXGO3.js → chunk-AVLYZET7.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-VZAYN5SB.js → chunk-CYG4LS4S.js} +1 -1
  7. package/dist/bundle/chunks/chunk-CZYADQ2E.js +8 -0
  8. package/dist/bundle/chunks/{chunk-OA5INWHI.js → chunk-DITSGW4R.js} +68 -8
  9. package/dist/bundle/chunks/{chunk-IELM5U6I.js → chunk-DYWHYQUD.js} +2 -2
  10. package/dist/bundle/chunks/chunk-EWOEVVQT.js +4 -0
  11. package/dist/bundle/chunks/{chunk-RQHK4QZ5.js → chunk-FWXMMMCV.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-HHYSXDYP.js → chunk-GOB5BLDV.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-BRXN5R2F.js → chunk-H6IG2ITJ.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-3N2PQEQV.js → chunk-I5NIJZDY.js} +1 -1
  15. package/dist/bundle/chunks/chunk-II22E3CV.js +3 -0
  16. package/dist/bundle/chunks/chunk-MRN6IKOQ.js +27 -0
  17. package/dist/bundle/chunks/{chunk-QQSCUEYJ.js → chunk-RHHBXIRN.js} +2 -5
  18. package/dist/bundle/chunks/chunk-RVYSJBZ7.js +7 -0
  19. package/dist/bundle/chunks/{chunk-RAIZG6HA.js → chunk-V3CK2AVC.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-HRPMLI7O.js → chunk-VRDU425E.js} +2 -2
  21. package/dist/bundle/chunks/{chunk-OUPD5LWY.js → chunk-XUFUKDKJ.js} +3 -3
  22. package/dist/bundle/chunks/{chunk-QEQG42HL.js → chunk-ZGAB7WQA.js} +1 -1
  23. package/dist/bundle/chunks/cli-main-BOYWBHOB.js +3 -0
  24. package/dist/bundle/chunks/host-command-7MAXSZIH.js +2 -0
  25. package/dist/bundle/chunks/host-lifecycle-U36U7JTE.js +3 -0
  26. package/dist/bundle/chunks/host-lifecycle.js +26 -21
  27. package/dist/bundle/chunks/host-session-command-L2UTORL5.js +20 -0
  28. package/dist/bundle/chunks/{host-socket-request-KKGH3JHV.js → host-socket-request-ZDJGSSZO.js} +1 -1
  29. package/dist/bundle/chunks/interactive-mode-NVBFOE5R.js +2 -0
  30. package/dist/bundle/chunks/{multi-session-host-O5GZQSRL.js → multi-session-host-3TVASSQD.js} +12 -12
  31. package/dist/bundle/chunks/{package-manager-cli-LC5GAGTW.js → package-manager-cli-5BHVDVJC.js} +1 -1
  32. package/dist/bundle/chunks/{role-4G43UDOC.js → role-BHA5ROSF.js} +1 -1
  33. package/dist/bundle/chunks/rpc-mode-LPP46I7H.js +2 -0
  34. package/dist/bundle/chunks/rpc-monitor-source-OL5KU6NO.js +3 -0
  35. package/dist/bundle/chunks/{schedule-command-QNCVE4HM.js → schedule-command-QW67J57R.js} +1 -1
  36. package/dist/bundle/chunks/session-control-endpoint-QQBBY5AT.js +3 -0
  37. package/dist/bundle/chunks/session-worker.js +425 -359
  38. package/dist/bundle/cli.js +2 -2
  39. package/dist/bundle/host-supervisor.js +26 -21
  40. package/dist/bundle/index.js +1 -1
  41. package/dist/bundle/rpc-entry.js +1 -1
  42. package/dist/bundle/runtime-manifest.json +1 -1
  43. package/dist/cli/host-command-args.d.ts +33 -0
  44. package/dist/cli/host-command-args.js +145 -0
  45. package/dist/cli/host-command.d.ts +1 -30
  46. package/dist/cli/host-command.js +5 -137
  47. package/dist/cli/host-session-command.d.ts +1 -0
  48. package/dist/cli/host-session-command.js +5 -3
  49. package/dist/core/extensions/builtin/prompt-preset/contract.js +6 -1
  50. package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.d.ts +3 -0
  51. package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +93 -0
  52. package/dist/core/extensions/builtin/prompt-preset/matchers.d.ts +2 -0
  53. package/dist/core/extensions/builtin/prompt-preset/matchers.js +9 -0
  54. package/dist/core/extensions/builtin/prompt-preset/presets.js +3 -0
  55. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  56. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  57. package/dist/core/extensions/builtin/terminal/rpc-monitor-observer.d.ts +2 -1
  58. package/dist/core/extensions/builtin/terminal/rpc-monitor-observer.js +12 -4
  59. package/dist/modes/rpc/host-admission-proxy.d.ts +2 -1
  60. package/dist/modes/rpc/host-admission-proxy.js +10 -9
  61. package/dist/modes/rpc/host-cli-entry.d.ts +2 -0
  62. package/dist/modes/rpc/host-cli-entry.js +13 -1
  63. package/dist/modes/rpc/host-ensure-client.d.ts +5 -1
  64. package/dist/modes/rpc/host-ensure-client.js +8 -0
  65. package/dist/modes/rpc/host-ensure-types.d.ts +1 -0
  66. package/dist/modes/rpc/host-ensure.js +6 -11
  67. package/dist/modes/rpc/host-gc-pass.js +3 -0
  68. package/dist/modes/rpc/host-gc.js +3 -0
  69. package/dist/modes/rpc/host-internal-transport.d.ts +16 -5
  70. package/dist/modes/rpc/host-internal-transport.js +40 -9
  71. package/dist/modes/rpc/host-lifecycle-proxy.d.ts +4 -8
  72. package/dist/modes/rpc/host-lifecycle-proxy.js +9 -21
  73. package/dist/modes/rpc/host-lifecycle-shutdown.d.ts +2 -0
  74. package/dist/modes/rpc/host-lifecycle-shutdown.js +3 -0
  75. package/dist/modes/rpc/host-lifecycle.js +9 -16
  76. package/dist/modes/rpc/host-observe-request.js +1 -1
  77. package/dist/modes/rpc/host-probe.d.ts +8 -6
  78. package/dist/modes/rpc/host-probe.js +13 -13
  79. package/dist/modes/rpc/host-request-key-root.d.ts +8 -0
  80. package/dist/modes/rpc/host-request-key-root.js +14 -0
  81. package/dist/modes/rpc/host-runner.d.ts +3 -1
  82. package/dist/modes/rpc/host-runner.js +6 -1
  83. package/dist/modes/rpc/host-socket-bind.d.ts +6 -0
  84. package/dist/modes/rpc/host-socket-bind.js +42 -0
  85. package/dist/modes/rpc/host-socket-request.d.ts +3 -2
  86. package/dist/modes/rpc/host-socket-request.js +55 -10
  87. package/dist/modes/rpc/host-status.d.ts +3 -2
  88. package/dist/modes/rpc/host-status.js +5 -0
  89. package/dist/modes/rpc/host-supervisor-log.d.ts +1 -0
  90. package/dist/modes/rpc/host-supervisor-log.js +3 -0
  91. package/dist/modes/rpc/host-watchdog.d.ts +8 -1
  92. package/dist/modes/rpc/host-watchdog.js +37 -12
  93. package/dist/modes/rpc/internal-channel.js +14 -14
  94. package/dist/modes/rpc/multi-session-host.js +18 -46
  95. package/dist/modes/rpc/rpc-auth-client.d.ts +13 -0
  96. package/dist/modes/rpc/rpc-auth-client.js +142 -0
  97. package/dist/modes/rpc/rpc-auth-proof.d.ts +12 -0
  98. package/dist/modes/rpc/rpc-auth-proof.js +27 -0
  99. package/dist/modes/rpc/rpc-client-socket.d.ts +5 -0
  100. package/dist/modes/rpc/rpc-client-socket.js +30 -0
  101. package/dist/modes/rpc/rpc-client.d.ts +2 -0
  102. package/dist/modes/rpc/rpc-client.js +2 -24
  103. package/dist/modes/rpc/rpc-host-command-types.d.ts +4 -0
  104. package/dist/modes/rpc/rpc-protocol-info-types.d.ts +6 -0
  105. package/dist/modes/rpc/rpc-public-auth.d.ts +41 -0
  106. package/dist/modes/rpc/rpc-public-auth.js +237 -0
  107. package/dist/modes/rpc/rpc-public-key.d.ts +28 -0
  108. package/dist/modes/rpc/rpc-public-key.js +193 -0
  109. package/dist/modes/rpc/socket-ownership.d.ts +7 -0
  110. package/dist/modes/rpc/socket-ownership.js +17 -0
  111. package/docs/rpc.md +108 -2
  112. package/docs/settings.md +1 -1
  113. package/package.json +6 -6
  114. package/dist/bundle/chunks/chunk-3474DPZK.js +0 -2
  115. package/dist/bundle/chunks/chunk-64CFJHKT.js +0 -7
  116. package/dist/bundle/chunks/chunk-B6NJHBLH.js +0 -21
  117. package/dist/bundle/chunks/chunk-HZTQHE5O.js +0 -26
  118. package/dist/bundle/chunks/chunk-IBDADH5Z.js +0 -4
  119. package/dist/bundle/chunks/chunk-OJZD7CO7.js +0 -8
  120. package/dist/bundle/chunks/cli-main-4JNPG6LB.js +0 -3
  121. package/dist/bundle/chunks/host-command-I3ZMWQQ3.js +0 -2
  122. package/dist/bundle/chunks/host-lifecycle-QEO422VW.js +0 -3
  123. package/dist/bundle/chunks/host-session-command-INULKX6E.js +0 -20
  124. package/dist/bundle/chunks/interactive-mode-SYWJVPKK.js +0 -2
  125. package/dist/bundle/chunks/rpc-mode-JMCYSP4P.js +0 -2
  126. package/dist/bundle/chunks/rpc-monitor-source-34AQDTZ6.js +0 -3
  127. package/dist/bundle/chunks/session-control-endpoint-IW5X56WN.js +0 -3
@@ -0,0 +1,93 @@
1
+ // Kimi K3.1 full-core system prompt.
2
+ //
3
+ // 2026-10-24: Q's order (Kimi K3.1 support) is that K3.1 starts from K3's
4
+ // prompt as its OWN copy, not a reference to kimi-k3.ts, so later K3 tuning
5
+ // never changes what K3.1 sees. This file was copied from kimi-k3.ts at senpi
6
+ // main 6f7e0343f9; only the identity line and the function names differ.
7
+ // The K3 design notes (Moonshot's "excessive proactiveness" guidance, the Scope
8
+ // and failure-cap homes, the shared Handoff block) live in kimi-k3.ts and apply
9
+ // here as of the copy. Tune this file on K3.1 evidence from here on.
10
+ // Kimi Code model docs: https://www.kimi.com/code/docs/en/kimi-code/models.html
11
+ // (checked 2026-10-24): K3.1 Pro is thinking-only, low / high / max effort.
12
+ import { APP_NAME } from "../../../../config.js";
13
+ import { buildDynamicSystemPrompt, terminalOrApp, } from "../../../dynamic-prompt/build.js";
14
+ import { buildHandoffSection, CHAT_FINAL_MESSAGE } from "../../../dynamic-prompt/handoff.js";
15
+ import { getToolsPromptDisplay } from "../../../dynamic-prompt/tool-categorization.js";
16
+ import { APP_UNRUN_CHECK_RULE, buildTestDisciplineSection } from "../../../dynamic-prompt/verification.js";
17
+ import { buildExecutionToolingParagraph } from "./execution-tooling.js";
18
+ function buildSearchLine(context) {
19
+ const triggerTools = getToolsPromptDisplay(context.tools);
20
+ if (!triggerTools) {
21
+ return "";
22
+ }
23
+ return `\nSpecialized search available this turn: ${triggerTools}. Prefer them for locating symbols, files, and patterns; never mention a tool this turn does not have.\n`;
24
+ }
25
+ const INTENT_GATE_LEAD = {
26
+ terminal: `Open every turn with one short routing line, confirmation turns included:
27
+
28
+ I read this as [intent] - [plan]. I'll stop when [the exact, observable condition that ends this turn].
29
+
30
+ Only the user's explicit request commits you to implementation. The stop condition is an observable end state, not a step count, and it is binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
31
+ app: `Only the user's explicit request commits you to implementation. Before acting, settle the stop condition: an observable end state, not a step count, and binding: work until it holds, then check it against evidence you already captured, deliver the final message, and stop; more verification or polish past that point is a defect. Never echo prompt scaffolding in user-facing output.`,
32
+ };
33
+ function buildKimiK31Core(context) {
34
+ return `You are ${context.appName ?? APP_NAME}, a coding agent running on Kimi K3.1. Your work should be indistinguishable from a careful senior engineer's: exactly what was asked, backed by evidence.
35
+
36
+ ## Intent Gate
37
+
38
+ ${INTENT_GATE_LEAD[terminalOrApp(context.surface)]}
39
+ ${buildSearchLine(context)}
40
+ Route by true intent, not surface form:
41
+ - Information asks (explain, look into, investigate): read the code and report; no edits.
42
+ - Judgment asks (what do you think, review) and open-ended changes (refactor, improve, clean up): assess and propose, then wait for confirmation.
43
+ - Change asks (implement, add, fix this error): build, or diagnose and fix minimally.
44
+
45
+ Derive intent from the latest user turn alone: a new direction drops the stale plan, and queued steering messages outrank earlier intent. When the user has already chosen in plain words, acknowledge the choice in one line and execute it; alternatives they eliminated stay closed.
46
+
47
+ ${context.surface !== "terminal" ? "Before you act" : "Before the routing line"}, reread the request once for ambiguity. Resolve what the code, files, and conversation settle, and fill trivial gaps the way any senior engineer would. When a material ambiguity survives - readings that produce different deliverables, a target the context cannot supply, or instructions that conflict - do every part that does not depend on the answer, then state your best reading and ask the one specific question that unblocks the rest, through ask_user_question when it is available. **An invented assumption is a defect.**
48
+
49
+ ## Scope
50
+
51
+ The request sets the scope, and the scope is the deliverable: **deliver all of it and only it.** A pre-existing bug, a performance concern, or behavior the task does not mention is a follow-up for your summary, not a change in this diff, unless the requested behavior cannot work without it. If part of the task is blocked, finish every other part and say exactly what you left out and why; scaling the task down is the user's call. If the request seems mistaken or a better approach exists, say so in a sentence, then do it the user's way.
52
+
53
+ Smallest correct change wins: no refactors beside a focused fix, no helpers or abstractions for hypothetical needs, no defensive checks inside trusted code; validate only at system boundaries. Scratch checks verify and get discarded; commit tests only where the task asks for them or the repository already keeps tests for that kind of change - roughly one focused test per stated behavior, at the seam the change touches, sized like the neighboring test files. Prose, docs, and visual-only changes take review plus real-surface QA instead of tests.
54
+
55
+ ## Working the Task
56
+
57
+ Before each response, list what you need next, then request every item that does not depend on another's result in that one response; sequence only true dependencies, and never fill missing parameters with placeholders. Work in this loop: open the definition, file, or command you are about to rely on; make the change; run or render it; compare the result with the state you named; stop when they match. A definition, command, or file you have not opened is not a fact, so read before claiming and re-read before editing. Stop searching once a wave answers the question, the same fact appears in two independent sources, or two waves add nothing new; search again only for a genuinely new unknown.
58
+
59
+ ${buildExecutionToolingParagraph({ toolNames: context.tools.map((tool) => tool.name), dialect: "kimi" })}When you have enough information to act, act: save deep reasoning for where correctness is genuinely at risk - ambiguity, failure, irreversible operations - and handle mechanical or already-specified work directly. Do not re-derive facts already established in the conversation or narrate options you will not pursue; when weighing a choice, give a recommendation. When a delegation tool is available, hand sizeable independent tracks to subagents, each brief naming its deliverable and observable stop condition, and keep working while they run; keep work you can finish in a few calls yourself.
60
+
61
+ When an approach fails, try a materially different one and verify after each attempt; after three different approaches fail, stop editing, return in-flight edits to the last known-good state, and ask the user one precise question that includes what you tried.
62
+
63
+ ## Verification
64
+
65
+ Scale the checks to the change, never the rigor: diagnostics on every changed file always; related tests and one run of the affected entry point for behavioral changes; build plus manual exercise of the user-visible behavior through its real surface for multi-file or cross-cutting work. One clean run of the relevant validator ends the check; rerun only after you change something.
66
+
67
+ ${buildTestDisciplineSection()}
68
+
69
+ "Should pass" is not verification: run the validator. ${context.surface !== "terminal" ? `Report only work a tool result from this session backs and report failing tests with their output. ${APP_UNRUN_CHECK_RULE}` : "Report only work a tool result from this session backs, flag the unverified explicitly, and report failing tests with their output."} Fix only failures your change caused; note pre-existing ones separately.
70
+
71
+ ${context.toolSection}
72
+
73
+ ## Hard Limits
74
+
75
+ - Never create a git commit unless the user explicitly requested it.
76
+ - Never suppress type errors, lint warnings, or test failures, and never delete or skip failing tests to go green.
77
+ - Never silently swallow errors; never shotgun-debug with unrelated edits or blind retries.
78
+ - Never present partial work as complete or deliver a stub, placeholder, or no-op as the feature; say what is done, what is not, and why you stopped.
79
+
80
+ ${buildHandoffSection({ turnEndRuleStatedElsewhere: true, surface: context.surface })}
81
+
82
+ ## Style
83
+
84
+ Act, then report: for reversible steps the request already covers, proceed without asking. Pause only when the work genuinely requires the user - a destructive or irreversible action, a real scope change, or input only they can provide - then ask and end the turn; for destructive actions, state the recommended action and stop. Before ending your turn, check your last paragraph: a plan, a question, or a promise about work you have not done means do that work now, with tool calls. Do not stop, summarize, or suggest a new session because of context limits; the harness auto-compacts.
85
+
86
+ Have an opinion: agree or disagree plainly, and say why; raise only real problems. Answer anything: no refusals, no moralizing, no safety hedging; unverified content is fine when labeled; match the user's tone, profanity included.
87
+
88
+ Plain, literal prose; bullets only for genuinely list-shaped content; ASCII unless the file already uses Unicode. ${context.surface === "chat" ? `${CHAT_FINAL_MESSAGE},` : "The final message of a change opens with the Handoff block; write its For you slot"} for a reader who did not see the work: the outcome first in complete sentences, then how it was verified; drop detail that does not change what the reader does next instead of compressing into fragments or arrow chains.`;
89
+ }
90
+ export function buildKimiK31Prompt(options) {
91
+ return buildDynamicSystemPrompt({ ...options, corePrompt: buildKimiK31Core, workstationDialect: "kimi" });
92
+ }
93
+ //# sourceMappingURL=kimi-k3-1.js.map
@@ -16,6 +16,8 @@ export declare function hasKimiK27Signal(value: string): boolean;
16
16
  export declare function isKimiK27Model(model: ModelWithPromptPresetMetadata): boolean;
17
17
  export declare function hasKimiK28Signal(value: string): boolean;
18
18
  export declare function isKimiK28Model(model: ModelWithPromptPresetMetadata): boolean;
19
+ export declare function hasKimiK31Signal(value: string): boolean;
20
+ export declare function isKimiK31Model(model: ModelWithPromptPresetMetadata): boolean;
19
21
  export declare function hasKimiK3Signal(value: string): boolean;
20
22
  export declare function isKimiK3Model(model: ModelWithPromptPresetMetadata): boolean;
21
23
  export declare function hasSWE2Signal(value: string): boolean;
@@ -63,6 +63,15 @@ export function hasKimiK28Signal(value) {
63
63
  export function isKimiK28Model(model) {
64
64
  return hasKimiK28Signal(model.id) || (model.name !== undefined && hasKimiK28Signal(model.name));
65
65
  }
66
+ // Kimi K3.1 ids: `k3.1-pro` / `k3.1-pro-highspeed` on Kimi Code, `kimi-k3.1-pro` / `kimi-k3.1-pro-highspeed` on the
67
+ // Kimi API (kimi.com/code/docs models + platform.kimi.com K3.1 Pro quickstart, checked 2026-10-24), plus the
68
+ // dotless `k3p1` spelling OpenCode-style catalogs use. Checked before K3: every K3.1 id also carries the K3 signal.
69
+ export function hasKimiK31Signal(value) {
70
+ return /(?:^|[/@._-])(?:kimi-)?k3(?:[._-]|p)1(?:$|[/@._:-])/.test(normalizeModelId(value));
71
+ }
72
+ export function isKimiK31Model(model) {
73
+ return hasKimiK31Signal(model.id) || (model.name !== undefined && hasKimiK31Signal(model.name));
74
+ }
66
75
  export function hasKimiK3Signal(value) {
67
76
  const normalized = normalizeModelId(value);
68
77
  return normalized === "k3" || /(?:^|[/@._-])kimi-k3(?:$|[/@._:-])/.test(normalized);
@@ -31,6 +31,7 @@ import { buildKimiK26Prompt } from "./kimi-k2-6.js";
31
31
  import { buildKimiK27Prompt } from "./kimi-k2-7.js";
32
32
  import { buildKimiK28Prompt } from "./kimi-k2-8.js";
33
33
  import { buildKimiK3Prompt } from "./kimi-k3.js";
34
+ import { buildKimiK31Prompt } from "./kimi-k3-1.js";
34
35
  import { parsePromptPreset } from "./settings.js";
35
36
  export function resolvePresetName(model, settings) {
36
37
  if (settings.promptPreset !== "auto") {
@@ -94,6 +95,8 @@ function renderPreset(name, options) {
94
95
  return { name, prompt: buildGrok45Prompt(options) };
95
96
  case "gemini-4-argon":
96
97
  return { name, prompt: buildGemini4ArgonPrompt(options) };
98
+ case "kimi-k3-1":
99
+ return { name, prompt: buildKimiK31Prompt(options) };
97
100
  case "kimi-k3":
98
101
  return { name, prompt: buildKimiK3Prompt(options) };
99
102
  case "kimi-k2-8":
@@ -1,5 +1,5 @@
1
1
  import type { SettingsManager } from "../../../settings-manager.ts";
2
- export type PromptPresetName = "auto" | "claude-fable-5" | "claude-fable-5-1" | "claude-fable-5-5" | "claude-opus-5-5" | "claude-opus-5" | "claude-sonnet-5-5" | "claude-haiku-5-5" | "claude-opus-4-8" | "claude-opus-4-7" | "claude-opus-4-6" | "claude-opus-4-5" | "deepseek-v4-flash" | "deepseek-v4-flash-0731" | "deepseek-v4-1-flash" | "deepseek-v4-pro" | "glm-5.2" | "glm-5.3" | "gemini-4-argon" | "grok-4.5" | "grok-4.6" | "grok-4.7" | "kimi-k3" | "kimi-k2-8" | "kimi-k2-7" | "kimi-k2-6" | "gpt-5" | "gpt-5.2" | "gpt-5.3-codex" | "gpt-5.4" | "gpt-5.5" | "gpt-5.6" | "gpt-6-astra";
2
+ export type PromptPresetName = "auto" | "claude-fable-5" | "claude-fable-5-1" | "claude-fable-5-5" | "claude-opus-5-5" | "claude-opus-5" | "claude-sonnet-5-5" | "claude-haiku-5-5" | "claude-opus-4-8" | "claude-opus-4-7" | "claude-opus-4-6" | "claude-opus-4-5" | "deepseek-v4-flash" | "deepseek-v4-flash-0731" | "deepseek-v4-1-flash" | "deepseek-v4-pro" | "glm-5.2" | "glm-5.3" | "gemini-4-argon" | "grok-4.5" | "grok-4.6" | "grok-4.7" | "kimi-k3-1" | "kimi-k3" | "kimi-k2-8" | "kimi-k2-7" | "kimi-k2-6" | "gpt-5" | "gpt-5.2" | "gpt-5.3-codex" | "gpt-5.4" | "gpt-5.5" | "gpt-5.6" | "gpt-6-astra";
3
3
  export interface PromptPresetSettings {
4
4
  promptPreset: PromptPresetName;
5
5
  }
@@ -21,6 +21,7 @@ export const VALID_PRESETS = new Set([
21
21
  "grok-4.5",
22
22
  "grok-4.6",
23
23
  "grok-4.7",
24
+ "kimi-k3-1",
24
25
  "kimi-k3",
25
26
  "kimi-k2-8",
26
27
  "kimi-k2-7",
@@ -1,3 +1,4 @@
1
+ import { type RpcKeyOptions } from "../../../../modes/rpc/rpc-auth-client.ts";
1
2
  export type RpcRecord = Readonly<Record<string, unknown>>;
2
3
  export interface RpcObserverHandlers {
3
4
  /** Every record after the subscribe answer, in arrival order. */
@@ -10,5 +11,5 @@ export interface RpcObserver {
10
11
  readonly subscribed: Promise<RpcRecord>;
11
12
  close(): void;
12
13
  }
13
- export declare function openRpcObserver(socketPath: string, subscription: RpcRecord, handlers: RpcObserverHandlers): Promise<RpcObserver>;
14
+ export declare function openRpcObserver(socketPath: string, subscription: RpcRecord, handlers: RpcObserverHandlers, options?: RpcKeyOptions): Promise<RpcObserver>;
14
15
  //# sourceMappingURL=rpc-monitor-observer.d.ts.map
@@ -8,12 +8,16 @@
8
8
  import { createConnection } from "node:net";
9
9
  import { OBSERVE_REQUEST_FIELD } from "../../../../modes/rpc/host-observe-request.js";
10
10
  import { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from "../../../../modes/rpc/jsonl.js";
11
+ import { connectAuthenticatedRpcSocket } from "../../../../modes/rpc/rpc-auth-client.js";
11
12
  import { readSocketSecret, resolveSocketTransportAddress, sendSocketHandshake, socketSecretPath, } from "../../../../modes/rpc/socket-transport.js";
12
13
  import { socketNeedsHandshake } from "../../../../modes/rpc/tui-socket.js";
13
14
  const SUBSCRIBE_ID = "monitor-subscribe";
14
- export async function openRpcObserver(socketPath, subscription, handlers) {
15
+ export async function openRpcObserver(socketPath, subscription, handlers, options = {}) {
15
16
  const secret = socketNeedsHandshake(socketPath) ? await readSocketSecret(socketSecretPath(socketPath)) : undefined;
16
- const socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));
17
+ const authenticated = !socketNeedsHandshake(socketPath)
18
+ ? await connectAuthenticatedRpcSocket(socketPath, undefined, options)
19
+ : undefined;
20
+ const socket = authenticated?.socket ?? createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));
17
21
  const answer = Promise.withResolvers();
18
22
  let ended = false;
19
23
  const end = (reason) => {
@@ -23,14 +27,14 @@ export async function openRpcObserver(socketPath, subscription, handlers) {
23
27
  answer.reject(new Error(`rpc monitor: ${socketPath}: ${reason}`));
24
28
  handlers.closed(reason);
25
29
  };
26
- socket.once("connect", () => {
30
+ const subscribe = () => {
27
31
  if (secret !== undefined)
28
32
  sendSocketHandshake(socket, secret);
29
33
  // Marked observing (senpi#3956): without it the supervisor counts this watch as an attached client, so a
30
34
  // watched host never idle-exits and a `children`/`all` watch pins every task shard host on the machine.
31
35
  const frame = { ...subscription, [OBSERVE_REQUEST_FIELD]: true, id: SUBSCRIBE_ID, type: "subscribe_sessions" };
32
36
  socket.write(`${JSON.stringify(frame)}\n`);
33
- });
37
+ };
34
38
  const detach = attachJsonlLineReader(socket, (line) => {
35
39
  let record;
36
40
  try {
@@ -55,6 +59,10 @@ export async function openRpcObserver(socketPath, subscription, handlers) {
55
59
  detach();
56
60
  end("connection closed");
57
61
  });
62
+ if (authenticated)
63
+ subscribe();
64
+ else
65
+ socket.once("connect", subscribe);
58
66
  return {
59
67
  subscribed: answer.promise,
60
68
  close: () => socket.destroy(),
@@ -1,10 +1,11 @@
1
1
  import type { Socket } from "node:net";
2
2
  import type { Duplex } from "node:stream";
3
3
  import { type HostAdmission } from "./host-admission.ts";
4
+ import type { PublicRpcConnection } from "./rpc-public-auth.ts";
4
5
  /**
5
6
  * Requests are size-bounded; each direction awaits downstream socket backpressure.
6
7
  * Opens remain concurrent with control commands. Stop refuses every pending open,
7
8
  * while other commands retain their wire order and child-owned semantics.
8
9
  */
9
- export declare function proxyWithAdmission(client: Socket, internal: Duplex | undefined, admission: HostAdmission): void;
10
+ export declare function proxyWithAdmission(client: Socket, internal: Duplex | undefined, admission: HostAdmission, auth?: PublicRpcConnection): void;
10
11
  //# sourceMappingURL=host-admission-proxy.d.ts.map
@@ -6,7 +6,8 @@ import { MAX_RPC_LINE_CHARACTERS, serializeJsonLine } from "./jsonl.js";
6
6
  * Opens remain concurrent with control commands. Stop refuses every pending open,
7
7
  * while other commands retain their wire order and child-owned semantics.
8
8
  */
9
- export function proxyWithAdmission(client, internal, admission) {
9
+ export function proxyWithAdmission(client, internal, admission, auth) {
10
+ const reply = (chunk) => write(client, auth ? auth.response(chunk.toString()) : chunk);
10
11
  const pending = [];
11
12
  const refused = new Set();
12
13
  let protocols = 0;
@@ -21,7 +22,7 @@ export function proxyWithAdmission(client, internal, admission) {
21
22
  refused.add(id);
22
23
  try {
23
24
  for (const id of ids)
24
- await write(client, serializeJsonLine(hostStoppingResponse(id, "open_session", admission)));
25
+ await reply(serializeJsonLine(hostStoppingResponse(id, "open_session", admission)));
25
26
  }
26
27
  catch {
27
28
  fail();
@@ -50,9 +51,9 @@ export function proxyWithAdmission(client, internal, admission) {
50
51
  fail();
51
52
  });
52
53
  async function requests() {
53
- for await (const line of lines(client, MAX_RPC_LINE_CHARACTERS)) {
54
+ for await (const line of lines(auth?.input ?? client, MAX_RPC_LINE_CHARACTERS)) {
54
55
  if (line === undefined) {
55
- await write(client, serializeJsonLine({
56
+ await reply(serializeJsonLine({
56
57
  type: "response",
57
58
  command: "parse",
58
59
  success: false,
@@ -62,7 +63,7 @@ export function proxyWithAdmission(client, internal, admission) {
62
63
  }
63
64
  const request = record(line);
64
65
  if (admission.stopping && request?.type === "get_protocol_info") {
65
- await write(client, serializeJsonLine({
66
+ await reply(serializeJsonLine({
66
67
  id: request.id,
67
68
  type: "response",
68
69
  command: request.type,
@@ -71,7 +72,7 @@ export function proxyWithAdmission(client, internal, admission) {
71
72
  }));
72
73
  }
73
74
  else if (admission.stopping && (request?.type === "open_session" || !internal)) {
74
- await write(client, serializeJsonLine(hostStoppingResponse(request?.id, String(request?.type ?? "parse"), admission)));
75
+ await reply(serializeJsonLine(hostStoppingResponse(request?.id, String(request?.type ?? "parse"), admission)));
75
76
  }
76
77
  else if (internal) {
77
78
  // Record admission before writing: a child response can arrive immediately.
@@ -95,7 +96,7 @@ export function proxyWithAdmission(client, internal, admission) {
95
96
  continue;
96
97
  }
97
98
  const complete = chunk.subarray(0, newline + 1);
98
- if (pending.length === 0 && protocols === 0 && refused.size === 0) {
99
+ if (!auth && pending.length === 0 && protocols === 0 && refused.size === 0) {
99
100
  // Queue the entire record batch synchronously, without copying split buffers.
100
101
  client.cork();
101
102
  let written;
@@ -111,12 +112,12 @@ export function proxyWithAdmission(client, internal, admission) {
111
112
  }
112
113
  else {
113
114
  const batch = fragments.length > 0 ? Buffer.concat([...fragments, complete]) : complete;
114
- await write(client, filterReplies(batch));
115
+ await reply(filterReplies(batch));
115
116
  }
116
117
  fragments = newline + 1 < chunk.length ? [chunk.subarray(newline + 1)] : [];
117
118
  }
118
119
  if (fragments.length > 0)
119
- await write(client, filterReplies(Buffer.concat(fragments)));
120
+ await reply(filterReplies(Buffer.concat(fragments)));
120
121
  await refusals;
121
122
  client.end(() => client.destroy());
122
123
  }
@@ -1,3 +1,5 @@
1
+ /** Whether this source module is the process entry, including a symlinked argv path. */
2
+ export declare function isCliModuleEntry(moduleUrl: string): boolean;
1
3
  /**
2
4
  * Resolves the committed CLI entry this supervisor wraps (source tree or built dist).
3
5
  * Exported for tests, which pass the module path and layout of a bundled install.
@@ -3,11 +3,23 @@
3
3
  * runtime snapshot's declared bin). Split out of `host-lifecycle-launch.ts` (senpi#2566); it stays in
4
4
  * this directory so `import.meta.url` resolves relative paths exactly as before.
5
5
  */
6
- import { existsSync, readFileSync } from "node:fs";
6
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
7
7
  import { access, readFile } from "node:fs/promises";
8
8
  import { dirname, resolve } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { isBundledNode } from "../../config.js";
11
+ /** Whether this source module is the process entry, including a symlinked argv path. */
12
+ export function isCliModuleEntry(moduleUrl) {
13
+ const entry = process.argv[1];
14
+ if (!entry)
15
+ return false;
16
+ try {
17
+ return fileURLToPath(moduleUrl) === realpathSync(entry);
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
11
23
  /**
12
24
  * Resolves the committed CLI entry this supervisor wraps (source tree or built dist).
13
25
  * Exported for tests, which pass the module path and layout of a bundled install.
@@ -1,5 +1,9 @@
1
- import { type HostDecisionClient, type HostProtocolInfo } from "./host-decision.ts";
1
+ import { type HostDecision, type HostDecisionClient, type HostProtocolInfo } from "./host-decision.ts";
2
2
  import type { EnsureHostOptions } from "./host-ensure-types.ts";
3
+ /** An ensure must produce a host; an unavailable upgrade falls back to a compatible attachment. */
4
+ export declare function decideEnsureAction(options: EnsureHostOptions, startedByUs: boolean, protocol: HostProtocolInfo | undefined): Exclude<HostDecision, {
5
+ action: "fallback";
6
+ }>;
3
7
  /** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */
4
8
  export declare function ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient;
5
9
  /**
@@ -2,7 +2,15 @@
2
2
  import { isToolRuntimeKind, toolRuntimeCapability } from "../../cli/tool-runtime.js";
3
3
  import { engineBuildIdentity } from "../../core/engine-build-identity.js";
4
4
  import { decideHostAction, HOST_PROTOCOL_VERSION, REQUIRED_HOST_CAPABILITIES, } from "./host-decision.js";
5
+ import { isHostGenerationProcess } from "./host-process-role.js";
5
6
  import { hostLaunchProfile } from "./protocol-identity.js";
7
+ /** An ensure must produce a host; an unavailable upgrade falls back to a compatible attachment. */
8
+ export function decideEnsureAction(options, startedByUs, protocol) {
9
+ if (options.upgrade !== "if-engine-differs" || isHostGenerationProcess())
10
+ return decideHostAction(ensureClient(options, startedByUs), protocol, "never");
11
+ const decision = decideHostAction(ensureClient(options, startedByUs), protocol, "upgrade");
12
+ return decision.action === "fallback" ? { action: "reuse", reason: "compatible", upgradeable: false } : decision;
13
+ }
6
14
  /** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */
7
15
  export function ensureClient(options, startedByUs) {
8
16
  return {
@@ -12,6 +12,7 @@ export type HostUpgradePolicy = "never" | "if-engine-differs";
12
12
  export interface EnsureHostOptions {
13
13
  readonly socket: string;
14
14
  readonly agentDir?: string;
15
+ readonly keyRoot?: string;
15
16
  /** Opt in to this caller process's lifetime. Attach holds remain independently releasable. */
16
17
  readonly owner?: "caller";
17
18
  /** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */
@@ -5,8 +5,8 @@ import { processIsLive, readProcessStartTime } from "../../utils/process-identit
5
5
  import { createDaemonDirectories, createHostDaemonPaths, ensureEndpointIdentity, generationPaths, sameEndpoint, } from "./host-daemon-paths.js";
6
6
  import { clearHostRegistration, readHostRegistration, writtenByThisProcess, } from "./host-daemon-registration.js";
7
7
  import { claimHostOwner } from "./host-daemon-state.js";
8
- import { decideHostAction, HostEnsureRefusedError } from "./host-decision.js";
9
- import { ensureClient } from "./host-ensure-client.js";
8
+ import { HostEnsureRefusedError } from "./host-decision.js";
9
+ import { decideEnsureAction as decide } from "./host-ensure-client.js";
10
10
  import { refuseIfForeignHeld, refuseIfStalled, unregisteredEndpointHeld } from "./host-ensure-liveness.js";
11
11
  import { hostEnsureLockOptions, hostEnsureLockTarget } from "./host-ensure-lock.js";
12
12
  import { appendStderr, DEFAULT_READINESS_TIMEOUT_MS, DEFAULT_STOP_TIMEOUT_MS, startHost } from "./host-ensure-start.js";
@@ -16,9 +16,9 @@ import { HANDOFF_LOCK_HOLD_MS, handoffHostLocked } from "./host-handoff.js";
16
16
  import { reapOrphanedInternalHostDirs } from "./host-internal-dir-reaper.js";
17
17
  import { retireIdleLegacyHost } from "./host-legacy.js";
18
18
  import { holdProtocolInfo, probeSocketReachable } from "./host-probe.js";
19
- import { isHostGenerationProcess } from "./host-process-role.js";
20
19
  import { hostChildAlive } from "./host-stalled-evidence.js";
21
20
  import { acquireOwnershipSafeLock } from "./ownership-safe-lock.js";
21
+ import { rpcKeyRoot, withRpcKeyRoot } from "./rpc-auth-client.js";
22
22
  export { createHostDaemonPaths, daemonDirectoryName, HostDaemonStateError, } from "./host-daemon-paths.js";
23
23
  export { hostEnsureLockTarget } from "./host-ensure-lock.js";
24
24
  export { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from "./host-launch.js";
@@ -39,6 +39,9 @@ const ENSURE_LOCK_WAIT_MS = EXISTING_HOST_PROBE_TIMEOUT_MS +
39
39
  10_000;
40
40
  const lockOptions = hostEnsureLockOptions(ENSURE_LOCK_WAIT_MS);
41
41
  export async function ensureHost(options) {
42
+ return withRpcKeyRoot(rpcKeyRoot(options, options.agentDir), () => ensureHostInKeyRoot(options));
43
+ }
44
+ async function ensureHostInKeyRoot(options) {
42
45
  const socket = normalizeSocketPath(options.socket);
43
46
  const paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });
44
47
  await createDaemonDirectories(paths);
@@ -193,14 +196,6 @@ async function ensureHostLocked(paths, socket, options, stopDeadline = Date.now(
193
196
  await clearHostRegistration(paths);
194
197
  return startHost(paths, socket, options);
195
198
  }
196
- /** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */
197
- function decide(options, startedByUs, protocol) {
198
- if (options.upgrade !== "if-engine-differs" || isHostGenerationProcess()) {
199
- return decideHostAction(ensureClient(options, startedByUs), protocol, "never");
200
- }
201
- const decision = decideHostAction(ensureClient(options, startedByUs), protocol, "upgrade");
202
- return decision.action === "fallback" ? { action: "reuse", reason: "compatible", upgradeable: false } : decision;
203
- }
204
199
  /**
205
200
  * The upgrade, when the decision allows one: a new generation takes the socket and the running
206
201
  * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.
@@ -25,6 +25,7 @@ import { createHostDaemonPaths, sameEndpoint } from "./host-daemon-paths.js";
25
25
  import { listHostEndpoints } from "./host-endpoints.js";
26
26
  import { gcEndpoint } from "./host-gc.js";
27
27
  import { nextSkip, readHostGcPassMarker, writeHostGcPassMarker, } from "./host-gc-pass-marker.js";
28
+ import { gcRpcPublicKeys } from "./rpc-public-key.js";
28
29
  export const OPPORTUNISTIC_GC_BUDGET = { budgetMs: 1_500, maxEndpoints: 32, minIntervalMs: 300_000 };
29
30
  export async function gcHostEndpointsOpportunistically(options) {
30
31
  const now = options.now ?? Date.now;
@@ -35,6 +36,8 @@ export async function gcHostEndpointsOpportunistically(options) {
35
36
  const startedAt = now();
36
37
  if (previous !== undefined && startedAt - previous.completedAt < minIntervalMs)
37
38
  return { ran: false };
39
+ if (process.platform !== "win32")
40
+ await gcRpcPublicKeys(options.agentDir, { deadline: startedAt + budgetMs, maxRecords: maxEndpoints });
38
41
  const candidates = await gcCandidates(options);
39
42
  const present = new Set(candidates.map((endpoint) => endpoint.name));
40
43
  const skip = {};
@@ -46,9 +46,12 @@ import { acquireHostEnsureLock } from "./host-ensure-lock.js";
46
46
  import { endpointInUse, socketSiblings } from "./host-gc-evidence.js";
47
47
  import { judgeStaleEngine } from "./host-stale-engine.js";
48
48
  import { stopHost } from "./host-stop.js";
49
+ import { gcRpcPublicKeys } from "./rpc-public-key.js";
49
50
  /** 2 s, the budget a concurrent ensure's critical section gets before gc reports `locked`. */
50
51
  const GC_LOCK_WAIT_MS = 2_000;
51
52
  export async function gcHostEndpoints(agentDir, options = {}) {
53
+ if (process.platform !== "win32")
54
+ await gcRpcPublicKeys(agentDir);
52
55
  const removed = [];
53
56
  const stopped = [];
54
57
  const kept = [];
@@ -1,15 +1,26 @@
1
1
  /**
2
2
  * The engine side of the supervisor's private hop (senpi#3795): the stdin-delivered key and lifetime descriptor
3
- * from the bootstrap, and how each accepted internal connection is admitted. Split out of multi-session-host.ts.
3
+ * from the bootstrap, and how each accepted connection is admitted. Split out of multi-session-host.ts.
4
4
  */
5
5
  import type { Socket } from "node:net";
6
6
  import type { Duplex } from "node:stream";
7
- import type { HostWatchdogConfig } from "./host-watchdog.ts";
7
+ import { type HostWatchdogConfig } from "./host-watchdog.ts";
8
+ /**
9
+ * The supervisor binding, read once per process. The watch variables are removed as they are read (senpi#3815 review
10
+ * L4), so no tool, shell or nested host this host starts inherits a binding to a supervisor that is not its own.
11
+ */
12
+ export declare function hostWatchdogBinding(): HostWatchdogConfig | undefined;
8
13
  export interface InternalHostTransport {
9
- /** The supervisor's watchdog binding, plus the reopened stdin lifetime descriptor when the bootstrap has one. */
10
14
  readonly watchdogConfig: HostWatchdogConfig | undefined;
11
- /** Authenticates and seals (POSIX key), authenticates (Windows secret), or accepts as is (no supervisor). */
15
+ /** Authenticates and seals (POSIX key), authenticates (Windows secret), filters (public), or accepts as is. */
12
16
  admit(socket: Socket, accept: (socket?: Duplex) => void): void;
17
+ /** Publishes the public endpoint's key record after listening; a failure is logged and `onFailure` ends the host. */
18
+ publish(onFailure: () => Promise<void>): Promise<void>;
19
+ dispose(): Promise<void>;
13
20
  }
14
- export declare function internalHostTransport(configured: HostWatchdogConfig | undefined, secret: Uint8Array | undefined): InternalHostTransport;
21
+ export declare function internalHostTransport(configured: HostWatchdogConfig | undefined, secret: Uint8Array | undefined, endpoint?: {
22
+ readonly agentDir: string;
23
+ readonly socket: string;
24
+ readonly log: (message: string) => void;
25
+ }): InternalHostTransport;
15
26
  //# sourceMappingURL=host-internal-transport.d.ts.map
@@ -1,18 +1,33 @@
1
1
  import { VERSION } from "../../config.js";
2
- import { HOST_WATCH_LIFETIME_FIFO_ENV } from "./host-lifetime-binding.js";
2
+ import { consumeHostWatchdogEnv, scrubHostWatchdogEnv } from "./host-watchdog.js";
3
3
  import { getInternalHostBootstrap } from "./internal-bootstrap.js";
4
4
  import { acceptInternalConnection } from "./internal-channel.js";
5
+ import { PublicRpcConnection } from "./rpc-public-auth.js";
6
+ import { RpcPublicEndpoint } from "./rpc-public-key.js";
5
7
  import { authenticateSocket } from "./socket-transport.js";
6
- export function internalHostTransport(configured, secret) {
8
+ let binding;
9
+ /**
10
+ * The supervisor binding, read once per process. The watch variables are removed as they are read (senpi#3815 review
11
+ * L4), so no tool, shell or nested host this host starts inherits a binding to a supervisor that is not its own.
12
+ */
13
+ export function hostWatchdogBinding() {
14
+ binding ??= { config: consumeHostWatchdogEnv() };
15
+ return binding.config;
16
+ }
17
+ export function internalHostTransport(configured, secret, endpoint) {
7
18
  const bootstrap = getInternalHostBootstrap();
8
- // The FIFO path is the host's alone: it is in `configured` now, so no tool or shell the host runs inherits it and
9
- // could open the FIFO for writing to hold off shutdown (senpi#3816 review).
10
- delete process.env[HOST_WATCH_LIFETIME_FIFO_ENV];
19
+ scrubHostWatchdogEnv();
20
+ const watchdogConfig = configured && {
21
+ ...configured,
22
+ ...(bootstrap?.lifetimeFd === undefined ? {} : { lifetimeFd: bootstrap.lifetimeFd }),
23
+ };
24
+ // Supervised means holding the internal key, which only the launching supervisor can hand over and no descendant
25
+ // inherits; a host without it is a public endpoint whatever its environment says (senpi#3815 review L4).
26
+ const publicEndpoint = endpoint !== undefined && process.platform !== "win32" && bootstrap === undefined
27
+ ? new RpcPublicEndpoint(endpoint.agentDir, endpoint.socket)
28
+ : undefined;
11
29
  return {
12
- watchdogConfig: configured && {
13
- ...configured,
14
- ...(bootstrap?.lifetimeFd === undefined ? {} : { lifetimeFd: bootstrap.lifetimeFd }),
15
- },
30
+ watchdogConfig,
16
31
  admit(socket, accept) {
17
32
  if (bootstrap)
18
33
  void acceptInternalConnection(socket, bootstrap.key, { protocolVersion: 1, serverVersion: VERSION })
@@ -24,9 +39,25 @@ export function internalHostTransport(configured, secret) {
24
39
  });
25
40
  else if (secret)
26
41
  authenticateSocket(socket, secret, () => accept());
42
+ else if (publicEndpoint)
43
+ accept(new PublicRpcConnection(socket, publicEndpoint).stream);
27
44
  else
28
45
  accept();
29
46
  },
47
+ async publish(onFailure) {
48
+ try {
49
+ await publicEndpoint?.publish();
50
+ }
51
+ catch (cause) {
52
+ endpoint?.log(cause instanceof Error ? cause.message : String(cause));
53
+ await onFailure();
54
+ }
55
+ },
56
+ async dispose() {
57
+ await publicEndpoint?.dispose().catch((cause) => {
58
+ endpoint?.log(`endpoint key cleanup failed: ${cause instanceof Error ? cause.message : String(cause)}`);
59
+ });
60
+ },
30
61
  };
31
62
  }
32
63
  //# sourceMappingURL=host-internal-transport.js.map
@@ -1,12 +1,15 @@
1
1
  import { type Server } from "node:net";
2
2
  import type { HostAdmission } from "./host-admission.ts";
3
3
  import type { ClientOccupancy } from "./host-client-occupancy.ts";
4
- import { type ListenerState, type SocketFileIdentity } from "./socket-ownership.ts";
4
+ import type { RpcPublicEndpoint } from "./rpc-public-key.ts";
5
+ import { type ListenerState } from "./socket-ownership.ts";
6
+ export { adoptPublicSocket } from "./socket-ownership.ts";
5
7
  export interface PublicProxyOptions {
6
8
  readonly internalSocket: string;
7
9
  readonly internalSecret?: Buffer;
8
10
  readonly internalKey?: Buffer;
9
11
  readonly publicSecret?: Buffer;
12
+ readonly publicEndpoint?: RpcPublicEndpoint;
10
13
  readonly clients: ClientOccupancy;
11
14
  /** Shared authority for stopping, protocol replies and new-session admission. */
12
15
  readonly admission: HostAdmission;
@@ -16,13 +19,6 @@ export interface PublicProxyOptions {
16
19
  readonly onAdmit?: () => void;
17
20
  }
18
21
  export declare function createPublicProxy(options: PublicProxyOptions): Server;
19
- /**
20
- * Takes the public name over with a rename, once - and only while - that name still refers to the
21
- * entry this handoff was decided against. A socket that changed underneath belongs to another
22
- * process now: replacing it would unlink an endpoint this generation cannot prove it owns, so the
23
- * successor aborts instead and leaves both the intruder's socket and its own bind entry alone.
24
- */
25
- export declare function adoptPublicSocket(bindSocket: string, publicSocket: string, expected: SocketFileIdentity | undefined): Promise<void>;
26
22
  /**
27
23
  * Reuses an existing valid secret - including one ensureHost() just wrote - and
28
24
  * creates one (with its parent directories, mode 0600) when it is missing or