botmux 2.52.0 → 2.54.0

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 (150) hide show
  1. package/README.en.md +22 -269
  2. package/README.md +21 -300
  3. package/dist/adapters/backend/session-backend-selector.d.ts +5 -1
  4. package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
  5. package/dist/adapters/backend/session-backend-selector.js +15 -1
  6. package/dist/adapters/backend/session-backend-selector.js.map +1 -1
  7. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  8. package/dist/adapters/backend/tmux-backend.js +3 -0
  9. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  10. package/dist/adapters/backend/types.d.ts +22 -0
  11. package/dist/adapters/backend/types.d.ts.map +1 -1
  12. package/dist/adapters/backend/types.js +7 -1
  13. package/dist/adapters/backend/types.js.map +1 -1
  14. package/dist/adapters/backend/zellij-backend.d.ts +132 -0
  15. package/dist/adapters/backend/zellij-backend.d.ts.map +1 -0
  16. package/dist/adapters/backend/zellij-backend.js +375 -0
  17. package/dist/adapters/backend/zellij-backend.js.map +1 -0
  18. package/dist/adapters/backend/zellij-observe-backend.d.ts +62 -0
  19. package/dist/adapters/backend/zellij-observe-backend.d.ts.map +1 -0
  20. package/dist/adapters/backend/zellij-observe-backend.js +218 -0
  21. package/dist/adapters/backend/zellij-observe-backend.js.map +1 -0
  22. package/dist/adapters/cli/claude-code.d.ts +39 -5
  23. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  24. package/dist/adapters/cli/claude-code.js +53 -31
  25. package/dist/adapters/cli/claude-code.js.map +1 -1
  26. package/dist/adapters/cli/registry.d.ts +2 -1
  27. package/dist/adapters/cli/registry.d.ts.map +1 -1
  28. package/dist/adapters/cli/registry.js +3 -1
  29. package/dist/adapters/cli/registry.js.map +1 -1
  30. package/dist/adapters/cli/seed.d.ts +29 -0
  31. package/dist/adapters/cli/seed.d.ts.map +1 -0
  32. package/dist/adapters/cli/seed.js +63 -0
  33. package/dist/adapters/cli/seed.js.map +1 -0
  34. package/dist/adapters/cli/types.d.ts +17 -1
  35. package/dist/adapters/cli/types.d.ts.map +1 -1
  36. package/dist/bot-registry.d.ts +1 -1
  37. package/dist/bot-registry.d.ts.map +1 -1
  38. package/dist/cli.d.ts.map +1 -1
  39. package/dist/cli.js +79 -49
  40. package/dist/cli.js.map +1 -1
  41. package/dist/config.d.ts +7 -1
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +8 -0
  44. package/dist/config.js.map +1 -1
  45. package/dist/core/ask-hook/registry.d.ts.map +1 -1
  46. package/dist/core/ask-hook/registry.js +4 -0
  47. package/dist/core/ask-hook/registry.js.map +1 -1
  48. package/dist/core/command-handler.d.ts +4 -1
  49. package/dist/core/command-handler.d.ts.map +1 -1
  50. package/dist/core/command-handler.js +100 -8
  51. package/dist/core/command-handler.js.map +1 -1
  52. package/dist/core/dispatch.d.ts +33 -0
  53. package/dist/core/dispatch.d.ts.map +1 -1
  54. package/dist/core/dispatch.js +26 -0
  55. package/dist/core/dispatch.js.map +1 -1
  56. package/dist/core/pending-response.d.ts +31 -0
  57. package/dist/core/pending-response.d.ts.map +1 -0
  58. package/dist/core/pending-response.js +87 -0
  59. package/dist/core/pending-response.js.map +1 -0
  60. package/dist/core/session-discovery.d.ts +13 -4
  61. package/dist/core/session-discovery.d.ts.map +1 -1
  62. package/dist/core/session-discovery.js +5 -5
  63. package/dist/core/session-discovery.js.map +1 -1
  64. package/dist/core/session-manager.d.ts +10 -0
  65. package/dist/core/session-manager.d.ts.map +1 -1
  66. package/dist/core/session-manager.js +59 -19
  67. package/dist/core/session-manager.js.map +1 -1
  68. package/dist/core/types.d.ts +8 -2
  69. package/dist/core/types.d.ts.map +1 -1
  70. package/dist/core/types.js.map +1 -1
  71. package/dist/core/worker-pool.d.ts +2 -2
  72. package/dist/core/worker-pool.d.ts.map +1 -1
  73. package/dist/core/worker-pool.js +89 -15
  74. package/dist/core/worker-pool.js.map +1 -1
  75. package/dist/core/zellij-adopt-discovery.d.ts +28 -0
  76. package/dist/core/zellij-adopt-discovery.d.ts.map +1 -0
  77. package/dist/core/zellij-adopt-discovery.js +276 -0
  78. package/dist/core/zellij-adopt-discovery.js.map +1 -0
  79. package/dist/core/zellij-session-discovery.d.ts +73 -0
  80. package/dist/core/zellij-session-discovery.d.ts.map +1 -0
  81. package/dist/core/zellij-session-discovery.js +259 -0
  82. package/dist/core/zellij-session-discovery.js.map +1 -0
  83. package/dist/daemon.d.ts.map +1 -1
  84. package/dist/daemon.js +70 -4
  85. package/dist/daemon.js.map +1 -1
  86. package/dist/dashboard/web/i18n.js +1 -1
  87. package/dist/dashboard/web/i18n.js.map +1 -1
  88. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  89. package/dist/dashboard/web/sessions.js +1 -0
  90. package/dist/dashboard/web/sessions.js.map +1 -1
  91. package/dist/dashboard/web/workflows.js +1 -1
  92. package/dist/dashboard/web/workflows.js.map +1 -1
  93. package/dist/dashboard-web/app.js +3 -3
  94. package/dist/i18n/en.d.ts.map +1 -1
  95. package/dist/i18n/en.js +12 -0
  96. package/dist/i18n/en.js.map +1 -1
  97. package/dist/i18n/zh.d.ts.map +1 -1
  98. package/dist/i18n/zh.js +13 -1
  99. package/dist/i18n/zh.js.map +1 -1
  100. package/dist/im/lark/card-builder.d.ts +7 -1
  101. package/dist/im/lark/card-builder.d.ts.map +1 -1
  102. package/dist/im/lark/card-builder.js +92 -2
  103. package/dist/im/lark/card-builder.js.map +1 -1
  104. package/dist/im/lark/card-handler.d.ts.map +1 -1
  105. package/dist/im/lark/card-handler.js +78 -6
  106. package/dist/im/lark/card-handler.js.map +1 -1
  107. package/dist/im/lark/event-dispatcher.d.ts +8 -1
  108. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  109. package/dist/im/lark/event-dispatcher.js +28 -0
  110. package/dist/im/lark/event-dispatcher.js.map +1 -1
  111. package/dist/im/lark/grant-command.d.ts +13 -0
  112. package/dist/im/lark/grant-command.d.ts.map +1 -1
  113. package/dist/im/lark/grant-command.js +93 -0
  114. package/dist/im/lark/grant-command.js.map +1 -1
  115. package/dist/services/codex-app-threads.d.ts +20 -0
  116. package/dist/services/codex-app-threads.d.ts.map +1 -0
  117. package/dist/services/codex-app-threads.js +165 -0
  118. package/dist/services/codex-app-threads.js.map +1 -0
  119. package/dist/services/pending-response-transaction-store.d.ts +12 -0
  120. package/dist/services/pending-response-transaction-store.d.ts.map +1 -0
  121. package/dist/services/pending-response-transaction-store.js +52 -0
  122. package/dist/services/pending-response-transaction-store.js.map +1 -0
  123. package/dist/services/session-store.d.ts.map +1 -1
  124. package/dist/services/session-store.js +15 -1
  125. package/dist/services/session-store.js.map +1 -1
  126. package/dist/setup/bot-config-editor.d.ts +1 -1
  127. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  128. package/dist/setup/bot-config-editor.js +5 -4
  129. package/dist/setup/bot-config-editor.js.map +1 -1
  130. package/dist/setup/ensure-zellij.d.ts +48 -0
  131. package/dist/setup/ensure-zellij.d.ts.map +1 -0
  132. package/dist/setup/ensure-zellij.js +93 -0
  133. package/dist/setup/ensure-zellij.js.map +1 -0
  134. package/dist/types.d.ts +14 -3
  135. package/dist/types.d.ts.map +1 -1
  136. package/dist/utils/anchor-serializer.d.ts +3 -2
  137. package/dist/utils/anchor-serializer.d.ts.map +1 -1
  138. package/dist/utils/anchor-serializer.js +20 -5
  139. package/dist/utils/anchor-serializer.js.map +1 -1
  140. package/dist/utils/transient-snapshot.js +2 -2
  141. package/dist/utils/transient-snapshot.js.map +1 -1
  142. package/dist/worker.js +235 -32
  143. package/dist/worker.js.map +1 -1
  144. package/dist/workflows/attempt-resume.d.ts +1 -1
  145. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  146. package/dist/workflows/attempt-resume.js +1 -1
  147. package/dist/workflows/attempt-resume.js.map +1 -1
  148. package/dist/workflows/events/payloads.d.ts +20 -20
  149. package/dist/workflows/events/schema.d.ts +48 -48
  150. package/package.json +1 -1
