@pinet/slack-bridge 0.2.2 → 0.2.6

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 (93) hide show
  1. package/README.md +316 -451
  2. package/dist/activity-log.d.ts +10 -3
  3. package/dist/agent-event-runtime.d.ts +0 -2
  4. package/dist/agent-event-runtime.js +0 -1
  5. package/dist/agent-prompt-guidance.d.ts +1 -6
  6. package/dist/agent-prompt-guidance.js +1 -6
  7. package/dist/broker/adapters/slack.d.ts +4 -1
  8. package/dist/broker/adapters/slack.js +16 -9
  9. package/dist/broker/client.d.ts +21 -4
  10. package/dist/broker/client.js +65 -25
  11. package/dist/broker/hibernation-activation-authority.d.ts +22 -0
  12. package/dist/broker/hibernation-activation-authority.js +51 -0
  13. package/dist/broker/hibernation-activation.d.ts +75 -0
  14. package/dist/broker/hibernation-activation.js +95 -0
  15. package/dist/broker/hibernation-command-router.d.ts +54 -0
  16. package/dist/broker/hibernation-command-router.js +124 -0
  17. package/dist/broker/hibernation-runtime-adapters.d.ts +79 -0
  18. package/dist/broker/hibernation-runtime-adapters.js +473 -0
  19. package/dist/broker/hibernation-runtime-helpers.d.ts +146 -0
  20. package/dist/broker/hibernation-runtime-helpers.js +243 -0
  21. package/dist/broker/index.d.ts +16 -1
  22. package/dist/broker/index.js +68 -10
  23. package/dist/broker/lock-conflict.d.ts +109 -0
  24. package/dist/broker/lock-conflict.js +409 -0
  25. package/dist/broker/socket-server.d.ts +26 -0
  26. package/dist/broker/socket-server.js +287 -4
  27. package/dist/broker-delivery.d.ts +1 -0
  28. package/dist/broker-delivery.js +10 -1
  29. package/dist/broker-runtime.d.ts +14 -2
  30. package/dist/broker-runtime.js +114 -3
  31. package/dist/canvases.d.ts +12 -2
  32. package/dist/canvases.js +1 -3
  33. package/dist/compaction-gate.d.ts +7 -0
  34. package/dist/compaction-gate.js +35 -0
  35. package/dist/deploy-manifest.d.ts +8 -2
  36. package/dist/deploy-manifest.js +9 -7
  37. package/dist/follower-runtime.d.ts +5 -2
  38. package/dist/follower-runtime.js +39 -5
  39. package/dist/git-metadata.d.ts +5 -9
  40. package/dist/git-metadata.js +23 -42
  41. package/dist/guardrails.js +5 -0
  42. package/dist/helpers.d.ts +77 -27
  43. package/dist/helpers.js +150 -39
  44. package/dist/hibernation-config.d.ts +13 -0
  45. package/dist/hibernation-config.js +14 -0
  46. package/dist/home-tab.d.ts +13 -2
  47. package/dist/imessage-tools.d.ts +4 -1
  48. package/dist/inbox-drain-runtime.d.ts +1 -0
  49. package/dist/inbox-drain-runtime.js +5 -1
  50. package/dist/index.js +341 -108
  51. package/dist/pinet-commands.d.ts +14 -1
  52. package/dist/pinet-commands.js +93 -22
  53. package/dist/pinet-mesh-ops.d.ts +6 -4
  54. package/dist/pinet-mesh-ops.js +9 -1
  55. package/dist/pinet-runtime-composition.d.ts +2 -1
  56. package/dist/pinet-runtime-composition.js +14 -3
  57. package/dist/pinet-session-formatting.d.ts +26 -2
  58. package/dist/pinet-session-formatting.js +2 -0
  59. package/dist/pinet-tools.d.ts +14 -0
  60. package/dist/pinet-tools.js +355 -50
  61. package/dist/prompts/broker/default.md +3 -3
  62. package/dist/prompts/broker/tmux.md +3 -3
  63. package/dist/runtime-agent-context.d.ts +3 -3
  64. package/dist/runtime-agent-context.js +12 -6
  65. package/dist/runtime-mode.js +4 -0
  66. package/dist/session-ui-runtime.js +3 -1
  67. package/dist/single-player-runtime.d.ts +1 -1
  68. package/dist/single-player-runtime.js +21 -5
  69. package/dist/slack-access.d.ts +10 -2
  70. package/dist/slack-access.js +64 -22
  71. package/dist/slack-block-kit.d.ts +44 -2
  72. package/dist/slack-block-kit.js +5 -5
  73. package/dist/slack-file-access.d.ts +12 -1
  74. package/dist/slack-file-access.js +4 -4
  75. package/dist/slack-message-context.d.ts +9 -2
  76. package/dist/slack-modals.d.ts +4 -1
  77. package/dist/slack-modals.js +5 -5
  78. package/dist/slack-pinet-runtime-adapter.d.ts +1 -0
  79. package/dist/slack-pinet-runtime-adapter.js +10 -0
  80. package/dist/slack-presence.d.ts +5 -4
  81. package/dist/slack-request-runtime.d.ts +3 -2
  82. package/dist/slack-scope-diagnostics.d.ts +27 -2
  83. package/dist/slack-socket-dedup.d.ts +8 -5
  84. package/dist/slack-tools.d.ts +10 -0
  85. package/dist/slack-tools.js +51 -64
  86. package/dist/slack-upload.d.ts +17 -1
  87. package/dist/subtree-broker-runtime.d.ts +83 -2
  88. package/dist/subtree-broker-runtime.js +774 -109
  89. package/dist/tool-output-limits.d.ts +11 -0
  90. package/dist/tool-output-limits.js +20 -0
  91. package/dist/tool-registration-runtime.d.ts +3 -0
  92. package/dist/tool-registration-runtime.js +27 -0
  93. package/package.json +8 -8
