@0xmaxma/claude-gateway 1.3.4 → 1.3.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 (55) hide show
  1. package/README.md +1 -1
  2. package/config.template.json +9 -7
  3. package/dist/agent/runner.d.ts +20 -0
  4. package/dist/agent/runner.d.ts.map +1 -1
  5. package/dist/agent/runner.js +94 -1
  6. package/dist/agent/runner.js.map +1 -1
  7. package/dist/api/gateway-router.d.ts +14 -0
  8. package/dist/api/gateway-router.d.ts.map +1 -1
  9. package/dist/api/gateway-router.js +279 -7
  10. package/dist/api/gateway-router.js.map +1 -1
  11. package/dist/discord/receiver.d.ts +1 -0
  12. package/dist/discord/receiver.d.ts.map +1 -1
  13. package/dist/discord/receiver.js +7 -1
  14. package/dist/discord/receiver.js.map +1 -1
  15. package/dist/session/process.d.ts +8 -0
  16. package/dist/session/process.d.ts.map +1 -1
  17. package/dist/session/process.js +34 -0
  18. package/dist/session/process.js.map +1 -1
  19. package/dist/shell/claude-pty-shell.js +211 -4
  20. package/dist/shell/claude-pty-shell.js.map +1 -1
  21. package/dist/shell/emitter.d.ts +13 -0
  22. package/dist/shell/emitter.d.ts.map +1 -1
  23. package/dist/shell/emitter.js +16 -0
  24. package/dist/shell/emitter.js.map +1 -1
  25. package/dist/shell/menu-cancel.d.ts +50 -0
  26. package/dist/shell/menu-cancel.d.ts.map +1 -0
  27. package/dist/shell/menu-cancel.js +62 -0
  28. package/dist/shell/menu-cancel.js.map +1 -0
  29. package/dist/shell/pty-serialize.d.ts +23 -0
  30. package/dist/shell/pty-serialize.d.ts.map +1 -0
  31. package/dist/shell/pty-serialize.js +131 -0
  32. package/dist/shell/pty-serialize.js.map +1 -0
  33. package/dist/shell/pty-stream-registry.d.ts +59 -0
  34. package/dist/shell/pty-stream-registry.d.ts.map +1 -0
  35. package/dist/shell/pty-stream-registry.js +279 -0
  36. package/dist/shell/pty-stream-registry.js.map +1 -0
  37. package/dist/shell/screen.d.ts +43 -0
  38. package/dist/shell/screen.d.ts.map +1 -1
  39. package/dist/shell/screen.js +91 -1
  40. package/dist/shell/screen.js.map +1 -1
  41. package/dist/telegram/receiver.d.ts +1 -0
  42. package/dist/telegram/receiver.d.ts.map +1 -1
  43. package/dist/telegram/receiver.js +7 -1
  44. package/dist/telegram/receiver.js.map +1 -1
  45. package/dist/ui/web-ui.d.ts +2 -2
  46. package/dist/ui/web-ui.d.ts.map +1 -1
  47. package/dist/ui/web-ui.js +682 -114
  48. package/dist/ui/web-ui.js.map +1 -1
  49. package/mcp/tools/discord/module.ts +88 -1
  50. package/mcp/tools/discord/outbound.ts +36 -0
  51. package/mcp/tools/discord/types.ts +2 -0
  52. package/mcp/tools/telegram/menu-parser.ts +37 -0
  53. package/mcp/tools/telegram/receiver-server.ts +83 -0
  54. package/mcp/tools/telegram/typing.ts +7 -1
  55. package/package.json +4 -2
@@ -1,4 +1,5 @@
1
1
  import type { AssistantRecord, UsageInfo } from './tailer';
2
+ import type { MenuOption } from './screen';
2
3
  /**
3
4
  * Synthesizes the stream-json events the gateway's SessionProcess stdout
4
5
  * parser consumes (src/session/process.ts). Every assistant event is emitted
@@ -25,6 +26,18 @@ export declare class ProtocolEmitter {
25
26
  * Returns the text contained in the record, '' if none.
26
27
  */