@@ -0,0 +1,73 @@
1
+ export interface LayoutPane {
2
+ /** Foreground command (argv0) zellij introspected for this pane, e.g. "claude". */
3
+ command: string;
4
+ /** Explicit pane name if set (zellij `name=`), else undefined. */
5
+ name?: string;
6
+ /** Absolute cwd of the pane (layout base cwd joined with the pane's relative cwd). */
7
+ cwd?: string;
8
+ /** Command args, when present in the dump. */
9
+ args: string[];
10
+ }
11
+ export interface ListedPane {
12
+ /** "terminal_<n>" — the id used to target zellij `action` commands. */
13
+ paneId: string;
14
+ isPlugin: boolean;
15
+ isFloating: boolean;
16
+ title?: string;
17
+ terminalCommand?: string | null;
18
+ }
19
+ export interface DiscoveredCli {
20
+ session: string;
21
+ paneId: string;
22
+ command: string;
23
+ cwd?: string;
24
+ args: string[];
25
+ title?: string;
26
+ }
27
+ /**
28
+ * Parse `zellij action dump-layout` output, returning only the panes that have
29
+ * a foreground `command=` (i.e. real terminal panes running something) in
30
+ * document order. Template sections (new_tab_template / swap_*_layout) are cut
31
+ * off first — their bare `pane` nodes have no command and would otherwise add
32
+ * noise. Plugin panes (tab-bar / status-bar / about) have no command= and are
33
+ * naturally excluded.
34
+ */
35
+ export declare function parseDumpLayoutPanes(kdl: string): LayoutPane[];
36
+ export interface LeafPane {
37
+ /** Foreground command (argv0) if the pane is running one; undefined for an
38
+ * idle shell pane (zellij emits a bare `pane` with no command=). */
39
+ command?: string;
40
+ name?: string;
41
+ cwd?: string;
42
+ args: string[];
43
+ }
44
+ /**
45
+ * Parse `zellij action dump-layout` into the ordered list of LEAF terminal
46
+ * panes — both command-bearing panes AND idle bare shell panes — skipping
47
+ * plugin panes (tab-bar/status-bar/about), container panes (splits), and the
48
+ * floating subtree. Preserving bare panes is what makes a positional join to
49
+ * `list-panes` correct: a bare shell pane that sorts before the CLI pane would
50
+ * otherwise shift every command pane onto the wrong pane id (the bug Codex
51
+ * found). Templates (new_tab_template / swap_*) are cut off first.
52
+ *
53
+ * zellij always pretty-prints dump-layout one node per line, so a brace-stack
54
+ * line walk is reliable here.
55
+ */
56
+ export declare function parseDumpLayoutLeafPanes(kdl: string): LeafPane[];
57
+ /** Parse `zellij action list-panes --json` into a flat list (document order). */
58
+ export declare function parseListPanesJson(json: string): ListedPane[];
59
+ /**
60
+ * Join dump-layout command panes with list-panes terminal panes by document
61
+ * order: the i-th command pane ↔ the i-th non-plugin terminal pane (sorted by
62
+ * id). zellij assigns pane ids in creation order and walks the tree in a stable
63
+ * order, so this aligns for normal layouts. Returns one DiscoveredCli per
64
+ * command pane that could be bound to an id.
65
+ */
66
+ export declare function joinPanes(session: string, layoutPanes: LayoutPane[], listed: ListedPane[]): DiscoveredCli[];
67
+ /** Names of live (non-exited) zellij sessions. */
68
+ export declare function listLiveSessions(): string[];
69
+ /** Discover CLIs running in one session. */
70
+ export declare function discoverSessionClis(session: string): DiscoveredCli[];
71
+ /** Discover CLIs across every live zellij session. */
72
+ export declare function discoverAllClis(): DiscoveredCli[];
73
+ //# sourceMappingURL=zellij-session-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zellij-session-discovery.d.ts","sourceRoot":"","sources":["../../src/core/zellij-session-discovery.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CA6C9D;AAED,MAAM,WAAW,QAAQ;IACvB;yEACqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAuDhE;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAW7D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,aAAa,EAAE,CAmB3G;AAID,kDAAkD;AAClD,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAS3C;AAUD,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAKpE;AAED,sDAAsD;AACtD,wBAAgB,eAAe,IAAI,aAAa,EAAE,CAEjD"}
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Zellij session discovery for /adopt — find CLIs already running inside a
3
+ * user's zellij sessions and the pane needed to drive them.
4
+ *
5
+ * Why this exists (and why it's not just `list-panes`): zellij's
6
+ * `list-panes --json` exposes pane ids + geometry but NOT the running command,
7
+ * cwd, or pid, and its `terminal_command` is null for anything the user started
8
+ * interactively (typed `claude` into a shell — the common case). The data we
9
+ * need is instead surfaced by zellij's **session-resurrection** machinery: to be
10
+ * able to restore a session after a reboot, zellij continuously introspects each
11
+ * pane's *foreground process command + cwd* and exposes it via
12
+ * `zellij action dump-layout`. That's where we read "what CLI is in this pane".
13
+ *
14
+ * Discovery pipeline:
15
+ * 1. dump-layout → per-pane { command, args, cwd } (detection)
16
+ * 2. list-panes --json → per-pane { id: terminal_<n> } (drive target)
17
+ * 3. order/geometry join → bind command ↔ pane id
18
+ * 4. (caller) /proc descent under the pane shell → pid → ~/.claude/sessions/<pid>.json
19
+ *
20
+ * Parsers here are pure (string in, struct out) so they unit-test without a
21
+ * live zellij. The order-join (step 3) is robust for normal single/few-pane
22
+ * layouts; exotic multi-tab/floating arrangements may need the geometry/proc
23
+ * cross-check the caller layers on top.
24
+ */
25
+ import { execFileSync } from 'node:child_process';
26
+ import { isAbsolute, join as pathJoin } from 'node:path';
27
+ import { zellijEnv } from '../setup/ensure-zellij.js';
28
+ const TEMPLATE_MARKERS = /\b(new_tab_template|swap_tiled_layout|swap_floating_layout)\b/;
29
+ /**
30
+ * Parse `zellij action dump-layout` output, returning only the panes that have
31
+ * a foreground `command=` (i.e. real terminal panes running something) in
32
+ * document order. Template sections (new_tab_template / swap_*_layout) are cut
33
+ * off first — their bare `pane` nodes have no command and would otherwise add
34
+ * noise. Plugin panes (tab-bar / status-bar / about) have no command= and are
35
+ * naturally excluded.
36
+ */
37
+ export function parseDumpLayoutPanes(kdl) {
38
+ // Drop the template tail so we only see live tab content.
39
+ const tmplIdx = kdl.search(TEMPLATE_MARKERS);
40
+ const body = tmplIdx >= 0 ? kdl.slice(0, tmplIdx) : kdl;
41
+ const lines = body.split('\n');
42
+ const panes = [];
43
+ // Layout base cwd is a NODE: `cwd "..."` (space). Pane cwd is an ATTRIBUTE:
44
+ // `cwd="..."` (equals). The first node-form cwd is the layout base.
45
+ let layoutCwd;
46
+ const baseCwdMatch = body.match(/^\s*cwd\s+"([^"]*)"/m);
47
+ if (baseCwdMatch)
48
+ layoutCwd = baseCwdMatch[1];
49
+ let pending = null;
50
+ const flush = () => { if (pending) {
51
+ panes.push(pending);
52
+ pending = null;
53
+ } };
54
+ for (const line of lines) {
55
+ const trimmed = line.trim();
56
+ // A pane that runs a command (attributes can appear in any order).
57
+ if (/^pane\b/.test(trimmed) && /\bcommand=/.test(trimmed)) {
58
+ flush();
59
+ const command = attr(trimmed, 'command');
60
+ const name = attr(trimmed, 'name');
61
+ const cwdAttr = attr(trimmed, 'cwd');
62
+ pending = {
63
+ command: command ?? '',
64
+ name: name ?? undefined,
65
+ cwd: resolveCwd(layoutCwd, cwdAttr),
66
+ args: [],
67
+ };
68
+ // Single-line pane (closed on same line, no block) — flush immediately.
69
+ if (trimmed.includes('}') && !trimmed.endsWith('{'))
70
+ flush();
71
+ continue;
72
+ }
73
+ // args node inside the current pane block: `args "a" "b" …`
74
+ if (pending && /^args\b/.test(trimmed)) {
75
+ pending.args = [...trimmed.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map(m => unescapeKdl(m[1]));
76
+ continue;
77
+ }
78
+ // Next pane / tab / closing — a new `pane` (without command) ends the block.
79
+ if (pending && /^pane\b/.test(trimmed))
80
+ flush();
81
+ }
82
+ flush();
83
+ return panes;
84
+ }
85
+ /**
86
+ * Parse `zellij action dump-layout` into the ordered list of LEAF terminal
87
+ * panes — both command-bearing panes AND idle bare shell panes — skipping
88
+ * plugin panes (tab-bar/status-bar/about), container panes (splits), and the
89
+ * floating subtree. Preserving bare panes is what makes a positional join to
90
+ * `list-panes` correct: a bare shell pane that sorts before the CLI pane would
91
+ * otherwise shift every command pane onto the wrong pane id (the bug Codex
92
+ * found). Templates (new_tab_template / swap_*) are cut off first.
93
+ *
94
+ * zellij always pretty-prints dump-layout one node per line, so a brace-stack
95
+ * line walk is reliable here.
96
+ */
97
+ export function parseDumpLayoutLeafPanes(kdl) {
98
+ const tmplIdx = kdl.search(TEMPLATE_MARKERS);
99
+ const body = tmplIdx >= 0 ? kdl.slice(0, tmplIdx) : kdl;
100
+ const baseCwdMatch = body.match(/^\s*cwd\s+"([^"]*)"/m);
101
+ const layoutCwd = baseCwdMatch ? baseCwdMatch[1] : undefined;
102
+ const stack = [];
103
+ const leaves = [];
104
+ const inFloating = () => stack.some(f => f.isFloating);
105
+ const emit = (command, name, cwdAttr, args) => {
106
+ if (inFloating())
107
+ return;
108
+ leaves.push({ command, name, cwd: resolveCwd(layoutCwd, cwdAttr), args });
109
+ };
110
+ for (const raw of body.split('\n')) {
111
+ const line = raw.trim();
112
+ if (!line)
113
+ continue;
114
+ if (line === '}') {
115
+ const f = stack.pop();
116
+ // A pane frame that contained neither a plugin nor child panes is a leaf
117
+ // terminal pane (its block held only props like args/start_suspended).
118
+ if (f?.isPane && !f.hasPlugin && !f.hasChildPane && !inFloating()) {
119
+ leaves.push({ command: f.command, name: f.name, cwd: resolveCwd(layoutCwd, f.cwdAttr), args: f.args });
120
+ }
121
+ continue;
122
+ }
123
+ const opensBlock = line.endsWith('{');
124
+ if (line.startsWith('plugin')) {
125
+ if (stack.length && stack[stack.length - 1].isPane)
126
+ stack[stack.length - 1].hasPlugin = true;
127
+ if (opensBlock)
128
+ stack.push({ isPane: false, isFloating: false, args: [], hasPlugin: false, hasChildPane: false });
129
+ continue;
130
+ }
131
+ if (line.startsWith('pane')) {
132
+ if (stack.length && stack[stack.length - 1].isPane)
133
+ stack[stack.length - 1].hasChildPane = true;
134
+ const command = attr(line, 'command');
135
+ const name = attr(line, 'name');
136
+ const cwdAttr = attr(line, 'cwd');
137
+ if (opensBlock) {
138
+ stack.push({ isPane: true, isFloating: false, command, name, cwdAttr, args: [], hasPlugin: false, hasChildPane: false });
139
+ }
140
+ else {
141
+ emit(command, name, cwdAttr, []); // bare leaf, no block
142
+ }
143
+ continue;
144
+ }
145
+ if (opensBlock) {
146
+ // tab / floating_panes / swap_* / other container
147
+ stack.push({ isPane: false, isFloating: line.startsWith('floating_panes'), args: [], hasPlugin: false, hasChildPane: false });
148
+ continue;
149
+ }
150
+ if (line.startsWith('args') && stack.length && stack[stack.length - 1].isPane) {
151
+ stack[stack.length - 1].args = [...line.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map(m => unescapeKdl(m[1]));
152
+ }
153
+ }
154
+ return leaves;
155
+ }
156
+ /** Parse `zellij action list-panes --json` into a flat list (document order). */
157
+ export function parseListPanesJson(json) {
158
+ let arr;
159
+ try {
160
+ arr = JSON.parse(json);
161
+ }
162
+ catch {
163
+ return [];
164
+ }
165
+ if (!Array.isArray(arr))
166
+ return [];
167
+ return arr.map((p) => ({
168
+ paneId: `terminal_${p.id}`,
169
+ isPlugin: !!p.is_plugin,
170
+ isFloating: !!p.is_floating,
171
+ title: typeof p.title === 'string' ? p.title : undefined,
172
+ terminalCommand: p.terminal_command ?? null,
173
+ }));
174
+ }
175
+ /**
176
+ * Join dump-layout command panes with list-panes terminal panes by document
177
+ * order: the i-th command pane ↔ the i-th non-plugin terminal pane (sorted by
178
+ * id). zellij assigns pane ids in creation order and walks the tree in a stable
179
+ * order, so this aligns for normal layouts. Returns one DiscoveredCli per
180
+ * command pane that could be bound to an id.
181
+ */
182
+ export function joinPanes(session, layoutPanes, listed) {
183
+ const terminals = listed
184
+ .filter(p => !p.isPlugin)
185
+ .sort((a, b) => paneNum(a.paneId) - paneNum(b.paneId));
186
+ const out = [];
187
+ for (let i = 0; i < layoutPanes.length; i++) {
188
+ const lp = layoutPanes[i];
189
+ const tp = terminals[i];
190
+ if (!tp)
191
+ break;
192
+ out.push({
193
+ session,
194
+ paneId: tp.paneId,
195
+ command: lp.command,
196
+ cwd: lp.cwd,
197
+ args: lp.args,
198
+ title: tp.title,
199
+ });
200
+ }
201
+ return out;
202
+ }
203
+ // ─── Runtime (shells out to zellij) ─────────────────────────────────────────
204
+ /** Names of live (non-exited) zellij sessions. */
205
+ export function listLiveSessions() {
206
+ try {
207
+ const out = execFileSync('zellij', ['list-sessions', '--no-formatting'], {
208
+ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, env: zellijEnv(),
209
+ });
210
+ return out.split('\n').map(l => l.trim())
211
+ .filter(l => l.length > 0 && !/EXITED/i.test(l))
212
+ .map(l => l.split(/\s+/)[0]).filter(Boolean);
213
+ }
214
+ catch {
215
+ return [];
216
+ }
217
+ }
218
+ function zellijAction(session, args) {
219
+ try {
220
+ return execFileSync('zellij', ['--session', session, 'action', ...args], {
221
+ encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 4000, env: zellijEnv(),
222
+ });
223
+ }
224
+ catch {
225
+ return null;
226
+ }
227
+ }
228
+ /** Discover CLIs running in one session. */
229
+ export function discoverSessionClis(session) {
230
+ const layout = zellijAction(session, ['dump-layout']);
231
+ const panesJson = zellijAction(session, ['list-panes', '--json']);
232
+ if (!layout || !panesJson)
233
+ return [];
234
+ return joinPanes(session, parseDumpLayoutPanes(layout), parseListPanesJson(panesJson));
235
+ }
236
+ /** Discover CLIs across every live zellij session. */
237
+ export function discoverAllClis() {
238
+ return listLiveSessions().flatMap(discoverSessionClis);
239
+ }
240
+ // ─── helpers ────────────────────────────────────────────────────────────────
241
+ function attr(line, key) {
242
+ const m = line.match(new RegExp(`\\b${key}="((?:[^"\\\\]|\\\\.)*)"`));
243
+ return m ? unescapeKdl(m[1]) : undefined;
244
+ }
245
+ function resolveCwd(base, paneCwd) {
246
+ if (!paneCwd)
247
+ return base;
248
+ if (isAbsolute(paneCwd))
249
+ return paneCwd;
250
+ return base ? pathJoin(base, paneCwd) : paneCwd;
251
+ }
252
+ function paneNum(paneId) {
253
+ const m = paneId.match(/(\d+)$/);
254
+ return m ? Number(m[1]) : 0;
255
+ }
256
+ function unescapeKdl(s) {
257
+ return s.replace(/\\(.)/g, '$1');
258
+ }
259
+ //# sourceMappingURL=zellij-session-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zellij-session-discovery.js","sourceRoot":"","sources":["../../src/core/zellij-session-discovery.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;AAsErD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAiB,MAAM,wBAAwB,CAAC;AAsQpF,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;AAkpED,wBAAsB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyTlE"}
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"}
package/dist/daemon.js CHANGED
@@ -27,7 +27,9 @@ import { buildTerminalUrl, setTerminalProxyPort } from './core/terminal-url.js';
27
27
  import { startTerminalProxy } from './core/terminal-proxy.js';
28
28
  import * as scheduler from './core/scheduler.js';
29
29
  import { scanMultipleProjects } from './services/project-scanner.js';
30
- import { buildQuotaExhaustedCard, buildRepoSelectCard, buildStreamingCard, getCliDisplayName } from './im/lark/card-builder.js';
30
+ import { buildPendingResponseCard, buildQuotaExhaustedCard, buildRepoSelectCard, buildStreamingCard, getCliDisplayName } from './im/lark/card-builder.js';
31
+ import { createPendingResponseQueue, markPendingResponseCardPatched, shouldTreatPendingCardAsPatchedByMarker, startPendingResponseTurn, syncPendingResponseState } from './core/pending-response.js';
32
+ import { readPendingResponsePatchMarker } from './services/pending-response-transaction-store.js';
31
33
  import { t as tr, botLocale, localeForBot } from './i18n/index.js';
32
34
  import { createCliAdapterSync } from './adapters/cli/registry.js';
33
35
  import { initWorkerPool, setActiveSessionsRegistry, forkWorker, killWorker, scheduleCardPatch, setCurrentCliVersion, CARD_POSTING_SENTINEL, parkStreamCard, closeSession as closeSessionHelper, ensureCliEnv, writableTerminalLinkFor, } from './core/worker-pool.js';
@@ -180,6 +182,56 @@ function startMemoryDiagnostics() {
180
182
  * Lark message ids start with `om_` and chat ids with `oc_`, so the two
181
183
  * address spaces never collide; the lookup just tries both.
182
184
  */
185
+ const pendingResponseQueue = createPendingResponseQueue();
186
+ function streamingCardDisabledFor(ds) {
187
+ if (ds.streamingCardForced)
188
+ return false;
189
+ try {
190
+ return getBot(ds.larkAppId).config.disableStreamingCard === true;
191
+ }
192
+ catch {
193
+ return false;
194
+ }
195
+ }
196
+ function readSessionFreshFromDisk(sessionId, larkAppId) {
197
+ const paths = [
198
+ join(config.session.dataDir, `sessions-${larkAppId}.json`),
199
+ join(config.session.dataDir, 'sessions.json'),
200
+ ];
201
+ for (const fp of paths) {
202
+ if (!existsSync(fp))
203
+ continue;
204
+ try {
205
+ const data = JSON.parse(readFileSync(fp, 'utf-8'));
206
+ if (data[sessionId])
207
+ return data[sessionId];
208
+ }
209
+ catch { /* ignore corrupt/racing session file */ }
210
+ }
211
+ return undefined;
212
+ }
213
+ async function postPendingResponseCard(ds, replyToMessageId, prompt, sender) {
214
+ if (!streamingCardDisabledFor(ds))
215
+ return;
216
+ await pendingResponseQueue.run(ds.session.sessionId, async () => {
217
+ syncPendingResponseState(ds, readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId));
218
+ const marker = readPendingResponsePatchMarker(ds.session.sessionId);
219
+ if (shouldTreatPendingCardAsPatchedByMarker(ds.pendingResponseCardId, marker)) {
220
+ markPendingResponseCardPatched(ds);
221
+ }
222
+ syncPendingResponseState(ds.session, ds);
223
+ const card = buildPendingResponseCard(localeForBot(ds.larkAppId));
224
+ try {
225
+ const messageId = await replyMessage(ds.larkAppId, replyToMessageId, card, 'interactive', false);
226
+ startPendingResponseTurn(ds, messageId);
227
+ startPendingResponseTurn(ds.session, messageId);
228
+ sessionStore.updateSession(ds.session);
229
+ }
230
+ catch (err) {
231
+ logger.warn(`[${tag(ds)}] failed to post pending response card: ${err instanceof Error ? err.message : String(err)}`);
232
+ }
233
+ });
234
+ }
183
235
  async function sessionReply(anchor, content, msgType = 'text', larkAppId) {
184
236
  let ds;
185
237
  if (larkAppId) {
@@ -1673,6 +1725,7 @@ async function handleNewTopic(data, ctx) {
1673
1725
  const selfBot = getBot(larkAppId);
1674
1726
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, chatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), newTopicSender, { larkAppId, chatId });
1675
1727
  rememberLastCliInput(ds, promptContent, prompt);
1728
+ await postPendingResponseCard(ds, messageId, content, newTopicSender);
1676
1729
  forkWorker(ds, prompt);
1677
1730
  const reason = oncallEntry
1678
1731
  ? `oncall-bound chat ${chatId}`
@@ -1703,6 +1756,7 @@ async function handleNewTopic(data, ctx) {
1703
1756
  const selfBot = getBot(larkAppId);
1704
1757
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, chatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), newTopicSender, { larkAppId, chatId });
1705
1758
  rememberLastCliInput(ds, promptContent, prompt);
1759
+ await postPendingResponseCard(ds, messageId, content, newTopicSender);
1706
1760
  forkWorker(ds, prompt);
1707
1761
  logger.info(`Session ${session.sessionId} ready (no projects to select), total active: ${getActiveCount()}`);
1708
1762
  }
