@a1hvdy/cc-openclaw 0.24.0 → 0.25.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.
Files changed (96) hide show
  1. package/dist/src/channels/telegram-mirror/burst-accumulator.d.ts +96 -0
  2. package/dist/src/channels/telegram-mirror/burst-accumulator.js +130 -0
  3. package/dist/src/channels/telegram-mirror/burst-accumulator.js.map +1 -0
  4. package/dist/src/channels/telegram-mirror/callback-mapping.d.ts +61 -0
  5. package/dist/src/channels/telegram-mirror/callback-mapping.js +99 -0
  6. package/dist/src/channels/telegram-mirror/callback-mapping.js.map +1 -0
  7. package/dist/src/channels/telegram-mirror/card-renderer.d.ts +74 -0
  8. package/dist/src/channels/telegram-mirror/card-renderer.js +131 -0
  9. package/dist/src/channels/telegram-mirror/card-renderer.js.map +1 -0
  10. package/dist/src/channels/telegram-mirror/commands.d.ts +142 -0
  11. package/dist/src/channels/telegram-mirror/commands.js +389 -0
  12. package/dist/src/channels/telegram-mirror/commands.js.map +1 -0
  13. package/dist/src/channels/telegram-mirror/compose-buffer.d.ts +71 -0
  14. package/dist/src/channels/telegram-mirror/compose-buffer.js +110 -0
  15. package/dist/src/channels/telegram-mirror/compose-buffer.js.map +1 -0
  16. package/dist/src/channels/telegram-mirror/cost-views.d.ts +58 -0
  17. package/dist/src/channels/telegram-mirror/cost-views.js +121 -0
  18. package/dist/src/channels/telegram-mirror/cost-views.js.map +1 -0
  19. package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.d.ts +21 -0
  20. package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.js +30 -0
  21. package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.js.map +1 -0
  22. package/dist/src/channels/telegram-mirror/failure/gateway-down.d.ts +15 -0
  23. package/dist/src/channels/telegram-mirror/failure/gateway-down.js +27 -0
  24. package/dist/src/channels/telegram-mirror/failure/gateway-down.js.map +1 -0
  25. package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.d.ts +15 -0
  26. package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.js +27 -0
  27. package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.js.map +1 -0
  28. package/dist/src/channels/telegram-mirror/failure/index.d.ts +23 -0
  29. package/dist/src/channels/telegram-mirror/failure/index.js +35 -0
  30. package/dist/src/channels/telegram-mirror/failure/index.js.map +1 -0
  31. package/dist/src/channels/telegram-mirror/failure/model-5xx.d.ts +16 -0
  32. package/dist/src/channels/telegram-mirror/failure/model-5xx.js +24 -0
  33. package/dist/src/channels/telegram-mirror/failure/model-5xx.js.map +1 -0
  34. package/dist/src/channels/telegram-mirror/failure/network-blip.d.ts +17 -0
  35. package/dist/src/channels/telegram-mirror/failure/network-blip.js +25 -0
  36. package/dist/src/channels/telegram-mirror/failure/network-blip.js.map +1 -0
  37. package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.d.ts +15 -0
  38. package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.js +24 -0
  39. package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.js.map +1 -0
  40. package/dist/src/channels/telegram-mirror/failure/rate-limit.d.ts +16 -0
  41. package/dist/src/channels/telegram-mirror/failure/rate-limit.js +26 -0
  42. package/dist/src/channels/telegram-mirror/failure/rate-limit.js.map +1 -0
  43. package/dist/src/channels/telegram-mirror/failure/returning-after-24h.d.ts +14 -0
  44. package/dist/src/channels/telegram-mirror/failure/returning-after-24h.js +33 -0
  45. package/dist/src/channels/telegram-mirror/failure/returning-after-24h.js.map +1 -0
  46. package/dist/src/channels/telegram-mirror/failure/types.d.ts +30 -0
  47. package/dist/src/channels/telegram-mirror/failure/types.js +9 -0
  48. package/dist/src/channels/telegram-mirror/failure/types.js.map +1 -0
  49. package/dist/src/channels/telegram-mirror/inbound-handler.d.ts +52 -0
  50. package/dist/src/channels/telegram-mirror/inbound-handler.js +115 -0
  51. package/dist/src/channels/telegram-mirror/inbound-handler.js.map +1 -0
  52. package/dist/src/channels/telegram-mirror/index.d.ts +30 -0
  53. package/dist/src/channels/telegram-mirror/index.js +49 -0
  54. package/dist/src/channels/telegram-mirror/index.js.map +1 -0
  55. package/dist/src/channels/telegram-mirror/plan-attachment.d.ts +120 -0
  56. package/dist/src/channels/telegram-mirror/plan-attachment.js +138 -0
  57. package/dist/src/channels/telegram-mirror/plan-attachment.js.map +1 -0
  58. package/dist/src/channels/telegram-mirror/quota-reader.d.ts +41 -0
  59. package/dist/src/channels/telegram-mirror/quota-reader.js +29 -0
  60. package/dist/src/channels/telegram-mirror/quota-reader.js.map +1 -0
  61. package/dist/src/channels/telegram-mirror/sessions-keyboard.d.ts +84 -0
  62. package/dist/src/channels/telegram-mirror/sessions-keyboard.js +113 -0
  63. package/dist/src/channels/telegram-mirror/sessions-keyboard.js.map +1 -0
  64. package/dist/src/channels/telegram-mirror/soak-log.d.ts +98 -0
  65. package/dist/src/channels/telegram-mirror/soak-log.js +136 -0
  66. package/dist/src/channels/telegram-mirror/soak-log.js.map +1 -0
  67. package/dist/src/channels/telegram-mirror/state-machine.d.ts +102 -0
  68. package/dist/src/channels/telegram-mirror/state-machine.js +117 -0
  69. package/dist/src/channels/telegram-mirror/state-machine.js.map +1 -0
  70. package/dist/src/channels/telegram-mirror/sync-commands.d.ts +63 -0
  71. package/dist/src/channels/telegram-mirror/sync-commands.js +51 -0
  72. package/dist/src/channels/telegram-mirror/sync-commands.js.map +1 -0
  73. package/dist/src/channels/telegram-mirror/threshold-watcher.d.ts +54 -0
  74. package/dist/src/channels/telegram-mirror/threshold-watcher.js +77 -0
  75. package/dist/src/channels/telegram-mirror/threshold-watcher.js.map +1 -0
  76. package/dist/src/command-router/cc-handler.js +1 -1
  77. package/dist/src/command-router/cc-handler.js.map +1 -1
  78. package/dist/src/index.js +12 -8
  79. package/dist/src/index.js.map +1 -1
  80. package/dist/src/lib/auto-recovery.js +1 -1
  81. package/dist/src/lib/auto-recovery.js.map +1 -1
  82. package/dist/src/lib/drift-detector.js +1 -1
  83. package/dist/src/lib/drift-detector.js.map +1 -1
  84. package/dist/src/lib/error-renderer.d.ts +23 -0
  85. package/dist/src/lib/error-renderer.js +106 -0
  86. package/dist/src/lib/error-renderer.js.map +1 -0
  87. package/dist/src/lib/perf/speculative-bubble.d.ts +27 -0
  88. package/dist/src/lib/perf/speculative-bubble.js +36 -0
  89. package/dist/src/lib/perf/speculative-bubble.js.map +1 -0
  90. package/dist/src/lib/session-registry.d.ts +66 -0
  91. package/dist/src/lib/session-registry.js +188 -0
  92. package/dist/src/lib/session-registry.js.map +1 -0
  93. package/dist/src/lib/telegram-bot-api.d.ts +100 -0
  94. package/dist/src/lib/telegram-bot-api.js +204 -0
  95. package/dist/src/lib/telegram-bot-api.js.map +1 -0
  96. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ /**
2
+ * failure/pool-exhausted-fallback.ts — v0.25.0 M12.
3
+ *
4
+ * Mode: pool-exhausted-fallback. The SubprocessPool (v0.16+ / M1
5
+ * v0.24.0) is saturated — incoming turn falls back to direct CLI spawn.
6
+ * The user surface is informational only; the engine completes the turn
7
+ * regardless. Tracked so soak instrumentation can flag if it fires often.
8
+ */
9
+ export const META = {
10
+ mode: 'pool-exhausted-fallback',
11
+ glyph: 'ℹ️',
12
+ severity: 'info',
13
+ };
14
+ export const handle = (ctx, info) => {
15
+ const sz = info?.poolSize && Number.isFinite(info.poolSize) ? ` (${info.poolSize} slots in use)` : '';
16
+ return [
17
+ {
18
+ type: 'sendMessage',
19
+ chat_id: ctx.chatId,
20
+ text: `${META.glyph} Subprocess pool full${sz} — running this turn via direct CLI.`,
21
+ },
22
+ ];
23
+ };
24
+ //# sourceMappingURL=pool-exhausted-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool-exhausted-fallback.js","sourceRoot":"","sources":["../../../../../src/channels/telegram-mirror/failure/pool-exhausted-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,CAAC,MAAM,IAAI,GAAsB;IACrC,IAAI,EAAE,yBAAyB;IAC/B,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,MAAM;CACjB,CAAC;AAOF,MAAM,CAAC,MAAM,MAAM,GAAsC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACrE,MAAM,EAAE,GAAG,IAAI,EAAE,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,QAAQ,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;IACtG,OAAO;QACL;YACE,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,GAAG,CAAC,MAAM;YACnB,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,wBAAwB,EAAE,sCAAsC;SACpF;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * failure/rate-limit.ts — v0.25.0 M12.
3
+ *
4
+ * Mode: rate-limit. Telegram bot API 429 or Anthropic 429 hit. Caller
5
+ * supplies the upstream retry-after hint when known; default surface is
6
+ * a transparent back-off advisory without prompting user action.
7
+ */
8
+ import type { FailureHandler, FailureModuleMeta } from './types.js';
9
+ export declare const META: FailureModuleMeta;
10
+ export interface RateLimitInfo {
11
+ /** Source of the limit — "telegram" or "anthropic". */
12
+ source?: 'telegram' | 'anthropic';
13
+ /** Server-advertised retry-after seconds, when available. */
14
+ retryAfterSec?: number;
15
+ }
16
+ export declare const handle: FailureHandler<RateLimitInfo>;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * failure/rate-limit.ts — v0.25.0 M12.
3
+ *
4
+ * Mode: rate-limit. Telegram bot API 429 or Anthropic 429 hit. Caller
5
+ * supplies the upstream retry-after hint when known; default surface is
6
+ * a transparent back-off advisory without prompting user action.
7
+ */
8
+ export const META = {
9
+ mode: 'rate-limit',
10
+ glyph: '🟧',
11
+ severity: 'warn',
12
+ };
13
+ export const handle = (ctx, info) => {
14
+ const src = info?.source ?? 'upstream';
15
+ const after = info?.retryAfterSec && Number.isFinite(info.retryAfterSec)
16
+ ? ` Retrying in ${info.retryAfterSec}s.`
17
+ : '';
18
+ return [
19
+ {
20
+ type: 'sendMessage',
21
+ chat_id: ctx.chatId,
22
+ text: `${META.glyph} Rate-limited by ${src}.${after} Auto-backing off.`,
23
+ },
24
+ ];
25
+ };
26
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../../../../src/channels/telegram-mirror/failure/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,CAAC,MAAM,IAAI,GAAsB;IACrC,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,MAAM;CACjB,CAAC;AASF,MAAM,CAAC,MAAM,MAAM,GAAkC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACjE,MAAM,GAAG,GAAG,IAAI,EAAE,MAAM,IAAI,UAAU,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,EAAE,aAAa,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC;QACtE,CAAC,CAAC,gBAAgB,IAAI,CAAC,aAAa,IAAI;QACxC,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;QACL;YACE,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,GAAG,CAAC,MAAM;YACnB,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,oBAAoB,GAAG,IAAI,KAAK,oBAAoB;SACxE;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * failure/returning-after-24h.ts — v0.25.0 M12.
3
+ *
4
+ * Mode: returning-after-24h. The chat last had inbound activity more
5
+ * than 24 hours ago. Surfaces a friendly welcome-back + status hint so
6
+ * the user can decide whether to resume vs start fresh.
7
+ */
8
+ import type { FailureHandler, FailureModuleMeta } from './types.js';
9
+ export declare const META: FailureModuleMeta;
10
+ export interface ReturningAfter24hInfo {
11
+ /** Hours since the last inbound activity, rounded to nearest integer. */
12
+ hoursAgo?: number;
13
+ }
14
+ export declare const handle: FailureHandler<ReturningAfter24hInfo>;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * failure/returning-after-24h.ts — v0.25.0 M12.
3
+ *
4
+ * Mode: returning-after-24h. The chat last had inbound activity more
5
+ * than 24 hours ago. Surfaces a friendly welcome-back + status hint so
6
+ * the user can decide whether to resume vs start fresh.
7
+ */
8
+ export const META = {
9
+ mode: 'returning-after-24h',
10
+ glyph: '👋',
11
+ severity: 'info',
12
+ };
13
+ function formatHoursAgo(h) {
14
+ if (h >= 168)
15
+ return `${Math.floor(h / 168)}w ago`;
16
+ if (h >= 24)
17
+ return `${Math.floor(h / 24)}d ago`;
18
+ return `${h}h ago`;
19
+ }
20
+ export const handle = (ctx, info) => {
21
+ const ago = info?.hoursAgo && Number.isFinite(info.hoursAgo)
22
+ ? formatHoursAgo(info.hoursAgo)
23
+ : '24h+ ago';
24
+ return [
25
+ {
26
+ type: 'sendMessage',
27
+ chat_id: ctx.chatId,
28
+ text: `${META.glyph} Welcome back — last activity ${ago}. ` +
29
+ `Type /status to inspect sessions or /sessions to switch.`,
30
+ },
31
+ ];
32
+ };
33
+ //# sourceMappingURL=returning-after-24h.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"returning-after-24h.js","sourceRoot":"","sources":["../../../../../src/channels/telegram-mirror/failure/returning-after-24h.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,CAAC,MAAM,IAAI,GAAsB;IACrC,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,MAAM;CACjB,CAAC;AAOF,SAAS,cAAc,CAAC,CAAS;IAC/B,IAAI,CAAC,IAAI,GAAG;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;IACnD,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;IACjD,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAA0C,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACzE,MAAM,GAAG,GACP,IAAI,EAAE,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC9C,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC/B,CAAC,CAAC,UAAU,CAAC;IACjB,OAAO;QACL;YACE,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,GAAG,CAAC,MAAM;YACnB,IAAI,EACF,GAAG,IAAI,CAAC,KAAK,iCAAiC,GAAG,IAAI;gBACrD,0DAA0D;SAC7D;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * src/channels/telegram-mirror/failure/types.ts — v0.25.0 M12 shared types.
3
+ *
4
+ * Each failure mode lives in its own file per ADR-008 ("per-mode handlers
5
+ * make each response shape testable and grep-able"). This file holds the
6
+ * shared types so the 8 handler modules stay tight (each ~30-50 lines).
7
+ */
8
+ import type { TelegramAction } from '../commands.js';
9
+ export interface FailureContext {
10
+ chatId: string | number;
11
+ }
12
+ export type Severity = 'info' | 'warn' | 'error';
13
+ /**
14
+ * Shape every failure handler exports. Handlers are pure functions:
15
+ * inputs are the chat context + an optional info payload; output is the
16
+ * array of TelegramAction the dispatch layer applies.
17
+ */
18
+ export type FailureHandler<I = unknown> = (ctx: FailureContext, info?: I) => TelegramAction[];
19
+ /**
20
+ * Registry-side metadata per failure module. Asserted by M13's chaos
21
+ * test against the file name + handler shape.
22
+ */
23
+ export interface FailureModuleMeta {
24
+ /** Mode id, matches the file name without the .ts extension. */
25
+ mode: string;
26
+ /** Glyph that prefixes every emitted message for this mode. */
27
+ glyph: string;
28
+ /** Severity classification used by the chaos test + observability. */
29
+ severity: Severity;
30
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * src/channels/telegram-mirror/failure/types.ts — v0.25.0 M12 shared types.
3
+ *
4
+ * Each failure mode lives in its own file per ADR-008 ("per-mode handlers
5
+ * make each response shape testable and grep-able"). This file holds the
6
+ * shared types so the 8 handler modules stay tight (each ~30-50 lines).
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/channels/telegram-mirror/failure/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * src/channels/telegram-mirror/inbound-handler.ts — v0.25.1 wire-up.
3
+ *
4
+ * The piece that was missing from v0.25.0: subscribes to OpenClaw's
5
+ * `before_dispatch` event, identifies mirror slash commands, routes
6
+ * through `dispatchCommand` (from commands.ts), and forwards the
7
+ * resulting TelegramAction list out via `telegram-bot-api`'s sendTg.
8
+ *
9
+ * Returns `{ handled: true }` on dispatch so OpenClaw's default model
10
+ * route (or any built-in command handler) doesn't ALSO respond — the
11
+ * legacy event-reducer's `/quota` / `/sessions` handlers used the same
12
+ * claim shape.
13
+ *
14
+ * Out-of-scope for v0.25.1 (deferred to v0.25.2+):
15
+ * • Tool/text/turn stream-event subscription → mirror-card render.
16
+ * • callback_query handling for inline-keyboard taps.
17
+ * • Burst-accumulator + compose-buffer engine injection (the
18
+ * ComposeBuffer + BurstAccumulator capture user text but the
19
+ * concatenated payload from /send still needs to reach the engine
20
+ * as a synthetic user message — that's a separate dispatch path).
21
+ *
22
+ * Single shared CallbackMap + ComposeBuffer per process so callback
23
+ * resolution and compose state survive across handler invocations.
24
+ */
25
+ import { CallbackMap } from './callback-mapping.js';
26
+ import { ComposeBuffer } from './compose-buffer.js';
27
+ export interface InboundHandlerApi {
28
+ on(event: string, handler: (...args: unknown[]) => unknown | Promise<unknown>): void;
29
+ logger?: {
30
+ info: (msg: string) => void;
31
+ warn: (msg: string) => void;
32
+ };
33
+ }
34
+ /**
35
+ * Internal handler state — exported only for tests so they can inspect
36
+ * compose-buffer state + callback-map without going through Telegram.
37
+ */
38
+ export interface HandlerState {
39
+ callbackMap: CallbackMap;
40
+ composeBuffer: ComposeBuffer;
41
+ }
42
+ export declare function createHandlerState(): HandlerState;
43
+ /**
44
+ * Register the inbound dispatcher. Called by mirror's register() at boot.
45
+ *
46
+ * Hooked to `before_dispatch` — OpenClaw's name for the inbound-message
47
+ * event surface. We return `{ handled: true }` to claim the message when
48
+ * it's a known mirror slash command; `undefined` lets the event proceed
49
+ * to OpenClaw's default routing (which is what we want for non-slash
50
+ * user text destined for the model).
51
+ */
52
+ export declare function registerInboundHandler(api: InboundHandlerApi, state?: HandlerState): HandlerState;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * src/channels/telegram-mirror/inbound-handler.ts — v0.25.1 wire-up.
3
+ *
4
+ * The piece that was missing from v0.25.0: subscribes to OpenClaw's
5
+ * `before_dispatch` event, identifies mirror slash commands, routes
6
+ * through `dispatchCommand` (from commands.ts), and forwards the
7
+ * resulting TelegramAction list out via `telegram-bot-api`'s sendTg.
8
+ *
9
+ * Returns `{ handled: true }` on dispatch so OpenClaw's default model
10
+ * route (or any built-in command handler) doesn't ALSO respond — the
11
+ * legacy event-reducer's `/quota` / `/sessions` handlers used the same
12
+ * claim shape.
13
+ *
14
+ * Out-of-scope for v0.25.1 (deferred to v0.25.2+):
15
+ * • Tool/text/turn stream-event subscription → mirror-card render.
16
+ * • callback_query handling for inline-keyboard taps.
17
+ * • Burst-accumulator + compose-buffer engine injection (the
18
+ * ComposeBuffer + BurstAccumulator capture user text but the
19
+ * concatenated payload from /send still needs to reach the engine
20
+ * as a synthetic user message — that's a separate dispatch path).
21
+ *
22
+ * Single shared CallbackMap + ComposeBuffer per process so callback
23
+ * resolution and compose state survive across handler invocations.
24
+ */
25
+ import { dispatchCommand, parseSlash, COMMAND_HANDLERS } from './commands.js';
26
+ import { sendTg, editTg } from '../../lib/telegram-bot-api.js';
27
+ import { CallbackMap } from './callback-mapping.js';
28
+ import { ComposeBuffer } from './compose-buffer.js';
29
+ const PLUGIN_TAG = '[cc-openclaw/telegram-mirror/inbound]';
30
+ export function createHandlerState() {
31
+ return {
32
+ callbackMap: new CallbackMap(),
33
+ composeBuffer: new ComposeBuffer(),
34
+ };
35
+ }
36
+ /**
37
+ * The set of slash commands the mirror claims. Names match what
38
+ * dispatchCommand recognises (commands.ts COMMAND_HANDLERS keys).
39
+ */
40
+ const MIRROR_COMMANDS = new Set(Object.keys(COMMAND_HANDLERS));
41
+ /**
42
+ * Forward a single TelegramAction to the actual Telegram API. Returns
43
+ * the API response (or {ok:false} on failure). Pure I/O — no state
44
+ * mutation.
45
+ *
46
+ * editMessageText and sendDocument variants land in v0.25.2 when the
47
+ * render pipeline and plan-attachment dispatch wire up.
48
+ */
49
+ async function forwardAction(action, threadId, logger) {
50
+ try {
51
+ if (action.type === 'sendMessage') {
52
+ await sendTg(String(action.chat_id), action.text, threadId !== undefined ? String(threadId) : undefined, action.reply_markup);
53
+ return;
54
+ }
55
+ if (action.type === 'editMessageText') {
56
+ await editTg(String(action.chat_id), action.message_id, action.text, action.reply_markup);
57
+ return;
58
+ }
59
+ // sendDocument: v0.25.2 — plan-mode attachment wire-up.
60
+ logger.warn(`${PLUGIN_TAG} action type "${action.type}" not yet forwarded (deferred to v0.25.2)`);
61
+ }
62
+ catch (err) {
63
+ logger.warn(`${PLUGIN_TAG} forwardAction failed: ${err.message}`);
64
+ }
65
+ }
66
+ /**
67
+ * Register the inbound dispatcher. Called by mirror's register() at boot.
68
+ *
69
+ * Hooked to `before_dispatch` — OpenClaw's name for the inbound-message
70
+ * event surface. We return `{ handled: true }` to claim the message when
71
+ * it's a known mirror slash command; `undefined` lets the event proceed
72
+ * to OpenClaw's default routing (which is what we want for non-slash
73
+ * user text destined for the model).
74
+ */
75
+ export function registerInboundHandler(api, state = createHandlerState()) {
76
+ const logger = (api.logger ?? console);
77
+ api.on('before_dispatch', async (...args) => {
78
+ const event = args[0];
79
+ if (!event)
80
+ return undefined;
81
+ if (event.channel !== 'telegram')
82
+ return undefined;
83
+ // callback_query rides on `before_dispatch` too — deferred to v0.25.2.
84
+ if (event.raw?.callback_query)
85
+ return undefined;
86
+ const text = event.raw?.message?.text;
87
+ if (typeof text !== 'string')
88
+ return undefined;
89
+ const parsed = parseSlash(text);
90
+ if (!parsed)
91
+ return undefined;
92
+ if (!MIRROR_COMMANDS.has(parsed.cmd))
93
+ return undefined;
94
+ const chatRaw = event.raw?.message?.chat?.id;
95
+ const chatId = chatRaw !== undefined && chatRaw !== null ? String(chatRaw) : '';
96
+ if (!chatId)
97
+ return undefined;
98
+ const threadId = event.raw?.message?.message_thread_id;
99
+ const result = dispatchCommand(parsed, {
100
+ chatId,
101
+ callbackMap: state.callbackMap,
102
+ composeBuffer: state.composeBuffer,
103
+ });
104
+ if (!result)
105
+ return undefined;
106
+ for (const action of result.actions) {
107
+ await forwardAction(action, threadId, logger);
108
+ }
109
+ logger.info(`${PLUGIN_TAG} dispatched /${parsed.cmd}${parsed.args.length ? ' ' + parsed.args.join(' ') : ''} (chat=${chatId}, actions=${result.actions.length})`);
110
+ return { handled: true };
111
+ });
112
+ logger.info(`${PLUGIN_TAG} subscribed to before_dispatch — ${MIRROR_COMMANDS.size} commands armed.`);
113
+ return state;
114
+ }
115
+ //# sourceMappingURL=inbound-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound-handler.js","sourceRoot":"","sources":["../../../../src/channels/telegram-mirror/inbound-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * src/channels/telegram-mirror/index.ts — v0.25.0 Telegram Terminal Mirror entry.
3
+ *
4
+ * Parallel implementation per ADR-002. The legacy 14-module live card at
5
+ * src/channels/telegram/ is read-only observability; this mirror replaces it
6
+ * with a Telegram-native surface (sessions/plan/cost/compose/etc.) so A1 can
7
+ * drive cc-openclaw from phone without opening the laptop.
8
+ *
9
+ * M0 milestone (this file): empty register() stub gated by
10
+ * CC_OPENCLAW_TERMINAL_MIRROR=1. Subsequent milestones (M1..M16) wire each
11
+ * subsystem in dependency order. The flag picks which channel barrel loads
12
+ * at boot — legacy and mirror are mutually exclusive (no double-listener
13
+ * collisions). Roadmap + ADRs live at
14
+ * /home/a1xai/dev/cc-openclaw/PRPs/v0.25.0-telegram-terminal-mirror.md.
15
+ *
16
+ * ADRs touched at M0: ADR-002 (parallel impl, not in-place).
17
+ * Risks monitored: R-7 (parallel maintenance burden — re-evaluate at soak start).
18
+ */
19
+ export interface PluginApi {
20
+ on(event: string, handler: (...args: unknown[]) => unknown | Promise<unknown>): void;
21
+ logger?: Console;
22
+ }
23
+ /**
24
+ * Mirror channel register — idempotent via defaultRegisterGuard.
25
+ *
26
+ * M0: empty stub. No api.on() wiring yet; subsequent milestones add handlers.
27
+ * The stub still completes successfully so M0's exit criterion ("Plugin boot
28
+ * succeeds in both flag states") holds end-to-end.
29
+ */
30
+ export declare function register(api: PluginApi): void;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * src/channels/telegram-mirror/index.ts — v0.25.0 Telegram Terminal Mirror entry.
3
+ *
4
+ * Parallel implementation per ADR-002. The legacy 14-module live card at
5
+ * src/channels/telegram/ is read-only observability; this mirror replaces it
6
+ * with a Telegram-native surface (sessions/plan/cost/compose/etc.) so A1 can
7
+ * drive cc-openclaw from phone without opening the laptop.
8
+ *
9
+ * M0 milestone (this file): empty register() stub gated by
10
+ * CC_OPENCLAW_TERMINAL_MIRROR=1. Subsequent milestones (M1..M16) wire each
11
+ * subsystem in dependency order. The flag picks which channel barrel loads
12
+ * at boot — legacy and mirror are mutually exclusive (no double-listener
13
+ * collisions). Roadmap + ADRs live at
14
+ * /home/a1xai/dev/cc-openclaw/PRPs/v0.25.0-telegram-terminal-mirror.md.
15
+ *
16
+ * ADRs touched at M0: ADR-002 (parallel impl, not in-place).
17
+ * Risks monitored: R-7 (parallel maintenance burden — re-evaluate at soak start).
18
+ */
19
+ import { defaultRegisterGuard } from '../../lib/register-guard.js';
20
+ import { initBotTokenFromConfig } from '../../lib/telegram-bot-api.js';
21
+ import { registerInboundHandler } from './inbound-handler.js';
22
+ /**
23
+ * Mirror channel register — idempotent via defaultRegisterGuard.
24
+ *
25
+ * M0: empty stub. No api.on() wiring yet; subsequent milestones add handlers.
26
+ * The stub still completes successfully so M0's exit criterion ("Plugin boot
27
+ * succeeds in both flag states") holds end-to-end.
28
+ */
29
+ export function register(api) {
30
+ defaultRegisterGuard.guard('cc-openclaw/telegram-mirror', api, () => {
31
+ const logger = api.logger || console;
32
+ logger.info('[cc-openclaw/telegram-mirror] register() — wiring inbound dispatcher (v0.25.1).');
33
+ // v0.25.0 M16 — initialise Telegram bot token (sourcing migrated
34
+ // out of the legacy live-card module into src/lib/telegram-bot-api).
35
+ // Same precedence as before: api.config first, then ~/.openclaw/openclaw.json.
36
+ initBotTokenFromConfig({
37
+ config: api.config,
38
+ logger: logger,
39
+ });
40
+ // v0.25.1 — subscribe before_dispatch and route mirror slash commands
41
+ // through dispatchCommand → telegram-bot-api. Without this, v0.25.0's
42
+ // handlers were unreachable from the live Telegram path.
43
+ registerInboundHandler({
44
+ on: api.on.bind(api),
45
+ logger: logger,
46
+ });
47
+ });
48
+ }
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/channels/telegram-mirror/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9D;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAc;IACrC,oBAAoB,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC;QACrC,MAAM,CAAC,IAAI,CACT,iFAAiF,CAClF,CAAC;QACF,iEAAiE;QACjE,qEAAqE;QACrE,+EAA+E;QAC/E,sBAAsB,CAAC;YACrB,MAAM,EAAG,GAAuD,CAAC,MAAM;YACvE,MAAM,EAAE,MAAiF;SAC1F,CAAC,CAAC;QACH,sEAAsE;QACtE,sEAAsE;QACtE,yDAAyD;QACzD,sBAAsB,CAAC;YACrB,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;YACpB,MAAM,EAAE,MAAiF;SAC1F,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * src/channels/telegram-mirror/plan-attachment.ts — v0.25.0 M9.
3
+ *
4
+ * When Claude emits ExitPlanMode (the plan-mode tool call), the mirror
5
+ * uploads the plan body as a .md document attachment with two inline
6
+ * buttons (Approve / Reject). Decision: ADR-006 — sendDocument over
7
+ * editMessageMedia. Rationale: sendDocument is simpler, has the larger
8
+ * size limit, and matches A1's on-disk plan-file convention.
9
+ *
10
+ * Approve → callback payload `{ action: 'plan-approve', planId }` resolves
11
+ * to model input "approved" which the engine bridge feeds to the running
12
+ * session.
13
+ *
14
+ * Reject → two-phase. The first tap stashes a pending-reject for the chat;
15
+ * the next non-slash inbound message becomes the rejection feedback payload
16
+ * "<plan rejected>\n<feedback text>". A tap-then-tap-Reject again clears
17
+ * the pending state silently.
18
+ *
19
+ * Risk references: R-3 (callback_data 64-byte cap) — payloads here route
20
+ * through CallbackMap exactly like M3's keyboard.
21
+ */
22
+ import type { CallbackMap } from './callback-mapping.js';
23
+ import type { TelegramAction } from './commands.js';
24
+ /**
25
+ * Shape of the ExitPlanMode tool_use event we care about. Mirrors the
26
+ * subset of the upstream stream-event shape — keep narrow so future
27
+ * upstream changes don't ripple here unnecessarily.
28
+ */
29
+ export interface ExitPlanModeEvent {
30
+ /** Tool name; always "ExitPlanMode" for this handler. */
31
+ toolName: string;
32
+ /** Free-form plan body (Markdown). */
33
+ plan: string;
34
+ /** Optional project slug — used in the .md filename. */
35
+ slug?: string;
36
+ /** Optional caption to surface above the document on Telegram (kept short). */
37
+ caption?: string;
38
+ }
39
+ /**
40
+ * Compose the slug-prefixed plan filename. Sanitises non-filename chars
41
+ * so the Telegram upload always succeeds.
42
+ */
43
+ export declare function planFilename(slug: string | undefined): string;
44
+ /**
45
+ * Build a sendDocument action carrying the plan + the Approve/Reject
46
+ * inline keyboard. The callback ids are minted into the supplied
47
+ * CallbackMap; payloads include a fresh planId so multiple in-flight
48
+ * plans on the same chat don't collide.
49
+ */
50
+ export declare function buildPlanAttachment(opts: {
51
+ chatId: string | number;
52
+ event: ExitPlanModeEvent;
53
+ callbackMap: CallbackMap;
54
+ /** Optional planId override for deterministic tests. */
55
+ planId?: string;
56
+ }): {
57
+ action: Extract<TelegramAction, {
58
+ type: 'sendDocument';
59
+ }>;
60
+ planId: string;
61
+ };
62
+ interface RejectPending {
63
+ chatId: string;
64
+ planId: string;
65
+ pendingSince: number;
66
+ }
67
+ export interface RejectFeedbackTrackerOptions {
68
+ /** TTL after which a pending-reject silently expires (default 10 min). */
69
+ ttlMs?: number;
70
+ /** Clock for tests. */
71
+ now?: () => number;
72
+ }
73
+ /**
74
+ * Tracks the (chatId → pending-reject) state for two-phase rejection.
75
+ * The dispatch layer pokes startPending on a Reject tap and consumeFeedback
76
+ * on the next non-slash inbound. Pending-state TTL prevents a half-open
77
+ * rejection from corrupting later turns.
78
+ */
79
+ export declare class RejectFeedbackTracker {
80
+ private readonly pending;
81
+ private readonly ttlMs;
82
+ private readonly now;
83
+ constructor(opts?: RejectFeedbackTrackerOptions);
84
+ /**
85
+ * Mark a chat as having tapped Reject and waiting for feedback. If a
86
+ * prior pending exists, it is replaced (the user re-tapped on a newer plan).
87
+ */
88
+ startPending(chatId: string, planId: string): void;
89
+ /**
90
+ * Returns true when the chat has a non-stale pending-reject.
91
+ */
92
+ isPending(chatId: string): boolean;
93
+ /**
94
+ * Consume the next inbound text as the rejection feedback. Returns a
95
+ * formatted payload that the engine bridge feeds to openai-compat as a
96
+ * single user-message, or undefined when no pending state exists.
97
+ *
98
+ * Payload shape: "<plan rejected: planId>\n<feedback text>" — the
99
+ * "<plan rejected>" sentinel lets the model recognise this as
100
+ * rejection feedback (not a new turn).
101
+ */
102
+ consumeFeedback(chatId: string, feedback: string): string | undefined;
103
+ /**
104
+ * Drop the pending state without consuming feedback (e.g. user taps
105
+ * Reject twice, or types another slash command in the interim).
106
+ */
107
+ cancel(chatId: string): boolean;
108
+ /**
109
+ * Inspect pending without consuming. Filters expired entries.
110
+ */
111
+ peek(chatId: string): RejectPending | undefined;
112
+ clear(): void;
113
+ }
114
+ /**
115
+ * Helper: format the approve payload as engine input. Kept as a function
116
+ * (not a constant) so future variants — e.g. caching a planId reference —
117
+ * have a single place to extend.
118
+ */
119
+ export declare function formatApprovePayload(planId: string): string;
120
+ export {};