27
28
  emitAssistant(record: AssistantRecord, sessionId: string): string;
29
+ /**
30
+ * Signal that the TUI is blocked on an interactive select menu. The gateway
31
+ * (runner.ts) renders this as channel-native UI — inline buttons on
32
+ * Telegram/Discord — while the turn's result text carries the same numbered
33
+ * list as a fallback (and for API, which has no buttons). callback_data /
34
+ * custom_id only ever carry the 1-based choice index.
35
+ */
36
+ emitMenuPrompt(opts: {
37
+ sessionId: string;
38
+ prompt: string;
39
+ options: MenuOption[];
40
+ }): void;
28
41
  emitResult(opts: {
29
42
  sessionId: string;
30
43
  isError: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/shell/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE3D;;;;;;GAMG;AACH,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,GAAE,MAAM,CAAC,cAA+B;IAExE,OAAO,CAAC,SAAS;IAIjB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAW7D;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAiB/D;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAmBjE,UAAU,CAAC,IAAI,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;KACzB,GAAG,IAAI;CAaT"}
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/shell/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,GAAE,MAAM,CAAC,cAA+B;IAExE,OAAO,CAAC,SAAS;IAIjB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAW7D;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAiB/D;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAmBjE;;;;;;OAMG;IACH,cAAc,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,EAAE,CAAA;KAAE,GAAG,IAAI;IAUxF,UAAU,CAAC,IAAI,EAAE;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;KACzB,GAAG,IAAI;CAaT"}
@@ -68,6 +68,22 @@ class ProtocolEmitter {
68
68
  .map((b) => String(b.text ?? ''))
69
69
  .join('');
70
70
  }
71
+ /**
72
+ * Signal that the TUI is blocked on an interactive select menu. The gateway
73
+ * (runner.ts) renders this as channel-native UI — inline buttons on
74
+ * Telegram/Discord — while the turn's result text carries the same numbered
75
+ * list as a fallback (and for API, which has no buttons). callback_data /
76
+ * custom_id only ever carry the 1-based choice index.
77
+ */
78
+ emitMenuPrompt(opts) {
79
+ this.writeLine({
80
+ type: 'system',
81
+ subtype: 'menu_prompt',
82
+ session_id: opts.sessionId,
83
+ prompt: opts.prompt,
84
+ options: opts.options,
85
+ });
86
+ }
71
87
  emitResult(opts) {
72
88
  this.writeLine({
73
89
  type: 'result',
@@ -1 +1 @@
1
- {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/shell/emitter.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACH,MAAa,eAAe;IAC1B,YAA6B,MAA6B,OAAO,CAAC,MAAM;QAA3C,QAAG,GAAH,GAAG,CAAwC;IAAG,CAAC;IAEpE,SAAS,CAAC,GAA4B;QAC5C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,QAAQ,CAAC,SAAiB,EAAE,KAAa,EAAE,GAAW;QACpD,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,SAAS;YACrB,KAAK;YACL,GAAG;YACH,KAAK,EAAE,EAAE;SACV,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,KAAgB,EAAE,SAAiB;QACtD,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,cAAc;YACpB,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE;gBACL,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,CAAC;wBACrC,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,IAAI,CAAC;wBAC3D,2BAA2B,EAAE,KAAK,CAAC,2BAA2B,IAAI,CAAC;qBACpE;iBACF;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,MAAuB,EAAE,SAAiB;QACtD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAC1C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAClD,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEnC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,WAAW;YACjB,UAAU,EAAE,SAAS;YACrB,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,IAAI,UAAU;YACrD,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE;SAChD,CAAC,CAAC;QAEH,OAAO,MAAM;aACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAE,CAAwB,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;aACxD,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;IAED,UAAU,CAAC,IAMV;QACC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,SAAS;YAC5D,QAAQ,EAAE,IAAI,CAAC,OAAO;YACtB,MAAM,EAAE,IAAI,CAAC,IAAI;YACjB,WAAW,EAAE,IAAI,CAAC,UAAU;YAC5B,SAAS,EAAE,CAAC;YACZ,UAAU,EAAE,IAAI,CAAC,SAAS;YAC1B,KAAK,EAAE,EAAE,aAAa,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC,EAAE;SACzD,CAAC,CAAC;IACL,CAAC;CAEF;AArFD,0CAqFC"}
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/shell/emitter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Decision logic for submitting a queued prompt after ESC-cancelling a bridged
3
+ * interactive menu.
4
+ *
5
+ * When the user replies to a bridged menu with free text instead of a number,
6
+ * the wrapper sends ESC to dismiss the menu and then wants to submit the text as
7
+ * a fresh prompt. But ESC makes Claude resume (it processes the cancellation),
8
+ * so the TUI is briefly busy/redrawing. Submitting into that transition races —
9
+ * the paste lands in a non-prompt state, the Enter is swallowed, and the turn
10
+ * never starts (→ 30-min watchdog hang). So submission is deferred until the TUI
11
+ * settles back to an idle prompt.
12
+ *
13
+ * This module is the pure decision: given the current observations, return the
14
+ * action the caller should take. Kept free of node-pty / screen imports so it is
15
+ * cheap to unit-test in isolation.
16
+ */
17
+ export declare const MENU_CANCEL_MIN_WAIT_MS = 800;
18
+ export declare const MENU_CANCEL_SETTLE_QUIET_MS = 600;
19
+ export declare const MENU_CANCEL_ESC_RETRY_MS = 1500;
20
+ export declare const MENU_CANCEL_MAX_ESC = 3;
21
+ export declare const MENU_CANCEL_TIMEOUT_MS = 15000;
22
+ export type MenuCancelAction = 'wait' | 'resend-esc' | 'submit';
23
+ export interface MenuCancelState {
24
+ /** Timestamp (ms) of the first ESC that started this cancel. */
25
+ since: number;
26
+ /** Timestamp (ms) of the most recent ESC sent. */
27
+ lastEscAt: number;
28
+ /** How many ESCs have been sent so far (starts at 1). */
29
+ escs: number;
30
+ }
31
+ export interface MenuCancelObs {
32
+ now: number;
33
+ /** Is an interactive select menu still on screen? */
34
+ menuVisible: boolean;
35
+ /** Is the normal input prompt box present? */
36
+ hasPrompt: boolean;
37
+ /** Is the TUI busy (spinner / processing)? */
38
+ isBusy: boolean;
39
+ /** Milliseconds since the last PTY output. */
40
+ quietMs: number;
41
+ }
42
+ /**
43
+ * Decide what to do while waiting for the TUI to settle after a menu-cancel.
44
+ *
45
+ * - `submit` — the queued prompt is safe to type now (or the hard timeout hit).
46
+ * - `resend-esc` — the menu is still up; ESC was likely swallowed, send it again.
47
+ * - `wait` — keep waiting; nothing to do this tick.
48
+ */
49
+ export declare function decideMenuCancel(state: MenuCancelState, obs: MenuCancelObs): MenuCancelAction;
50
+ //# sourceMappingURL=menu-cancel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-cancel.d.ts","sourceRoot":"","sources":["../../src/shell/menu-cancel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,qDAAqD;IACrD,WAAW,EAAE,OAAO,CAAC;IACrB,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,EAAE,aAAa,GAAG,gBAAgB,CAyB7F"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * Decision logic for submitting a queued prompt after ESC-cancelling a bridged
4
+ * interactive menu.
5
+ *
6
+ * When the user replies to a bridged menu with free text instead of a number,
7
+ * the wrapper sends ESC to dismiss the menu and then wants to submit the text as
8
+ * a fresh prompt. But ESC makes Claude resume (it processes the cancellation),
9
+ * so the TUI is briefly busy/redrawing. Submitting into that transition races —
10
+ * the paste lands in a non-prompt state, the Enter is swallowed, and the turn
11
+ * never starts (→ 30-min watchdog hang). So submission is deferred until the TUI
12
+ * settles back to an idle prompt.
13
+ *
14
+ * This module is the pure decision: given the current observations, return the
15
+ * action the caller should take. Kept free of node-pty / screen imports so it is
16
+ * cheap to unit-test in isolation.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.MENU_CANCEL_TIMEOUT_MS = exports.MENU_CANCEL_MAX_ESC = exports.MENU_CANCEL_ESC_RETRY_MS = exports.MENU_CANCEL_SETTLE_QUIET_MS = exports.MENU_CANCEL_MIN_WAIT_MS = void 0;
20
+ exports.decideMenuCancel = decideMenuCancel;
21
+ // Minimum delay after the ESC before we allow a submit. Gives Claude time to
22
+ // start any cancellation response so `isBusy` can catch it (and block us).
23
+ exports.MENU_CANCEL_MIN_WAIT_MS = 800;
24
+ // The PTY must be quiet at least this long, once back at an idle prompt.
25
+ exports.MENU_CANCEL_SETTLE_QUIET_MS = 600;
26
+ // If the menu lingers (ESC swallowed), re-send ESC no more often than this.
27
+ exports.MENU_CANCEL_ESC_RETRY_MS = 1500;
28
+ // Cap on ESC re-sends before we stop retrying.
29
+ exports.MENU_CANCEL_MAX_ESC = 3;
30
+ // Hard fallback: submit anyway after this long so a stuck TUI never hangs.
31
+ exports.MENU_CANCEL_TIMEOUT_MS = 15000;
32
+ /**
33
+ * Decide what to do while waiting for the TUI to settle after a menu-cancel.
34
+ *
35
+ * - `submit` — the queued prompt is safe to type now (or the hard timeout hit).
36
+ * - `resend-esc` — the menu is still up; ESC was likely swallowed, send it again.
37
+ * - `wait` — keep waiting; nothing to do this tick.
38
+ */
39
+ function decideMenuCancel(state, obs) {
40
+ const { since, lastEscAt, escs } = state;
41
+ const { now, menuVisible, hasPrompt, isBusy, quietMs } = obs;
42
+ // Hard timeout: never hang — submit regardless of screen state. The turn's
43
+ // own Enter-retry logic is the last line of defence for a swallowed submit.
44
+ if (now - since > exports.MENU_CANCEL_TIMEOUT_MS)
45
+ return 'submit';
46
+ // Settled back to an idle prompt → safe to submit the queued text.
47
+ if (!menuVisible
48
+ && hasPrompt
49
+ && !isBusy
50
+ && now - since >= exports.MENU_CANCEL_MIN_WAIT_MS
51
+ && quietMs >= exports.MENU_CANCEL_SETTLE_QUIET_MS) {
52
+ return 'submit';
53
+ }
54
+ // Menu still lingering → ESC may have been swallowed; re-send periodically.
55
+ if (menuVisible
56
+ && now - lastEscAt > exports.MENU_CANCEL_ESC_RETRY_MS
57
+ && escs < exports.MENU_CANCEL_MAX_ESC) {
58
+ return 'resend-esc';
59
+ }
60
+ return 'wait';
61
+ }
62
+ //# sourceMappingURL=menu-cancel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-cancel.js","sourceRoot":"","sources":["../../src/shell/menu-cancel.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AA4CH,4CAyBC;AAnED,6EAA6E;AAC7E,2EAA2E;AAC9D,QAAA,uBAAuB,GAAG,GAAG,CAAC;AAC3C,yEAAyE;AAC5D,QAAA,2BAA2B,GAAG,GAAG,CAAC;AAC/C,4EAA4E;AAC/D,QAAA,wBAAwB,GAAG,IAAI,CAAC;AAC7C,+CAA+C;AAClC,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACrC,2EAA2E;AAC9D,QAAA,sBAAsB,GAAG,KAAM,CAAC;AAyB7C;;;;;;GAMG;AACH,SAAgB,gBAAgB,CAAC,KAAsB,EAAE,GAAkB;IACzE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACzC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;IAE7D,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAI,GAAG,GAAG,KAAK,GAAG,8BAAsB;QAAE,OAAO,QAAQ,CAAC;IAE1D,mEAAmE;IACnE,IAAI,CAAC,WAAW;WACT,SAAS;WACT,CAAC,MAAM;WACP,GAAG,GAAG,KAAK,IAAI,+BAAuB;WACtC,OAAO,IAAI,mCAA2B,EAAE,CAAC;QAC9C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,4EAA4E;IAC5E,IAAI,WAAW;WACR,GAAG,GAAG,SAAS,GAAG,gCAAwB;WAC1C,IAAI,GAAG,2BAAmB,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Terminal } from '@xterm/headless';
2
+ /**
3
+ * Serialize a headless terminal's CURRENT visible screen into a self-contained
4
+ * sequence of escape codes that, written into a fresh xterm, reproduces the same
5
+ * screen exactly.
6
+ *
7
+ * Why this exists: the PTY stream registry used to replay the last N KB of RAW
8
+ * bytes on every (re)connect. An alt-screen TUI (Claude Code runs in the
9
+ * alternate buffer) cannot be reconstructed from a truncated byte tail — the
10
+ * visible screen is the *cumulative* result of every byte since session start.
11
+ * Once output exceeds the ring-buffer cap (which happens fast during an active
12
+ * turn), the front is trimmed mid-sequence: the alt-screen-enter, scroll-region
13
+ * setup, cursor moves, and even multi-byte UTF-8 codepoints get cut in half, so
14
+ * xterm desyncs and renders a blank or garbled screen (notably the infrequently
15
+ * repainted bottom status/cost bar). Serializing the live screen grid instead
16
+ * guarantees the viewer always receives one complete, coherent frame regardless
17
+ * of how much history has scrolled past.
18
+ *
19
+ * The output is a Unicode string (cell chars are decoded) — callers MUST encode
20
+ * it as UTF-8 on the wire, not latin1.
21
+ */
22
+ export declare function serializeScreen(term: Terminal): string;
23
+ //# sourceMappingURL=pty-serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-serialize.d.ts","sourceRoot":"","sources":["../../src/shell/pty-serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAqDtD"}
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializeScreen = serializeScreen;
4
+ /**
5
+ * Serialize a headless terminal's CURRENT visible screen into a self-contained
6
+ * sequence of escape codes that, written into a fresh xterm, reproduces the same
7
+ * screen exactly.
8
+ *
9
+ * Why this exists: the PTY stream registry used to replay the last N KB of RAW
10
+ * bytes on every (re)connect. An alt-screen TUI (Claude Code runs in the
11
+ * alternate buffer) cannot be reconstructed from a truncated byte tail — the
12
+ * visible screen is the *cumulative* result of every byte since session start.
13
+ * Once output exceeds the ring-buffer cap (which happens fast during an active
14
+ * turn), the front is trimmed mid-sequence: the alt-screen-enter, scroll-region
15
+ * setup, cursor moves, and even multi-byte UTF-8 codepoints get cut in half, so
16
+ * xterm desyncs and renders a blank or garbled screen (notably the infrequently
17
+ * repainted bottom status/cost bar). Serializing the live screen grid instead
18
+ * guarantees the viewer always receives one complete, coherent frame regardless
19
+ * of how much history has scrolled past.
20
+ *
21
+ * The output is a Unicode string (cell chars are decoded) — callers MUST encode
22
+ * it as UTF-8 on the wire, not latin1.
23
+ */
24
+ function serializeScreen(term) {
25
+ const buf = term.buffer.active;
26
+ const cols = term.cols;
27
+ const rows = term.rows;
28
+ let out = '\x1b[0m';
29
+ // Match the server's buffer mode so a later '?1049l' (session exit) behaves
30
+ // correctly on the client; alt-screen also clears its own scrollback.
31
+ if (buf.type === 'alternate')
32
+ out += '\x1b[?1049h';
33
+ out += '\x1b[2J';
34
+ let cell;
35
+ for (let y = 0; y < rows; y++) {
36
+ const line = buf.getLine(buf.viewportY + y);
37
+ out += `\x1b[${y + 1};1H`;
38
+ if (!line)
39
+ continue;
40
+ // Find the last column that paints something visible. The screen was already
41
+ // cleared by '\x1b[2J', so trailing blanks need not be emitted — this keeps
42
+ // null cells null (faithful reconstruction) and shrinks the frame a lot.
43
+ let last = -1;
44
+ for (let x = cols - 1; x >= 0; x--) {
45
+ cell = line.getCell(x, cell);
46
+ if (cell && isVisible(cell)) {
47
+ last = x;
48
+ break;
49
+ }
50
+ }
51
+ if (last < 0)
52
+ continue;
53
+ let curSgr = '';
54
+ for (let x = 0; x <= last; x++) {
55
+ cell = line.getCell(x, cell);
56
+ if (!cell) {
57
+ out += ' ';
58
+ continue;
59
+ }
60
+ // Width 0 = the spacer cell that follows a wide (CJK/emoji) glyph; the
61
+ // glyph itself already emitted its full width, so skip the spacer.
62
+ if (cell.getWidth() === 0)
63
+ continue;
64
+ const sgr = sgrFor(cell);
65
+ if (sgr !== curSgr) {
66
+ // Reset first so attributes never leak from the previous cell.
67
+ out += '\x1b[0m' + sgr;
68
+ curSgr = sgr;
69
+ }
70
+ const chars = cell.getChars();
71
+ out += chars === '' ? ' ' : chars;
72
+ }
73
+ out += '\x1b[0m';
74
+ }
75
+ // Restore the cursor to where the live screen has it (1-based, viewport-relative).
76
+ out += `\x1b[${buf.cursorY + 1};${buf.cursorX + 1}H`;
77
+ return out;
78
+ }
79
+ /**
80
+ * Whether a cell paints anything the eye can see. A space with default colors is
81
+ * invisible (so it can be trimmed from a cleared screen), but a space with a
82
+ * background color, or under inverse video (which swaps fg/bg), is visible.
83
+ */
84
+ function isVisible(cell) {
85
+ const ch = cell.getChars();
86
+ if (ch !== '' && ch !== ' ')
87
+ return true;
88
+ if (!cell.isBgDefault())
89
+ return true;
90
+ if (cell.isInverse())
91
+ return true;
92
+ return false;
93
+ }
94
+ /** Build the SGR (Select Graphic Rendition) escape for a single cell's attributes. */
95
+ function sgrFor(cell) {
96
+ const codes = [];
97
+ if (cell.isBold())
98
+ codes.push(1);
99
+ if (cell.isDim())
100
+ codes.push(2);
101
+ if (cell.isItalic())
102
+ codes.push(3);
103
+ if (cell.isUnderline())
104
+ codes.push(4);
105
+ if (cell.isBlink())
106
+ codes.push(5);
107
+ if (cell.isInverse())
108
+ codes.push(7);
109
+ if (cell.isInvisible())
110
+ codes.push(8);
111
+ if (cell.isStrikethrough())
112
+ codes.push(9);
113
+ if (cell.isOverline())
114
+ codes.push(53);
115
+ if (cell.isFgRGB()) {
116
+ const c = cell.getFgColor();
117
+ codes.push(38, 2, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
118
+ }
119
+ else if (cell.isFgPalette()) {
120
+ codes.push(38, 5, cell.getFgColor());
121
+ }
122
+ if (cell.isBgRGB()) {
123
+ const c = cell.getBgColor();
124
+ codes.push(48, 2, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
125
+ }
126
+ else if (cell.isBgPalette()) {
127
+ codes.push(48, 5, cell.getBgColor());
128
+ }
129
+ return codes.length ? `\x1b[${codes.join(';')}m` : '';
130
+ }
131
+ //# sourceMappingURL=pty-serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-serialize.js","sourceRoot":"","sources":["../../src/shell/pty-serialize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { WebSocket } from 'ws';
2
+ /**
3
+ * Tracks live PTY output streams so the dashboard can mirror a running shell.
4
+ *
5
+ * Everything is keyed by `streamKey` — the per-session id, NOT the agent id. A
6
+ * single agent can run several concurrent sessions (e.g. a Telegram session and
7
+ * an API/cron session), each its own PTY with its own screen. Keying by agent
8
+ * id would merge them: both sessions would feed one shared mirror (interleaved,
9
+ * unreadable output) and the viewer could not target a specific session. Keying
10
+ * by session id keeps each stream isolated.
11
+ */
12
+ export declare class PtyStreamRegistry {
13
+ private readonly clients;
14
+ private readonly servers;
15
+ private readonly streamSockets;
16
+ /**
17
+ * Headless terminal mirror per stream. Fed every PTY byte so it always holds
18
+ * the session's current screen grid; on subscribe we serialize this into one
19
+ * complete frame instead of replaying a (lossy, truncatable) raw-byte tail.
20
+ */
21
+ private readonly screens;
22
+ /** Per-session unix socket path. Derived from the session id so each session gets its own stream. */
23
+ socketPath(streamKey: string): string;
24
+ listen(streamKey: string, socketPath: string): void;
25
+ close(socketPath: string): void;
26
+ /** Returns true if at least one active PTY socket server is registered for this stream/session. */
27
+ hasSockets(streamKey: string): boolean;
28
+ /**
29
+ * Return the current visible screen as plain text — ANSI escape codes stripped,
30
+ * trailing blank lines removed. Each screen row becomes one line in the output.
31
+ * Returns null if no headless mirror exists for the given stream key (session
32
+ * not running in PTY mode, or not yet started).
33
+ *
34
+ * Intended for agents that need to observe what is currently displayed in the
35
+ * PTY shell (e.g. to detect hang states, unexpected prompts, or tool output)
36
+ * without having to parse ANSI/VT100 sequences.
37
+ *
38
+ * Async: flushes the terminal's write queue (same as subscribe does for the
39
+ * serialized frame) so the snapshot reflects every byte received so far.
40
+ */
41
+ screenText(streamKey: string): Promise<{
42
+ text: string;
43
+ cursorRow: number;
44
+ cursorCol: number;
45
+ cols: number;
46
+ rows: number;
47
+ } | null>;
48
+ subscribe(streamKey: string, ws: WebSocket): void;
49
+ unsubscribe(streamKey: string, ws: WebSocket): void;
50
+ broadcast(streamKey: string, data: string): void;
51
+ /** Feed raw PTY bytes into the stream's headless mirror, creating it on first use. */
52
+ private feedScreen;
53
+ /** Start a clean mirror for a fresh session, disposing any prior one. */
54
+ private resetScreen;
55
+ private disposeScreen;
56
+ private createTerm;
57
+ }
58
+ export declare const ptyStreamRegistry: PtyStreamRegistry;
59
+ //# sourceMappingURL=pty-stream-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-stream-registry.d.ts","sourceRoot":"","sources":["../../src/shell/pty-stream-registry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAS/B;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IAEvD,qGAAqG;IACrG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAOrC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAoBnD,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAe/B,mGAAmG;IACnG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAoDvI,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI;IAqBjD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI;IAOnD,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAYhD,sFAAsF;IACtF,OAAO,CAAC,UAAU;IAalB,yEAAyE;IACzE,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,UAAU;CAKnB;AAED,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}