@@ -1863,6 +1917,7 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId) {
1863
1917
  return;
1864
1918
  const prompt = await buildPrompt();
1865
1919
  rememberLastCliInput(ds, promptBody, prompt);
1920
+ await postPendingResponseCard(ds, anchor, promptBody);
1866
1921
  forkWorker(ds, prompt);
1867
1922
  logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 自动开工(${mode}/${scope}),workingDir=${pinnedWorkingDir}`);
1868
1923
  return;
@@ -1882,6 +1937,7 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId) {
1882
1937
  ds.pendingRepo = false;
1883
1938
  const prompt = await buildPrompt();
1884
1939
  rememberLastCliInput(ds, promptBody, prompt);
1940
+ await postPendingResponseCard(ds, anchor, promptBody);
1885
1941
  forkWorker(ds, prompt);
1886
1942
  logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 无默认目录且无可选项目,直接开工`);
1887
1943
  }
@@ -2133,6 +2189,8 @@ async function handleThreadReply(data, ctx) {
2133
2189
  // reply cards to whoever triggered this turn — matters in oncall groups
2134
2190
  // where the caller is often not the session owner).
2135
2191
  if (ds) {
2192
+ syncPendingResponseState(ds, readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId));
2193
+ syncPendingResponseState(ds.session, ds);
2136
2194
  markSessionActivity(ds);
2137
2195
  const callerOpenId = parsed.senderId || data?.sender?.sender_id?.open_id;
2138
2196
  // quoteTargetId changes every inbound message (always a new message_id), so
@@ -2285,6 +2343,7 @@ async function handleThreadReply(data, ctx) {
2285
2343
  const selfBot = getBot(larkAppId);
2286
2344
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, autoCreateChatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), autoCreateSender, { larkAppId, chatId: autoCreateChatId });
2287
2345
  rememberLastCliInput(newDs, promptContent, prompt);
