botmux 2.56.0 → 2.57.1

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.
@@ -11,6 +11,13 @@ export interface TerminalProxyOptions {
11
11
  host?: string;
12
12
  /** Resolve a sessionId to its live worker HTTP port (undefined if not running). */
13
13
  resolvePort: (sessionId: string) => number | undefined;
14
+ /**
15
+ * Optional on-demand wake: when `resolvePort` finds no live worker, re-fork it
16
+ * (re-attaching the surviving tmux/zellij pane) and resolve once its port is
17
+ * up. Lets terminals open after a quiet restart without first messaging the
18
+ * session. Returns undefined when there's nothing to wake. Slow path only.
19
+ */
20
+ ensureWorkerPort?: (sessionId: string) => Promise<number | undefined>;
14
21
  /** Max upward port probes when `port` is taken (EADDRINUSE). Default 20; 0 disables. */
15
22
  maxProbe?: number;
16
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-proxy.d.ts","sourceRoot":"","sources":["../../src/core/terminal-proxy.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUtF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAoH3F"}
1
+ {"version":3,"file":"terminal-proxy.d.ts","sourceRoot":"","sources":["../../src/core/terminal-proxy.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACtE,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAUtF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAkI3F"}
@@ -22,6 +22,21 @@ export function parseTarget(rawUrl) {
22
22
  }
23
23
  export function startTerminalProxy(opts) {
24
24
  const host = opts.host ?? '0.0.0.0';
25
+ // Fast sync lookup; fall back to the on-demand wake (slow path) only when no
26
+ // live worker is registered. Errors in the wake collapse to "not serveable".
27
+ const resolvePortMaybeWake = async (sessionId) => {
28
+ const live = opts.resolvePort(sessionId);
29
+ if (live)
30
+ return live;
31
+ if (!opts.ensureWorkerPort)
32
+ return undefined;
33
+ try {
34
+ return await opts.ensureWorkerPort(sessionId);
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ };
25
40
  const server = createServer((req, res) => {
26
41
  const parsed = parseTarget(req.url ?? '');
27
42
  if (!parsed) {
@@ -29,78 +44,84 @@ export function startTerminalProxy(opts) {
29
44
  res.end('not found');
30
45
  return;
31
46
  }
32
- const port = opts.resolvePort(parsed.sessionId);
33
- if (!port) {
34
- res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
35
- res.end('session not running');
36
- return;
37
- }
38
- const upstream = httpRequest({ host: '127.0.0.1', port, method: req.method, path: parsed.rest, headers: req.headers }, (up) => {
39
- res.writeHead(up.statusCode ?? 502, up.headers);
40
- up.pipe(res);
41
- });
42
- upstream.on('error', () => {
47
+ resolvePortMaybeWake(parsed.sessionId).then((port) => {
48
+ if (!port) {
49
+ res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
50
+ res.end('session not running');
51
+ return;
52
+ }
53
+ const upstream = httpRequest({ host: '127.0.0.1', port, method: req.method, path: parsed.rest, headers: req.headers }, (up) => {
54
+ res.writeHead(up.statusCode ?? 502, up.headers);
55
+ up.pipe(res);
56
+ });
57
+ upstream.on('error', () => {
58
+ if (!res.headersSent)
59
+ res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
60
+ res.end('proxy error');
61
+ });
62
+ req.pipe(upstream);
63
+ }).catch(() => {
43
64
  if (!res.headersSent)
44
65
  res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
45
66
  res.end('proxy error');
46
67
  });
47
- req.pipe(upstream);
48
68
  });
49
69
  server.on('upgrade', (req, clientSocket, head) => {
50
70
  const parsed = parseTarget(req.url ?? '');
51
71
  if (!parsed)
52
72
  return clientSocket.destroy();
53
- const port = opts.resolvePort(parsed.sessionId);
54
- if (!port)
55
- return clientSocket.destroy();
56
- const upstream = httpRequest({
57
- host: '127.0.0.1',
58
- port,
59
- method: req.method,
60
- path: parsed.rest,
61
- headers: req.headers,
62
- });
63
- upstream.on('upgrade', (upRes, upstreamSocket, upstreamHead) => {
64
- // rawHeaders is a flat [k, v, k, v, ...] list — preserves duplicates/casing.
65
- const lines = [`HTTP/1.1 ${upRes.statusCode} ${upRes.statusMessage}`];
66
- const rh = upRes.rawHeaders;
67
- for (let i = 0; i + 1 < rh.length; i += 2)
68
- lines.push(`${rh[i]}: ${rh[i + 1]}`);
69
- lines.push('', '');
70
- clientSocket.write(lines.join('\r\n'));
71
- if (upstreamHead?.length)
72
- clientSocket.write(upstreamHead);
73
- if (head?.length)
74
- upstreamSocket.write(head);
75
- upstreamSocket.pipe(clientSocket);
76
- clientSocket.pipe(upstreamSocket);
77
- const cleanup = () => { upstreamSocket.destroy(); clientSocket.destroy(); };
78
- upstreamSocket.on('error', cleanup);
79
- clientSocket.on('error', cleanup);
80
- upstreamSocket.on('close', () => clientSocket.destroy());
81
- clientSocket.on('close', () => upstreamSocket.destroy());
82
- });
83
- // Upstream answered without upgrading (e.g. worker rejected the handshake).
84
- // Relay the response and close the client socket so it doesn't hang. The
85
- // body arrives already de-chunked, so drop framing headers and let the
86
- // socket close delimit the response (HTTP/1.1 connection-close framing).
87
- upstream.on('response', (upRes) => {
88
- const lines = [`HTTP/1.1 ${upRes.statusCode} ${upRes.statusMessage}`, 'connection: close'];
89
- const rh = upRes.rawHeaders;
90
- for (let i = 0; i + 1 < rh.length; i += 2) {
91
- const name = rh[i].toLowerCase();
92
- if (name === 'transfer-encoding' || name === 'content-length' || name === 'connection')
93
- continue;
94
- lines.push(`${rh[i]}: ${rh[i + 1]}`);
95
- }
96
- lines.push('', '');
97
- clientSocket.write(lines.join('\r\n'));
98
- upRes.on('data', (chunk) => clientSocket.write(chunk));
99
- upRes.on('end', () => { clientSocket.end(); upRes.socket?.destroy(); });
100
- upRes.on('error', () => clientSocket.destroy());
101
- });
102
- upstream.on('error', () => clientSocket.destroy());
103
- upstream.end();
73
+ resolvePortMaybeWake(parsed.sessionId).then((port) => {
74
+ if (!port)
75
+ return clientSocket.destroy();
76
+ const upstream = httpRequest({
77
+ host: '127.0.0.1',
78
+ port,
79
+ method: req.method,
80
+ path: parsed.rest,
81
+ headers: req.headers,
82
+ });
83
+ upstream.on('upgrade', (upRes, upstreamSocket, upstreamHead) => {
84
+ // rawHeaders is a flat [k, v, k, v, ...] list — preserves duplicates/casing.
85
+ const lines = [`HTTP/1.1 ${upRes.statusCode} ${upRes.statusMessage}`];
86
+ const rh = upRes.rawHeaders;
87
+ for (let i = 0; i + 1 < rh.length; i += 2)
88
+ lines.push(`${rh[i]}: ${rh[i + 1]}`);
89
+ lines.push('', '');
90
+ clientSocket.write(lines.join('\r\n'));
91
+ if (upstreamHead?.length)
92
+ clientSocket.write(upstreamHead);
93
+ if (head?.length)
94
+ upstreamSocket.write(head);
95
+ upstreamSocket.pipe(clientSocket);
96
+ clientSocket.pipe(upstreamSocket);
97
+ const cleanup = () => { upstreamSocket.destroy(); clientSocket.destroy(); };
98
+ upstreamSocket.on('error', cleanup);
99
+ clientSocket.on('error', cleanup);
100
+ upstreamSocket.on('close', () => clientSocket.destroy());
101
+ clientSocket.on('close', () => upstreamSocket.destroy());
102
+ });
103
+ // Upstream answered without upgrading (e.g. worker rejected the handshake).
104
+ // Relay the response and close the client socket so it doesn't hang. The
105
+ // body arrives already de-chunked, so drop framing headers and let the
106
+ // socket close delimit the response (HTTP/1.1 connection-close framing).
107
+ upstream.on('response', (upRes) => {
108
+ const lines = [`HTTP/1.1 ${upRes.statusCode} ${upRes.statusMessage}`, 'connection: close'];
109
+ const rh = upRes.rawHeaders;
110
+ for (let i = 0; i + 1 < rh.length; i += 2) {
111
+ const name = rh[i].toLowerCase();
112
+ if (name === 'transfer-encoding' || name === 'content-length' || name === 'connection')
113
+ continue;
114
+ lines.push(`${rh[i]}: ${rh[i + 1]}`);
115
+ }
116
+ lines.push('', '');
117
+ clientSocket.write(lines.join('\r\n'));
118
+ upRes.on('data', (chunk) => clientSocket.write(chunk));
119
+ upRes.on('end', () => { clientSocket.end(); upRes.socket?.destroy(); });
120
+ upRes.on('error', () => clientSocket.destroy());
121
+ });
122
+ upstream.on('error', () => clientSocket.destroy());
123
+ upstream.end();
124
+ }).catch(() => clientSocket.destroy());
104
125
  });
105
126
  // When the preferred port is taken, probe upward to the next free port so the
106
127
  // proxy always comes up on a single stable-ish port (the daemon advertises the
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-proxy.js","sourceRoot":"","sources":["../../src/core/terminal-proxy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"terminal-proxy.js","sourceRoot":"","sources":["../../src/core/terminal-proxy.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AA0BA,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAwErD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAiB,MAAM,wBAAwB,CAAC;AAiTpF,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAGpB;AAyJD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,sBAAsB,GAAG,oBAAoB,CA0C5G;AA4pED,wBAAsB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6TlE"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AA0BA,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAyErD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAiB,MAAM,wBAAwB,CAAC;AAiTpF,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAGpB;AAyJD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,sBAAsB,GAAG,oBAAoB,CA0C5G;AA2sED,wBAAsB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqUlE"}
package/dist/daemon.js CHANGED
@@ -40,7 +40,7 @@ import { findInheritablePeer } from './core/inherit-peer.js';
40
40
  import { isCallbackUrl, handleCallbackUrl } from './utils/user-token.js';
41
41
  import { consumeQuota, removeChatGrant, removeGlobalGrant } from './services/grant-store.js';
42
42
  import { abortCharge, commitCharge, beginCharge } from './services/quota-dedup.js';
43
- import { getSessionWorkingDir, getProjectScanDirs, expandHome, downloadResources, formatAttachmentsHint, buildNewTopicPrompt, buildFollowUpContent, buildBridgeInputContent, buildReforkPrompt, getAvailableBots, restoreActiveSessions, executeScheduledTask, persistStreamCardState, rememberLastCliInput, } from './core/session-manager.js';
43
+ import { getSessionWorkingDir, getProjectScanDirs, expandHome, downloadResources, formatAttachmentsHint, buildNewTopicPrompt, buildFollowUpContent, buildBridgeInputContent, buildReforkPrompt, getAvailableBots, restoreActiveSessions, executeScheduledTask, persistStreamCardState, rememberLastCliInput, ensureTerminalWorkerPort, } from './core/session-manager.js';
44
44
  import { handleCardAction } from './im/lark/card-handler.js';
45
45
  import { executeWorkflowCommand, parseWorkflowCommand, resolveBotSnapshot, } from './im/lark/workflow-slash-command.js';
46
46
  import { workflowRunDetailUrl } from './im/lark/workflow-cards.js';
@@ -49,7 +49,7 @@ import { EventLog as WorkflowEventLog } from './workflows/events/append.js';
49
49
  import { replay as replayWorkflow } from './workflows/events/replay.js';
50
50
  import { isBotMentioned, probeBotOpenId, startLarkEventDispatcher, writeBotInfoFile, canOperate, evaluateTalk, grantCommandRestriction, isKnownPeerBot, checkRequiredScopes } from './im/lark/event-dispatcher.js';
51
51
  import { learnFromMentions, resolveSender, flushIdentityCacheSync } from './im/lark/identity-cache.js';
52
- import { renderSenderTag } from './core/session-manager.js';
52
+ import { renderBufferedSenderBlock } from './core/session-manager.js';
53
53
  import { markSessionActivity } from './core/session-activity.js';
54
54
  import { WorkflowEventWatcher, handleWorkflowFanoutEvent } from './workflows/fanout.js';
55
55
  import { runLoop } from './workflows/loop.js';
@@ -1113,6 +1113,29 @@ const commandDeps = {
1113
1113
  getActiveCount,
1114
1114
  lastRepoScan,
1115
1115
  };
1116
+ /**
1117
+ * Fire a session-less daemon command (`/group`, `/g`) WITHOUT blocking the Lark
1118
+ * event ACK on its slow work — the fast-ACK path.
1119
+ *
1120
+ * The Lark WS SDK sends the event response frame (a content-free `{code:200}`)
1121
+ * only AFTER our `im.message.receive_v1` handler resolves: node-sdk's
1122
+ * `handleEventData` does `await eventDispatcher.invoke(...)` and only then sends
1123
+ * the ack. `/group` runs several seconds of serial Lark API calls (create chat →
1124
+ * add bots → transfer owner → fetch share link → reply); awaiting that blocks the
1125
+ * ack past Feishu's redelivery window, so Feishu redelivers the same message_id.
1126
+ * Because these commands are session-less (SESSIONLESS_DAEMON_COMMANDS), no
1127
+ * session record exists to dedupe the redelivery against — so it builds a SECOND
1128
+ * group. (Observed: one `/g` created two identical groups, the duplicate ~19s
1129
+ * after the first.)
1130
+ *
1131
+ * The ack needs nothing from the command's output, so run it detached and let the
1132
+ * handler return immediately → the ack fires now → no redelivery. handleCommand
1133
+ * wraps its whole body in try/catch and replies failures to the chat; the `.catch`
1134
+ * here is only a last-resort backstop for anything that somehow escapes.
1135
+ */
1136
+ function fireSessionlessCommandDetached(cmd, anchor, message, larkAppId) {
1137
+ void handleCommand(cmd, anchor, message, commandDeps, larkAppId).catch((err) => logger.error(`[sessionless ${cmd}] ${anchor.substring(0, 12)} failed: ${err?.message ?? err}`));
1138
+ }
1116
1139
  // Dependencies passed to card-handler
1117
1140
  const cardDeps = {
1118
1141
  activeSessions,
@@ -1581,7 +1604,10 @@ async function handleNewTopic(data, ctx) {
1581
1604
  // without a session; pass chatId on the message so the handler can reach
1582
1605
  // the chat roster (it normally reads it from the active session's ds).
1583
1606
  if (SESSIONLESS_DAEMON_COMMANDS.has(cmd)) {
1584
- await handleCommand(cmd, anchor, { ...parsed, content: commandContent, chatId }, commandDeps, larkAppId);
1607
+ // Fast-ACK: run detached so the WS event ack isn't blocked on /group's
1608
+ // slow Lark API work → no Feishu redelivery → no duplicate group.
1609
+ // See fireSessionlessCommandDetached.
1610
+ fireSessionlessCommandDetached(cmd, anchor, { ...parsed, content: commandContent, chatId }, larkAppId);
1585
1611
  return;
1586
1612
  }
1587
1613
  // Same rootMessageId reasoning as below in the main spawn path:
@@ -2148,7 +2174,13 @@ async function handleThreadReply(data, ctx) {
2148
2174
  }
2149
2175
  // Pass mention-stripped content so /command argument parsing works.
2150
2176
  // chatId lets session-less handlers (e.g. /group) reach the chat roster.
2151
- await handleCommand(cmd, anchor, { ...parsed, content: commandContent, chatId: threadChatId }, commandDeps, larkAppId);
2177
+ const cmdMessage = { ...parsed, content: commandContent, chatId: threadChatId };
2178
+ if (SESSIONLESS_DAEMON_COMMANDS.has(cmd)) {
2179
+ // Fast-ACK for /group invoked mid-thread. See fireSessionlessCommandDetached.
2180
+ fireSessionlessCommandDetached(cmd, anchor, cmdMessage, larkAppId);
2181
+ return;
2182
+ }
2183
+ await handleCommand(cmd, anchor, cmdMessage, commandDeps, larkAppId);
2152
2184
  return;
2153
2185
  }
2154
2186
  }
@@ -2225,9 +2257,14 @@ async function handleThreadReply(data, ctx) {
2225
2257
  // tell multi-user buffered messages apart after repo selection unlocks.
2226
2258
  const followUpSender = await getThreadSender();
2227
2259
  if (followUpSender?.openId && followUpSender.openId !== ds.pendingSender?.openId) {
2228
- const followUpSenderTag = renderSenderTag(followUpSender);
2229
- if (followUpSenderTag)
2230
- enriched = `${followUpSenderTag}\n${enriched}`;
2260
+ // This buffer folds into the opening <user_message> after repo selection,
2261
+ // so pair the foreign sender tag with the cursor anti-echo note: without
2262
+ // the adjacent note a cursor session sees an inline ou_xxx:name with no
2263
+ // guard (the builder's own note only covers ds.pendingSender's top-level
2264
+ // tag, and is absent entirely when pendingSender is undefined).
2265
+ const followUpSenderBlock = renderBufferedSenderBlock(followUpSender, getBot(larkAppId).config.cliId, localeForBot(larkAppId));
2266
+ if (followUpSenderBlock)
2267
+ enriched = `${followUpSenderBlock}\n${enriched}`;
2231
2268
  }
2232
2269
  if (!ds.pendingFollowUps)
2233
2270
  ds.pendingFollowUps = [];
@@ -2560,6 +2597,15 @@ export async function startDaemon(botIndex) {
2560
2597
  }
2561
2598
  return undefined;
2562
2599
  },
2600
+ // Quiet-restart leaves sessions registered but worker-less until messaged.
2601
+ // Wake the worker on terminal access so links open without a manual ping.
2602
+ ensureWorkerPort: async (sessionId) => {
2603
+ for (const ds of activeSessions.values()) {
2604
+ if (ds.session.sessionId === sessionId)
2605
+ return ensureTerminalWorkerPort(ds);
2606
+ }
2607
+ return undefined;
2608
+ },
2563
2609
  });
2564
2610
  // Only mark the proxy live after a successful bind — buildTerminalUrl then
2565
2611
  // falls back to the worker's own port so links stay reachable if the port