@@ -0,0 +1,409 @@
1
+ import * as net from "node:net";
2
+ import { inspectBrokerLock, } from "./leader.js";
3
+ import { getDefaultSocketPath } from "./paths.js";
4
+ import { RPC_METHOD_NOT_FOUND } from "./types.js";
5
+ // ─── Constants ───────────────────────────────────────────
6
+ export const DEFAULT_PROBE_TIMEOUT_MS = 2000;
7
+ export const DEFAULT_SHUTDOWN_RPC_TIMEOUT_MS = 5000;
8
+ export const DEFAULT_GRACEFUL_WAIT_MS = 8000;
9
+ export const DEFAULT_TERMINATE_WAIT_MS = 8000;
10
+ export const DEFAULT_REPLACE_POLL_INTERVAL_MS = 250;
11
+ class BrokerSocketFailure extends Error {
12
+ kind;
13
+ constructor(kind, message) {
14
+ super(message);
15
+ this.name = "BrokerSocketFailure";
16
+ this.kind = kind;
17
+ }
18
+ }
19
+ /**
20
+ * Minimal sequential JSON-RPC client used for lock-conflict diagnostics.
21
+ * Deliberately independent from the full follower client: no reconnects, no
22
+ * heartbeats — one bounded connection for probe/shutdown calls.
23
+ */
24
+ class OneShotBrokerRpcClient {
25
+ socket;
26
+ buffer = "";
27
+ nextId = 1;
28
+ waiter = null;
29
+ constructor(socket) {
30
+ this.socket = socket;
31
+ socket.on("data", (chunk) => {
32
+ this.buffer += chunk.toString("utf-8");
33
+ let newlineIndex = this.buffer.indexOf("\n");
34
+ while (newlineIndex !== -1) {
35
+ const line = this.buffer.slice(0, newlineIndex).trim();
36
+ this.buffer = this.buffer.slice(newlineIndex + 1);
37
+ if (line && this.waiter) {
38
+ let raw = null;
39
+ try {
40
+ const parsed = JSON.parse(line);
41
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
42
+ raw = parsed;
43
+ }
44
+ }
45
+ catch {
46
+ raw = null;
47
+ }
48
+ if (raw && raw.id === this.waiter.id) {
49
+ const rawError = raw.error;
50
+ const response = {
51
+ error: rawError && typeof rawError === "object" && typeof rawError.code === "number"
52
+ ? {
53
+ code: rawError.code,
54
+ message: typeof rawError.message === "string" ? rawError.message : "",
55
+ }
56
+ : null,
57
+ };
58
+ const { resolve } = this.waiter;
59
+ this.waiter = null;
60
+ resolve(response);
61
+ }
62
+ }
63
+ newlineIndex = this.buffer.indexOf("\n");
64
+ }
65
+ });
66
+ const failPending = (kind, message) => {
67
+ if (!this.waiter)
68
+ return;
69
+ const { reject } = this.waiter;
70
+ this.waiter = null;
71
+ reject(new BrokerSocketFailure(kind, message));
72
+ };
73
+ socket.on("error", (err) => failPending("unreachable", err.message));
74
+ socket.on("close", () => failPending("unresponsive", "Broker socket closed before responding."));
75
+ }
76
+ static connect(target, timeoutMs) {
77
+ return new Promise((resolve, reject) => {
78
+ const socket = target.type === "unix"
79
+ ? net.createConnection({ path: target.path })
80
+ : net.createConnection({ port: target.port, host: target.host });
81
+ const timer = setTimeout(() => {
82
+ socket.destroy();
83
+ reject(new BrokerSocketFailure("unresponsive", `Broker socket connect timed out after ${timeoutMs}ms.`));
84
+ }, timeoutMs);
85
+ timer.unref?.();
86
+ socket.once("connect", () => {
87
+ clearTimeout(timer);
88
+ resolve(new OneShotBrokerRpcClient(socket));
89
+ });
90
+ socket.once("error", (err) => {
91
+ clearTimeout(timer);
92
+ socket.destroy();
93
+ reject(new BrokerSocketFailure("unreachable", err.message));
94
+ });
95
+ });
96
+ }
97
+ call(method, params, timeoutMs) {
98
+ const id = this.nextId++;
99
+ return new Promise((resolve, reject) => {
100
+ const timer = setTimeout(() => {
101
+ if (this.waiter?.id === id)
102
+ this.waiter = null;
103
+ reject(new BrokerSocketFailure("unresponsive", `Broker did not respond to ${method} within ${timeoutMs}ms.`));
104
+ }, timeoutMs);
105
+ timer.unref?.();
106
+ this.waiter = {
107
+ id,
108
+ resolve: (response) => {
109
+ clearTimeout(timer);
110
+ resolve(response);
111
+ },
112
+ reject: (error) => {
113
+ clearTimeout(timer);
114
+ reject(error);
115
+ },
116
+ };
117
+ try {
118
+ this.socket.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
119
+ }
120
+ catch (err) {
121
+ clearTimeout(timer);
122
+ this.waiter = null;
123
+ reject(new BrokerSocketFailure("unreachable", err instanceof Error ? err.message : String(err)));
124
+ }
125
+ });
126
+ }
127
+ close() {
128
+ this.socket.destroy();
129
+ }
130
+ }
131
+ function resolveTarget(options) {
132
+ if (options.target)
133
+ return options.target;
134
+ return { type: "unix", path: options.socketPath ?? getDefaultSocketPath() };
135
+ }
136
+ function failureToProbeResult(err) {
137
+ return err instanceof BrokerSocketFailure && err.kind === "unreachable"
138
+ ? "unreachable"
139
+ : "unresponsive";
140
+ }
141
+ /**
142
+ * Bounded liveness probe for a broker socket. Any well-formed JSON-RPC
143
+ * response — including an auth error — proves the broker event loop is
144
+ * serving, so it classifies as healthy.
145
+ */
146
+ export async function probeBrokerSocket(options = {}) {
147
+ const target = resolveTarget(options);
148
+ const timeoutMs = options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
149
+ let client;
150
+ try {
151
+ client = await OneShotBrokerRpcClient.connect(target, timeoutMs);
152
+ }
153
+ catch (err) {
154
+ return failureToProbeResult(err instanceof Error ? err : new Error(String(err)));
155
+ }
156
+ try {
157
+ await client.call("auth", {}, timeoutMs);
158
+ return "healthy";
159
+ }
160
+ catch (err) {
161
+ return failureToProbeResult(err instanceof Error ? err : new Error(String(err)));
162
+ }
163
+ finally {
164
+ client.close();
165
+ }
166
+ }
167
+ /**
168
+ * Ask a running broker to shut down gracefully via the authenticated
169
+ * `admin.shutdown` RPC. Brokers that predate the RPC report `unsupported`.
170
+ */
171
+ export async function requestBrokerShutdown(options = {}) {
172
+ const target = resolveTarget(options);
173
+ const timeoutMs = options.timeoutMs ?? DEFAULT_SHUTDOWN_RPC_TIMEOUT_MS;
174
+ let client;
175
+ try {
176
+ client = await OneShotBrokerRpcClient.connect(target, timeoutMs);
177
+ }
178
+ catch (err) {
179
+ return err instanceof BrokerSocketFailure && err.kind === "unreachable"
180
+ ? "unreachable"
181
+ : "failed";
182
+ }
183
+ try {
184
+ const secret = options.meshSecret?.trim();
185
+ const authResponse = await client.call("auth", secret ? { secret } : {}, timeoutMs);
186
+ if (authResponse.error)
187
+ return "rejected";
188
+ const shutdownResponse = await client.call("admin.shutdown", {}, timeoutMs);
189
+ if (shutdownResponse.error) {
190
+ return shutdownResponse.error.code === RPC_METHOD_NOT_FOUND ? "unsupported" : "rejected";
191
+ }
192
+ return "accepted";
193
+ }
194
+ catch {
195
+ return "failed";
196
+ }
197
+ finally {
198
+ client.close();
199
+ }
200
+ }
201
+ /**
202
+ * Combine lock-owner inspection with a bounded socket probe to classify a
203
+ * leader-lock conflict.
204
+ */
205
+ export async function classifyBrokerLockConflict(options = {}) {
206
+ const inspection = inspectBrokerLock(options.lockPath, options.probes ?? {});
207
+ if (inspection.state !== "alive") {
208
+ return { kind: "reclaimable", inspection, probe: null };
209
+ }
210
+ const target = resolveTarget(options);
211
+ const timeoutMs = options.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
212
+ const probe = options.probeSocket
213
+ ? await options.probeSocket(target, timeoutMs)
214
+ : await probeBrokerSocket({ target, timeoutMs });
215
+ return {
216
+ kind: "conflict",
217
+ classification: probe === "healthy" ? "active-broker" : "unresponsive-broker",
218
+ owner: inspection.owner,
219
+ probe,
220
+ };
221
+ }
222
+ export function formatBrokerLockConflictMessage(input) {
223
+ const owner = input.owner;
224
+ const ownerBits = owner
225
+ ? ` (pid ${owner.pid}${owner.createdAt ? `, lock created ${owner.createdAt}` : ""})`
226
+ : "";
227
+ if (input.classification === "active-broker") {
228
+ return (`Another pinet broker is already running${ownerBits} and its socket is responding. ` +
229
+ `Use /pinet follow to join it, or /pinet start replace to take over.`);
230
+ }
231
+ const socketState = input.probe === "unreachable" ? "not reachable" : "not responding";
232
+ return (`Another process holds the pinet broker lock${ownerBits} but the broker socket is ` +
233
+ `${socketState} — the broker looks stranded. Use /pinet start replace to recover.`);
234
+ }
235
+ /**
236
+ * Thrown by `startBroker` when the leader lock is held by another live
237
+ * process, carrying enough context for callers to present a per-state
238
+ * recovery path instead of a generic failure.
239
+ */
240
+ export class BrokerLockConflictError extends Error {
241
+ classification;
242
+ owner;
243
+ probe;
244
+ constructor(input) {
245
+ super(formatBrokerLockConflictMessage(input));
246
+ this.name = "BrokerLockConflictError";
247
+ this.classification = input.classification;
248
+ this.owner = input.owner;
249
+ this.probe = input.probe;
250
+ }
251
+ }
252
+ /**
253
+ * Conservatively replace the current broker lock owner:
254
+ *
255
+ * 1. No live owner → nothing to do (a normal start reclaims stale state).
256
+ * 2. Live owner → request graceful `admin.shutdown` and wait (bounded).
257
+ * 3. Fallback → re-verify the owner fence (pid + process start time +
258
+ * instance id), SIGTERM the verified owner, and wait (bounded).
259
+ * 4. Never SIGKILL; abort if the owner identity changes mid-flight.
260
+ */
261
+ export async function replaceBrokerOwner(options = {}) {
262
+ const steps = [];
263
+ const probes = options.probes ?? {};
264
+ const inspect = () => inspectBrokerLock(options.lockPath, probes);
265
+ // Deliberately not unref'd: the poll loop must keep the event loop alive
266
+ // until replacement resolves (caught by the cross-process E2E run).
267
+ const sleep = options.sleep ??
268
+ ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
269
+ const kill = options.kill ??
270
+ ((pid, signal) => {
271
+ process.kill(pid, signal);
272
+ });
273
+ const requestShutdown = options.requestShutdown ?? requestBrokerShutdown;
274
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_REPLACE_POLL_INTERVAL_MS;
275
+ const gracefulWaitMs = options.gracefulWaitMs ?? DEFAULT_GRACEFUL_WAIT_MS;
276
+ const terminateWaitMs = options.terminateWaitMs ?? DEFAULT_TERMINATE_WAIT_MS;
277
+ const initial = inspect();
278
+ if (initial.state !== "alive") {
279
+ steps.push(`Lock state: ${initial.state} — nothing to replace; a normal start reclaims it.`);
280
+ return { outcome: "no-conflict", owner: initial.owner ?? null, steps, error: null };
281
+ }
282
+ const owner = initial.owner;
283
+ if (owner.pid === process.pid) {
284
+ // Never signal ourselves: a same-process conflict means this session
285
+ // already hosts the broker (or another runtime within it does).
286
+ steps.push(`Lock is held by this session's own process (pid ${owner.pid}).`);
287
+ return {
288
+ outcome: "failed",
289
+ owner,
290
+ steps,
291
+ error: "The broker lock is held by this session's own process — nothing to replace. " +
292
+ "Use /pinet start to reload the current broker runtime instead.",
293
+ };
294
+ }
295
+ steps.push(`Lock held by live pid ${owner.pid}${owner.legacy ? " (legacy lock format — weaker identity fence)" : ""}.`);
296
+ const sameOwner = (current) => current.state === "alive" &&
297
+ current.owner.pid === owner.pid &&
298
+ current.owner.instanceId === owner.instanceId &&
299
+ current.owner.processStartTime === owner.processStartTime;
300
+ const ownerChangedResult = () => {
301
+ steps.push("Lock owner changed during replacement — aborting.");
302
+ return {
303
+ outcome: "owner-changed",
304
+ owner,
305
+ steps,
306
+ error: "The broker lock changed owners during replacement — another session may have recovered it. Check /pinet status and retry if needed.",
307
+ };
308
+ };
309
+ const waitForRelease = async (waitMs) => {
310
+ const deadline = Date.now() + waitMs;
311
+ for (;;) {
312
+ const current = inspect();
313
+ if (current.state !== "alive")
314
+ return "released";
315
+ if (!sameOwner(current))
316
+ return "owner-changed";
317
+ if (Date.now() >= deadline)
318
+ return "held";
319
+ await sleep(pollIntervalMs);
320
+ }
321
+ };
322
+ // ── Step 1: graceful shutdown over the socket ──
323
+ const shutdownResult = await requestShutdown({
324
+ ...(options.target ? { target: options.target } : {}),
325
+ ...(options.socketPath ? { socketPath: options.socketPath } : {}),
326
+ meshSecret: options.meshSecret ?? null,
327
+ timeoutMs: options.shutdownRpcTimeoutMs ?? DEFAULT_SHUTDOWN_RPC_TIMEOUT_MS,
328
+ });
329
+ steps.push(`Graceful shutdown request: ${shutdownResult}.`);
330
+ if (shutdownResult === "accepted") {
331
+ const wait = await waitForRelease(gracefulWaitMs);
332
+ if (wait === "released") {
333
+ steps.push("Broker released the lock after graceful shutdown.");
334
+ return { outcome: "replaced-graceful", owner, steps, error: null };
335
+ }
336
+ if (wait === "owner-changed")
337
+ return ownerChangedResult();
338
+ steps.push("Broker accepted shutdown but did not release the lock in time.");
339
+ }
340
+ if (shutdownResult === "rejected") {
341
+ // The broker is responsive and refused the request (typically a mesh
342
+ // secret mismatch). A responsive broker is not stranded — do not
343
+ // escalate to signals.
344
+ return {
345
+ outcome: "failed",
346
+ owner,
347
+ steps,
348
+ error: "The running broker rejected the shutdown request (often a mesh secret mismatch). " +
349
+ "It is responsive, so it was not terminated. Fix the mesh secret configuration, " +
350
+ "or stop that broker from its own session, then retry.",
351
+ };
352
+ }
353
+ // ── Step 2: fenced termination fallback ──
354
+ if (!owner.processStartTime) {
355
+ // Legacy locks (and structured locks with an unknown start time) carry no
356
+ // PID-reuse fence, so an automatic SIGTERM could hit an unrelated process
357
+ // that reused the PID. Require a manual, human-verified step instead.
358
+ steps.push("Lock owner has no recorded process start identity — refusing automatic termination.");
359
+ return {
360
+ outcome: "failed",
361
+ owner,
362
+ steps,
363
+ error: `The lock owner (pid ${owner.pid}) predates identity fencing, so automatic termination ` +
364
+ `cannot verify it is still the original broker. Inspect it manually (ps -p ${owner.pid}), ` +
365
+ `terminate it yourself if it is truly the stranded broker, then run /pinet start.`,
366
+ };
367
+ }
368
+ const preTerminate = inspect();
369
+ if (preTerminate.state !== "alive") {
370
+ steps.push("Lock was released before termination was needed.");
371
+ return {
372
+ outcome: shutdownResult === "accepted" ? "replaced-graceful" : "no-conflict",
373
+ owner,
374
+ steps,
375
+ error: null,
376
+ };
377
+ }
378
+ if (!sameOwner(preTerminate))
379
+ return ownerChangedResult();
380
+ // The fence re-check above and the signal below are not atomic: the
381
+ // verified owner could exit and its PID be reused in between. Closing that
382
+ // window needs a kernel handle bound to process identity (e.g. Linux
383
+ // pidfd_send_signal), which Node does not expose without native code — the
384
+ // immediately-preceding pid + start-time + instanceId fence keeps the
385
+ // residual window negligible.
386
+ steps.push(`Sending SIGTERM to verified lock owner pid ${owner.pid}.`);
387
+ try {
388
+ kill(owner.pid, "SIGTERM");
389
+ }
390
+ catch {
391
+ // ESRCH etc. — the process may have just exited; the release wait decides.
392
+ steps.push(`Signal delivery to pid ${owner.pid} failed — it may have already exited.`);
393
+ }
394
+ const wait = await waitForRelease(terminateWaitMs);
395
+ if (wait === "released") {
396
+ steps.push("Broker released the lock after SIGTERM.");
397
+ return { outcome: "replaced-terminated", owner, steps, error: null };
398
+ }
399
+ if (wait === "owner-changed")
400
+ return ownerChangedResult();
401
+ return {
402
+ outcome: "failed",
403
+ owner,
404
+ steps,
405
+ error: `Broker pid ${owner.pid} is still holding the lock after SIGTERM. ` +
406
+ `Not escalating to SIGKILL automatically — inspect the process (ps -p ${owner.pid}) ` +
407
+ `and terminate it manually if it is truly stranded, then run /pinet start.`,
408
+ };
409
+ }
@@ -47,6 +47,7 @@ export declare class BrokerSocketServer {
47
47
  private agentStatusChangeCallback;
48
48
  private outboundMessageAdapters;
49
49
  private agentRegistrationResolver;
50
+ private adminShutdownHandler;
50
51
  constructor(db: BrokerDB, target?: ListenTarget | string, options?: BrokerSocketServerOptions);
51
52
  start(): Promise<void>;
52
53
  stop(): Promise<void>;
@@ -75,6 +76,15 @@ export declare class BrokerSocketServer {
75
76
  */
76
77
  onAgentStatusChange(cb: AgentStatusChangeCallback): void;
77
78
  setAgentRegistrationResolver(resolver: AgentRegistrationResolver | null): void;
79
+ disconnectAgentConnections(agentId: string): void;
80
+ /**
81
+ * Register a handler invoked when an authenticated local client requests a
82
+ * graceful broker shutdown via the `admin.shutdown` RPC (used by
83
+ * `/pinet start replace` to take over a broker whose controlling session
84
+ * was lost). When no handler is wired, the RPC reports method-not-found so
85
+ * callers treat the broker as not supporting remote shutdown.
86
+ */
87
+ setAdminShutdownHandler(handler: (() => void | Promise<void>) | null): void;
78
88
  setOutboundMessageAdapters(adapters: ReadonlyArray<Pick<MessageAdapter, "name" | "send" | "invokeCapability">>): void;
79
89
  private startPruning;
80
90
  private stopPruning;
@@ -88,6 +98,14 @@ export declare class BrokerSocketServer {
88
98
  private send;
89
99
  private handleRequest;
90
100
  private handleAuth;
101
+ /**
102
+ * Enforce the accepted-generation wake fence for durable hibernation
103
+ * identities. Returns a rejection response when the fence fails; on success
104
+ * for a hibernation identity it accepts the generation and binds this
105
+ * connection to the revived agent id. Ordinary registration returns no
106
+ * rejection and leaves state untouched.
107
+ */
108
+ private enforceWakeFence;
91
109
  private handleRegister;
92
110
  private handleUnregister;
93
111
  private handleHeartbeat;
@@ -114,8 +132,16 @@ export declare class BrokerSocketServer {
114
132
  private handlePortLeaseExpire;
115
133
  private handleScheduleCreate;
116
134
  private handleStatusUpdate;
135
+ /**
136
+ * Graceful remote shutdown. Requires authentication (enforced by the
137
+ * generic auth gate). Responds first, then invokes the wired handler on the
138
+ * next tick so the requester receives the acknowledgement before the broker
139
+ * tears down its socket.
140
+ */
141
+ private handleAdminShutdown;
117
142
  private handleAdapterCapability;
118
143
  private handleLegacySlackProxy;
119
144
  private dispatchAdapterCapability;
145
+ private checkAdapterCapabilityThreadOwnership;
120
146
  private applyAdapterCapabilityEffects;
121
147
  }