2346
+ await postPendingResponseCard(newDs, parsed.messageId, parsed.content, autoCreateSender);
2288
2347
  forkWorker(newDs, prompt);
2289
2348
  const reason = oncallEntry
2290
2349
  ? `oncall-bound chat ${autoCreateChatId}`
@@ -2315,6 +2374,7 @@ async function handleThreadReply(data, ctx) {
2315
2374
  const selfBot = getBot(larkAppId);
2316
2375
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, autoCreateChatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), autoCreateSender, { larkAppId, chatId: autoCreateChatId });
2317
2376
  rememberLastCliInput(newDs, promptContent, prompt);
2377
+ await postPendingResponseCard(newDs, parsed.messageId, parsed.content, autoCreateSender);
2318
2378
  forkWorker(newDs, prompt);
2319
2379
  }
2320
2380
  return;
@@ -2350,6 +2410,7 @@ async function handleThreadReply(data, ctx) {
2350
2410
  });
2351
2411
  beginNewTurn(ds, parsed.content);
2352
2412
  rememberLastCliInput(ds, promptContent, msgContent);
2413
+ await postPendingResponseCard(ds, parsed.messageId, parsed.content, await getThreadSender());
2353
2414
  ds.worker.send({ type: 'message', content: msgContent });
2354
2415
  }
2355
2416
  else {
@@ -2398,6 +2459,7 @@ async function handleThreadReply(data, ctx) {
2398
2459
  sender: await getThreadSender(),
2399
2460
  });
2400
2461
  rememberLastCliInput(ds, promptContent, wrappedPrompt);
2462
+ await postPendingResponseCard(ds, parsed.messageId, parsed.content, await getThreadSender());
2401
2463
  forkWorker(ds, wrappedPrompt, ds.hasHistory);
2402
2464
  }
2403
2465
  }
@@ -2662,9 +2724,13 @@ export async function startDaemon(botIndex) {
2662
2724
  const backendType = ds.larkAppId
2663
2725
  ? (getBot(ds.larkAppId).config.backendType ?? config.daemon.backendType)
2664
2726
  : config.daemon.backendType;
2665
- if (backendType === 'tmux') {
2666
- // Tmux mode: just kill the worker process — tmux session survives for re-attach.
2667
- // Worker's SIGTERM handler calls backend.kill() which only detaches.
2727
+ if (backendType === 'tmux' || backendType === 'zellij') {
2728
+ // Persistent backends (tmux / zellij): just kill the worker process —
2729
+ // the multiplexer session survives for re-attach. The worker's SIGTERM
2730
+ // handler calls backend.kill(), which only DETACHES. Going through
2731
+ // killWorker() instead would send {type:'close'} → destroySession() →
2732
+ // `zellij delete-session -f`, permanently erasing the session and
2733
+ // breaking daemon-restart reattach (the blocker Codex flagged).
2668
2734
  try {
2669
2735
  w.kill('SIGTERM');
2670
2736
  }