@a1hvdy/cc-openclaw 0.29.0 → 0.31.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 (205) hide show
  1. package/dist/src/channels/telegram-mirror/card-renderer.js +9 -5
  2. package/dist/src/channels/telegram-mirror/commands.js +0 -8
  3. package/dist/src/channels/telegram-mirror/status-line.js +32 -2
  4. package/dist/src/constants.js +16 -2
  5. package/dist/src/lib/cache-parity-decide.js +54 -0
  6. package/dist/src/lib/config.js +40 -0
  7. package/dist/src/openai-compat/non-streaming-handler.js +2 -2
  8. package/dist/src/openai-compat/streaming-handler.js +2 -2
  9. package/dist/src/session-bootstrap/cwd-patch.js +61 -1
  10. package/package.json +3 -2
  11. package/dist/src/channels/adapter.d.ts +0 -103
  12. package/dist/src/channels/telegram-mirror/askuser.d.ts +0 -107
  13. package/dist/src/channels/telegram-mirror/burst-accumulator.d.ts +0 -96
  14. package/dist/src/channels/telegram-mirror/callback-mapping.d.ts +0 -61
  15. package/dist/src/channels/telegram-mirror/card-renderer.d.ts +0 -68
  16. package/dist/src/channels/telegram-mirror/card-state.d.ts +0 -83
  17. package/dist/src/channels/telegram-mirror/commands.d.ts +0 -183
  18. package/dist/src/channels/telegram-mirror/compose-buffer.d.ts +0 -71
  19. package/dist/src/channels/telegram-mirror/cost-views.d.ts +0 -58
  20. package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.d.ts +0 -21
  21. package/dist/src/channels/telegram-mirror/failure/gateway-down.d.ts +0 -15
  22. package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.d.ts +0 -15
  23. package/dist/src/channels/telegram-mirror/failure/index.d.ts +0 -23
  24. package/dist/src/channels/telegram-mirror/failure/model-5xx.d.ts +0 -16
  25. package/dist/src/channels/telegram-mirror/failure/network-blip.d.ts +0 -17
  26. package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.d.ts +0 -15
  27. package/dist/src/channels/telegram-mirror/failure/rate-limit.d.ts +0 -16
  28. package/dist/src/channels/telegram-mirror/failure/returning-after-24h.d.ts +0 -14
  29. package/dist/src/channels/telegram-mirror/failure/types.d.ts +0 -30
  30. package/dist/src/channels/telegram-mirror/inbound-handler.d.ts +0 -73
  31. package/dist/src/channels/telegram-mirror/index.d.ts +0 -32
  32. package/dist/src/channels/telegram-mirror/plan-attachment.d.ts +0 -120
  33. package/dist/src/channels/telegram-mirror/quota-reader.d.ts +0 -42
  34. package/dist/src/channels/telegram-mirror/sessions-keyboard.d.ts +0 -84
  35. package/dist/src/channels/telegram-mirror/soak-log.d.ts +0 -99
  36. package/dist/src/channels/telegram-mirror/state-machine.d.ts +0 -113
  37. package/dist/src/channels/telegram-mirror/status-line.d.ts +0 -51
  38. package/dist/src/channels/telegram-mirror/sync-commands.d.ts +0 -100
  39. package/dist/src/channels/telegram-mirror/threshold-watcher.d.ts +0 -54
  40. package/dist/src/channels/telegram-mirror/turn-bridge.d.ts +0 -125
  41. package/dist/src/cli/checks/bridge-wiring.d.ts +0 -14
  42. package/dist/src/cli/checks/config-schema.d.ts +0 -11
  43. package/dist/src/cli/checks/critical-openclaw-json-keys.d.ts +0 -21
  44. package/dist/src/cli/checks/install-path.d.ts +0 -11
  45. package/dist/src/cli/checks/patch-scaffold.d.ts +0 -17
  46. package/dist/src/cli/doctor.d.ts +0 -20
  47. package/dist/src/cli/index.d.ts +0 -8
  48. package/dist/src/cli/migrate.d.ts +0 -29
  49. package/dist/src/command-router/cc-handler.d.ts +0 -67
  50. package/dist/src/command-router/index.d.ts +0 -2
  51. package/dist/src/command-router/launch-policy.d.ts +0 -92
  52. package/dist/src/command-router/resume-policy.d.ts +0 -18
  53. package/dist/src/command-router/turn-formatter.d.ts +0 -19
  54. package/dist/src/config/loader.d.ts +0 -8
  55. package/dist/src/config/schema.d.ts +0 -192
  56. package/dist/src/constants.d.ts +0 -191
  57. package/dist/src/council/build-agent-prompt.d.ts +0 -11
  58. package/dist/src/council/cleanup-worktrees.d.ts +0 -10
  59. package/dist/src/council/consensus.d.ts +0 -20
  60. package/dist/src/council/council.d.ts +0 -67
  61. package/dist/src/council/index.d.ts +0 -2
  62. package/dist/src/council/system-prompt.d.ts +0 -16
  63. package/dist/src/council/write-worktree-claude-md.d.ts +0 -10
  64. package/dist/src/engines/base-oneshot-session.d.ts +0 -87
  65. package/dist/src/engines/heartbeat-guard.d.ts +0 -93
  66. package/dist/src/engines/index.d.ts +0 -8
  67. package/dist/src/engines/persistent-codex-session.d.ts +0 -16
  68. package/dist/src/engines/persistent-cursor-session.d.ts +0 -21
  69. package/dist/src/engines/persistent-custom-session.d.ts +0 -78
  70. package/dist/src/engines/persistent-gemini-session.d.ts +0 -21
  71. package/dist/src/engines/persistent-session.d.ts +0 -95
  72. package/dist/src/engines/resolve-bin.d.ts +0 -14
  73. package/dist/src/engines/subprocess-pool.d.ts +0 -78
  74. package/dist/src/health/handler.d.ts +0 -39
  75. package/dist/src/health/index.d.ts +0 -1
  76. package/dist/src/health/metrics.d.ts +0 -52
  77. package/dist/src/index.d.ts +0 -57
  78. package/dist/src/lib/auto-recovery.d.ts +0 -43
  79. package/dist/src/lib/cache-parity.d.ts +0 -38
  80. package/dist/src/lib/cc-cli-scan.d.ts +0 -52
  81. package/dist/src/lib/circuit-breaker.d.ts +0 -21
  82. package/dist/src/lib/config-service.d.ts +0 -106
  83. package/dist/src/lib/config.d.ts +0 -136
  84. package/dist/src/lib/cost-rollup.d.ts +0 -36
  85. package/dist/src/lib/debounce.d.ts +0 -12
  86. package/dist/src/lib/debug-tap.d.ts +0 -13
  87. package/dist/src/lib/domain-error.d.ts +0 -59
  88. package/dist/src/lib/drift-detector.d.ts +0 -46
  89. package/dist/src/lib/env-overrides.d.ts +0 -47
  90. package/dist/src/lib/error-formatter.d.ts +0 -91
  91. package/dist/src/lib/error-renderer.d.ts +0 -20
  92. package/dist/src/lib/heartbeat-config.d.ts +0 -34
  93. package/dist/src/lib/heartbeat-workaround.d.ts +0 -44
  94. package/dist/src/lib/html-render.d.ts +0 -50
  95. package/dist/src/lib/http-agent.d.ts +0 -47
  96. package/dist/src/lib/json-array.d.ts +0 -10
  97. package/dist/src/lib/markdown-to-mdv2.d.ts +0 -53
  98. package/dist/src/lib/markdown-v2.d.ts +0 -27
  99. package/dist/src/lib/perf/async-compact.d.ts +0 -26
  100. package/dist/src/lib/perf/direct-sdk.d.ts +0 -26
  101. package/dist/src/lib/perf/haiku-route.d.ts +0 -19
  102. package/dist/src/lib/perf/predictive-continuation.d.ts +0 -18
  103. package/dist/src/lib/perf/read-batch.d.ts +0 -33
  104. package/dist/src/lib/perf/skill-list-collapse.d.ts +0 -22
  105. package/dist/src/lib/perf/speculative-bubble.d.ts +0 -27
  106. package/dist/src/lib/perf/typing-prefetch.d.ts +0 -25
  107. package/dist/src/lib/probes.d.ts +0 -50
  108. package/dist/src/lib/register-guard.d.ts +0 -56
  109. package/dist/src/lib/req-shape-log.d.ts +0 -31
  110. package/dist/src/lib/safe-upstream-probes.d.ts +0 -25
  111. package/dist/src/lib/session-registry.d.ts +0 -66
  112. package/dist/src/lib/spawn-async.d.ts +0 -18
  113. package/dist/src/lib/status-tee-reader.d.ts +0 -29
  114. package/dist/src/lib/sysprompt-strip.d.ts +0 -53
  115. package/dist/src/lib/telegram-bot-api.d.ts +0 -146
  116. package/dist/src/lib/telemetry.d.ts +0 -38
  117. package/dist/src/lib/test-mode.d.ts +0 -26
  118. package/dist/src/lib/trajectory.d.ts +0 -44
  119. package/dist/src/lib/vendor-paths.d.ts +0 -12
  120. package/dist/src/lifecycle/boot.d.ts +0 -48
  121. package/dist/src/lifecycle/patch-manifest.d.ts +0 -82
  122. package/dist/src/lifecycle/phase-import-upstream.d.ts +0 -12
  123. package/dist/src/lifecycle/phase-install-patches.d.ts +0 -12
  124. package/dist/src/lifecycle/phase-schedule-jobs.d.ts +0 -12
  125. package/dist/src/lifecycle/phase-start-server.d.ts +0 -11
  126. package/dist/src/lifecycle/phase-validate-config.d.ts +0 -9
  127. package/dist/src/lifecycle/phase-validate-upstream.d.ts +0 -11
  128. package/dist/src/lifecycle/phase-wire-handlers.d.ts +0 -12
  129. package/dist/src/lifecycle/safe-restart.d.ts +0 -99
  130. package/dist/src/logger.d.ts +0 -14
  131. package/dist/src/mcp/bridge.d.ts +0 -21
  132. package/dist/src/mcp/index.d.ts +0 -2
  133. package/dist/src/models.d.ts +0 -68
  134. package/dist/src/observability/event-bus.d.ts +0 -86
  135. package/dist/src/observability/get-event-bus.d.ts +0 -25
  136. package/dist/src/observability/observability-service.d.ts +0 -19
  137. package/dist/src/observability/perf-telemetry.d.ts +0 -65
  138. package/dist/src/observability/subscribers/metrics.d.ts +0 -11
  139. package/dist/src/observability/subscribers/session-capture.d.ts +0 -15
  140. package/dist/src/openai-compat/autonomy-rule.d.ts +0 -26
  141. package/dist/src/openai-compat/bridges/allowlist.d.ts +0 -19
  142. package/dist/src/openai-compat/bridges/factory.d.ts +0 -30
  143. package/dist/src/openai-compat/bridges/media-bridge.d.ts +0 -34
  144. package/dist/src/openai-compat/bridges/openclaw-api-shim.d.ts +0 -54
  145. package/dist/src/openai-compat/bridges/openclaw-native-tools.d.ts +0 -61
  146. package/dist/src/openai-compat/bridges/openclaw-tool-registry.d.ts +0 -26
  147. package/dist/src/openai-compat/bridges/tts-media-bridge.d.ts +0 -19
  148. package/dist/src/openai-compat/chat-cwd.d.ts +0 -22
  149. package/dist/src/openai-compat/cli-stream-parser.d.ts +0 -134
  150. package/dist/src/openai-compat/index.d.ts +0 -1
  151. package/dist/src/openai-compat/message-extractor.d.ts +0 -84
  152. package/dist/src/openai-compat/mode-flags.d.ts +0 -34
  153. package/dist/src/openai-compat/non-streaming-handler.d.ts +0 -29
  154. package/dist/src/openai-compat/openai-chunk-types.d.ts +0 -35
  155. package/dist/src/openai-compat/openai-compat.d.ts +0 -49
  156. package/dist/src/openai-compat/openai-types.d.ts +0 -71
  157. package/dist/src/openai-compat/parse-route-body.d.ts +0 -24
  158. package/dist/src/openai-compat/prompts.d.ts +0 -47
  159. package/dist/src/openai-compat/request-coalescer.d.ts +0 -77
  160. package/dist/src/openai-compat/response-formatter.d.ts +0 -33
  161. package/dist/src/openai-compat/session-key-resolver.d.ts +0 -41
  162. package/dist/src/openai-compat/skill-resolver.d.ts +0 -59
  163. package/dist/src/openai-compat/sse-translator.d.ts +0 -51
  164. package/dist/src/openai-compat/status-reporter.d.ts +0 -30
  165. package/dist/src/openai-compat/streaming-handler.d.ts +0 -52
  166. package/dist/src/openai-compat/tool-calls-parser.d.ts +0 -34
  167. package/dist/src/openai-compat/tool-results-serializer.d.ts +0 -60
  168. package/dist/src/openai-compat/tts-rule.d.ts +0 -20
  169. package/dist/src/openai-compat/voice-recovery.d.ts +0 -56
  170. package/dist/src/patches/cache-parity-registry.d.ts +0 -103
  171. package/dist/src/patches/claude-md-injection.d.ts +0 -10
  172. package/dist/src/patches/cwd-redirect.d.ts +0 -10
  173. package/dist/src/patches/embedded-server-route.d.ts +0 -23
  174. package/dist/src/patches/pricing-overrides.d.ts +0 -10
  175. package/dist/src/patches/resume-registry-restore.d.ts +0 -11
  176. package/dist/src/patches/session-pid-tracking.d.ts +0 -10
  177. package/dist/src/patches/sysprompt-strip.d.ts +0 -46
  178. package/dist/src/patches/tools-restoration.d.ts +0 -12
  179. package/dist/src/persistence/migration-v0.d.ts +0 -24
  180. package/dist/src/persistence/session-registry.d.ts +0 -58
  181. package/dist/src/proxy/anthropic-adapter.d.ts +0 -136
  182. package/dist/src/proxy/handler.d.ts +0 -39
  183. package/dist/src/proxy/index.d.ts +0 -4
  184. package/dist/src/proxy/schema-cleaner.d.ts +0 -11
  185. package/dist/src/proxy/thought-cache.d.ts +0 -19
  186. package/dist/src/session/embedded-server.d.ts +0 -25
  187. package/dist/src/session/inbox-manager.d.ts +0 -38
  188. package/dist/src/session/index.d.ts +0 -3
  189. package/dist/src/session/persisted-sessions.d.ts +0 -50
  190. package/dist/src/session/session-manager.d.ts +0 -247
  191. package/dist/src/session/watchdogs.d.ts +0 -92
  192. package/dist/src/session-bootstrap/boot-self-heal.d.ts +0 -32
  193. package/dist/src/session-bootstrap/cwd-patch.d.ts +0 -50
  194. package/dist/src/session-bootstrap/index.d.ts +0 -3
  195. package/dist/src/session-bootstrap/resume-registry.d.ts +0 -27
  196. package/dist/src/session-bootstrap/session-hygiene.d.ts +0 -23
  197. package/dist/src/session-bootstrap/sysprompt-strip.d.ts +0 -24
  198. package/dist/src/session-bootstrap/think-conflict-resolver.d.ts +0 -32
  199. package/dist/src/types/route.d.ts +0 -11
  200. package/dist/src/types/runtime-config.d.ts +0 -208
  201. package/dist/src/types/sse.d.ts +0 -29
  202. package/dist/src/types/tool-bridge.d.ts +0 -82
  203. package/dist/src/types/upstream.d.ts +0 -580
  204. package/dist/src/types.d.ts +0 -498
  205. package/dist/src/validation.d.ts +0 -31
@@ -30,8 +30,12 @@ function basename(p) {
30
30
  * Glyph picked per tool-call status. Mirrors terminal "running / done /
31
31
  * errored" iconography that A1 already reads in the live Claude Code TUI.
32
32
  */
33
- function toolGlyph(tc) {
34
- if (tc.isError)
33
+ function toolGlyph(tc, suppressToolErrors = false) {
34
+ // OpenClaw 2026.5.20 #81561 — when the user set messages.suppressToolErrors,
35
+ // an errored-but-completed tool reads as a normal "✓" rather than the "✗"
36
+ // error glyph. tc.result is set for an errored tool (it carries the error
37
+ // body), so the completed-✓ branch below picks it up once "✗" is skipped.
38
+ if (tc.isError && !suppressToolErrors)
35
39
  return '✗';
36
40
  if (tc.result !== undefined)
37
41
  return '✓';
@@ -260,8 +264,8 @@ export function toolIcon(name) {
260
264
  return '🔌';
261
265
  return '🔹';
262
266
  }
263
- export function renderToolLine(tc) {
264
- const glyph = toolGlyph(tc);
267
+ export function renderToolLine(tc, suppressToolErrors = false) {
268
+ const glyph = toolGlyph(tc, suppressToolErrors);
265
269
  const icon = toolIcon(tc.name);
266
270
  // Status glyph + emoji are HTML-safe; the tool name is HTML-escaped and the
267
271
  // input detail rides in a <code> span (v0.27.0 M1 HTML styling).
@@ -384,7 +388,7 @@ export function renderTurn(turn, meta) {
384
388
  let acc = 0;
385
389
  for (let i = turn.toolCalls.length - 1; i >= 0; i--) {
386
390
  const tc = turn.toolCalls[i];
387
- const line = renderToolLine(tc);
391
+ const line = renderToolLine(tc, meta?.suppressToolErrors);
388
392
  // v0.27.4 M2 — for Edit/Write/MultiEdit show a diff (from input) instead of
389
393
  // the "File updated" result; other tools keep the result preview.
390
394
  const diffText = toolDiffBlock(tc);
@@ -44,14 +44,6 @@ export function parseSlash(text) {
44
44
  return { cmd: cmd.toLowerCase(), args: tokens.slice(1) };
45
45
  }
46
46
  // ── /sessions ────────────────────────────────────────────────────────────
47
- function enrichRows(entries, stateLookup) {
48
- return entries.map((e) => ({
49
- slug: e.slug,
50
- sessionName: e.sessionName,
51
- state: stateLookup(e.slug),
52
- lastUsedAt: e.lastUsedAt,
53
- }));
54
- }
55
47
  /**
56
48
  * v0.28.0 — `/sessions` is now a `claude -r`-style picker over the REAL Claude
57
49
  * Code sessions. It mirrors `~/.claude/projects/**` (the store every `claude`
@@ -4,7 +4,7 @@
4
4
  * Builds the Claude-Code-CLI-style status line that sits at the top of the
5
5
  * Telegram live-mirror card, e.g.:
6
6
  *
7
- * [Opus 4.7] · CC 2.1.145 · ⏱ 2h13m · 🔧 3 · bypass
7
+ * [Opus 4.7] · CC 2.1.145 · GW 2026.5.20 · ⏱ 2h13m · 🔧 3 · bypass
8
8
  *
9
9
  * STRICT no-fake-data rule (per the v0.26.2 brief): every segment is rendered
10
10
  * ONLY when its value is genuinely available. Missing model / version / etc.
@@ -40,6 +40,33 @@ export function getCcVersion() {
40
40
  export function _resetCcVersionForTests() {
41
41
  _ccVersion = undefined;
42
42
  }
43
+ // ── Gateway version (cached; resolved at most once per process) ──────────────
44
+ // `openclaw --version` prints e.g. "OpenClaw 2026.5.20 (e510042)". Mirrors the
45
+ // getCcVersion() resolve-once-and-cache contract (including a failed lookup as
46
+ // `null`). Surfaced on the status line as "GW <ver>" so the card shows which
47
+ // OpenClaw core is actually running alongside the CC CLI version.
48
+ let _gatewayVersion;
49
+ export function getGatewayVersion() {
50
+ if (_gatewayVersion !== undefined)
51
+ return _gatewayVersion;
52
+ try {
53
+ const out = execFileSync('openclaw', ['--version'], {
54
+ timeout: 5_000,
55
+ stdio: ['ignore', 'pipe', 'ignore'],
56
+ encoding: 'utf8',
57
+ });
58
+ const m = /(\d+\.\d+\.\d+)/.exec(out);
59
+ _gatewayVersion = m ? m[1] : null;
60
+ }
61
+ catch {
62
+ _gatewayVersion = null;
63
+ }
64
+ return _gatewayVersion;
65
+ }
66
+ /** Test-only — reset the cached gateway version. */
67
+ export function _resetGatewayVersionForTests() {
68
+ _gatewayVersion = undefined;
69
+ }
43
70
  // ── Model id → short label ─────────────────────────────────────────────────
44
71
  // "claude-opus-4-7" → "Opus 4.7"; "claude-sonnet-4-6" → "Sonnet 4.6". Unknown
45
72
  // shapes pass through unchanged (still real data, just not prettified).
@@ -69,7 +96,7 @@ export function fmtElapsed(ms) {
69
96
  /**
70
97
  * Render the status line for a turn. Returns `undefined` when NOTHING is
71
98
  * available to show (so callers omit the line entirely). Segments, in order:
72
- * [model] · CC <ver> · ⏱ <elapsed> · 🔧 <toolCount> · bypass
99
+ * [model] · CC <ver> · GW <ver> · ⏱ <elapsed> · 🔧 <toolCount> · bypass
73
100
  * Every segment is conditional on real data.
74
101
  */
75
102
  export function renderStatusLine(turn, meta, now = Date.now()) {
@@ -80,6 +107,9 @@ export function renderStatusLine(turn, meta, now = Date.now()) {
80
107
  const ver = getCcVersion();
81
108
  if (ver)
82
109
  segs.push(`CC ${ver}`);
110
+ const gwVer = getGatewayVersion();
111
+ if (gwVer)
112
+ segs.push(`GW ${gwVer}`);
83
113
  // Turn elapsed is always real (Turn.startedAt is set at card creation). Use
84
114
  // endedAt once the turn is done so the finalized card shows total duration.
85
115
  const elapsedMs = (turn.endedAt ?? now) - turn.startedAt;
@@ -28,14 +28,28 @@ export const SESSION_READY_FALLBACK_MS = 2_000;
28
28
  *
29
29
  * v0.20.0: env-overridable via `CC_OPENCLAW_TURN_TIMEOUT_MS` (see
30
30
  * src/lib/env-overrides.ts). Setting `=0` disables the per-turn timer
31
- * entirely, matching direct-CLI behavior on long tool turns. */
31
+ * entirely, matching direct-CLI behavior on long tool turns.
32
+ *
33
+ * OpenClaw 2026.5.20 (#83979): core now HONORS
34
+ * `models.providers.cc-openclaw.timeoutSeconds` above its idle watchdog —
35
+ * previously the outer request was killed at ~120s regardless of that value,
36
+ * so this 900_000 alignment was a workaround fighting a hidden ceiling. It is
37
+ * now a genuine, end-to-end envelope. Keep it aligned to timeoutSeconds (do
38
+ * NOT reduce below the configured outer bound). */
32
39
  export const TURN_TIMEOUT_MS = 900_000;
33
40
  /** Runtime watchdog threshold for "stuck" sessions. If a session is
34
41
  * `isBusy === true` AND its stats.lastActivity hasn't moved in this many
35
42
  * ms, the SessionManager watchdog aborts + disposes the session. Mirrors
36
43
  * the boot-time orphan reaper in gateway-pm2-wrapper.sh:53-60, but runs
37
44
  * continuously on a setInterval. Env-overridable via
38
- * CC_OPENCLAW_STALLED_KILL_MS. v0.10.0. */
45
+ * CC_OPENCLAW_STALLED_KILL_MS. v0.10.0.
46
+ *
47
+ * NOTE (OpenClaw 2026.5.20 #83979): this is ORTHOGONAL to core's now-honored
48
+ * provider idle watchdog — that bounds first-token / inter-token waits on the
49
+ * request; this bounds a wedged Claude SUBPROCESS that emits no output at all.
50
+ * The core fix does NOT make this redundant. Keep it. Raise (don't remove) via
51
+ * CC_OPENCLAW_STALLED_KILL_MS only if legitimately long no-output tool runs
52
+ * (e.g. a long Bash) trip it. */
39
53
  export const STALLED_SESSION_KILL_MS = 180_000;
40
54
  /** How often the stalled-session watchdog scans the sessions Map. */
41
55
  export const STALLED_WATCH_INTERVAL_MS = 30_000;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Content-addressed cache-parity decision (v0.30.0).
3
+ *
4
+ * Problem (diagnosed 2026-05-23 from the live sysprompt-cost telemetry):
5
+ * cc-openclaw's cache-parity registry is keyed by sessionKey. The cache HIT
6
+ * path strips the role:system messages so the Claude CLI subprocess reuses its
7
+ * already-cached `--append-system-prompt` block; a MISS inlines the full ~7K
8
+ * system prompt into the user message (uncached). On the live box, 70% of all
9
+ * cache misses were `session_unknown` — the FIRST turn of a session, where the
10
+ * registry has no entry yet. Telegram conversations are short (median 1 turn /
11
+ * session, 23 of 35 single-turn), so cold-start dominated: the hit rate stalled
12
+ * at ~75% vs the terminal CLI's ~95%. The dynamic-envelope churn we originally
13
+ * set out to fix was only ~7.5% of turns.
14
+ *
15
+ * Key observation: every Savvy session shares the *identical* system prefix
16
+ * (same SOUL/USER/AGENTS/TOOLS/MEMORY + harness ⇒ same sysHash). So a brand-new
17
+ * session whose sysHash was already seen for some *other* session is a
18
+ * known-good prefix — its `--append-system-prompt` will be injected at
19
+ * startSession from the registry entry the route patch writes this same turn,
20
+ * so it is SAFE to strip the redundant inline and ride the cached path on
21
+ * turn 1 instead of re-billing the full prompt.
22
+ *
23
+ * Safety: the "warm-hash hit" only applies when the session is NEW (not yet in
24
+ * the SessionManager) — that guarantees startSession runs and appends the
25
+ * prompt. An EXISTING session missing its registry entry (e.g. registry wiped
26
+ * mid-life) keeps the legacy inline path so the model never loses its system
27
+ * prompt. A `hash_mismatch` (entry exists, different hash = genuine mid-session
28
+ * churn) also stays on the inline path: the CLI's append still holds the OLD
29
+ * prompt, so the new one must be delivered in-band.
30
+ *
31
+ * Pure + side-effect-free so the decision is unit-testable independent of the
32
+ * EmbeddedServer route closure (matches the codebase's pure-helper pattern:
33
+ * isPersistedSessionFresh, shouldWriteThroughResumeId).
34
+ */
35
+ /**
36
+ * Decide how the route patch should treat the system prompt this turn.
37
+ *
38
+ * - 'hit' → registry entry matches this session: strip role:system,
39
+ * ride the already-cached append.
40
+ * - 'warm-hash-hit' → new session + known-good prefix: write the registry
41
+ * entry (so startSession appends it), strip role:system,
42
+ * ride the cached path. Closes the cold-start gap.
43
+ * - 'miss' → inline the system prompt into the user message (the safe
44
+ * legacy path): first-ever prefix, genuine churn, or an
45
+ * existing session that lost its registry entry.
46
+ */
47
+ export function decideCacheParityAction(input) {
48
+ const { entry, sysHash, knownHash, sessionIsNew } = input;
49
+ if (entry && entry.hash === sysHash)
50
+ return 'hit';
51
+ if (!entry && knownHash && sessionIsNew)
52
+ return 'warm-hash-hit';
53
+ return 'miss';
54
+ }
@@ -351,6 +351,46 @@ export function _resetTtsAutoModeCacheForTests() {
351
351
  _ttsAutoModeCache = null;
352
352
  _ttsAutoModeCacheChecked = false;
353
353
  }
354
+ // ── Suppress tool errors (OpenClaw 2026.5.20 #81561) ────────────────────────
355
+ // Reads `messages.suppressToolErrors` from `~/.openclaw/openclaw.json` once and
356
+ // caches it. When true, the user has opted out of tool-failure noise, so the
357
+ // Telegram live-card renders an errored-but-completed tool with its normal "✓"
358
+ // glyph instead of the "✗" error glyph (the card-side equivalent of the core's
359
+ // "no separate warning payloads" behavior). The TURN-level "❌ <reason>" header
360
+ // is unaffected — that's terminal turn state, not a per-tool warning.
361
+ //
362
+ // Same custom-file-reader rationale as getTtsAutoMode(): openclaw.json is a
363
+ // plugin-side decision that shouldn't reach through OpenClaw's typed runtime.
364
+ // Tests override via the CC_OPENCLAW_SUPPRESS_TOOL_ERRORS env var.
365
+ let _suppressToolErrorsCache = null;
366
+ let _suppressToolErrorsCacheChecked = false;
367
+ export function getSuppressToolErrors() {
368
+ // Test-time / explicit override takes precedence.
369
+ const envOverride = process.env.CC_OPENCLAW_SUPPRESS_TOOL_ERRORS?.trim().toLowerCase();
370
+ if (envOverride === '1' || envOverride === 'true' || envOverride === 'on')
371
+ return true;
372
+ if (envOverride === '0' || envOverride === 'false' || envOverride === 'off')
373
+ return false;
374
+ if (!_suppressToolErrorsCacheChecked) {
375
+ _suppressToolErrorsCacheChecked = true;
376
+ try {
377
+ const configPath = process.env.OPENCLAW_CONFIG_PATH || join(homedir(), '.openclaw', 'openclaw.json');
378
+ const raw = readFileSync(configPath, 'utf8');
379
+ const parsed = JSON.parse(raw);
380
+ _suppressToolErrorsCache = parsed.messages?.suppressToolErrors ?? null;
381
+ }
382
+ catch {
383
+ // File missing / unreadable / invalid JSON — fall through to false.
384
+ _suppressToolErrorsCache = null;
385
+ }
386
+ }
387
+ return _suppressToolErrorsCache === true;
388
+ }
389
+ /** Test-only: reset the suppress-tool-errors cache so the next call re-reads. */
390
+ export function _resetSuppressToolErrorsCacheForTests() {
391
+ _suppressToolErrorsCache = null;
392
+ _suppressToolErrorsCacheChecked = false;
393
+ }
354
394
  export function getClaudeBin() {
355
395
  const cfg = getConfigService();
356
396
  if (cfg)
@@ -23,7 +23,7 @@
23
23
  import { reportStatus, getToolDescription } from './status-reporter.js';
24
24
  import { parseToolCallsFromText } from './tool-calls-parser.js';
25
25
  import { formatCompletionResponse } from './response-formatter.js';
26
- import { getSurfaceThinkingEnabled, getTtsAutoMode, getCardAnswerMirrorEnabled } from '../lib/config.js';
26
+ import { getSurfaceThinkingEnabled, getTtsAutoMode, getCardAnswerMirrorEnabled, getSuppressToolErrors } from '../lib/config.js';
27
27
  import { emit as emitTrajectory, emitTurnTrace } from '../lib/trajectory.js';
28
28
  import { formatError, ERROR_CODES } from '../lib/error-formatter.js';
29
29
  import { applyVoiceRecovery, _logVoiceDebug, detectVoiceIntent, hasTtsMarkers } from './voice-recovery.js';
@@ -55,7 +55,7 @@ slashCommand) {
55
55
  // cards THIS module instance sees (see handleStreaming counterpart).
56
56
  process.stderr.write(`[cc-openclaw/openai-compat] handleNonStreaming pid=${process.pid} hasTools=${hasTools} ${mirrorCardStateDebug()} session=${sessionName}\n`);
57
57
  // v0.26.2 M1+M2 — feed the CC-CLI status line (model + bypass + quota; see streaming).
58
- mirrorSetCardMeta({ model, bypassPermissions: true, ...mirrorReadQuotaMeta() });
58
+ mirrorSetCardMeta({ model, bypassPermissions: true, suppressToolErrors: getSuppressToolErrors(), ...mirrorReadQuotaMeta() });
59
59
  // v0.14.0 turn-trace probe: capture wall-clock duration of the turn.
60
60
  const turnStartMs = Date.now();
61
61
  // v0.15.0 Slice 1: hoist userText so the catch-path probe emit can reference
@@ -41,7 +41,7 @@ import { formatCompletionChunk } from './response-formatter.js';
41
41
  import { isToolStreamMode } from './mode-flags.js';
42
42
  import { emit as emitTrajectory, emitTurnTrace } from '../lib/trajectory.js';
43
43
  import { formatError, ERROR_CODES } from '../lib/error-formatter.js';
44
- import { getSurfaceThinkingEnabled, getTtsAutoMode, getCardAnswerMirrorEnabled } from '../lib/config.js';
44
+ import { getSurfaceThinkingEnabled, getTtsAutoMode, getCardAnswerMirrorEnabled, getSuppressToolErrors } from '../lib/config.js';
45
45
  import { applyVoiceRecovery, detectVoiceIntent, hasTtsMarkers, _logVoiceDebug } from './voice-recovery.js';
46
46
  import { pushToolUse as mirrorPushToolUse, pushToolResult as mirrorPushToolResult, pushAssistantText as mirrorPushAssistantText, pushThinking as mirrorPushThinking, finalizeActiveCards as mirrorFinalizeActiveCards, extractInsightForCard as mirrorExtractInsightForCard, failActiveCards as mirrorFailActiveCards, classifyFailure, setCardMeta as mirrorSetCardMeta, readQuotaMeta as mirrorReadQuotaMeta, } from '../channels/telegram-mirror/turn-bridge.js';
47
47
  import { cardStateDebug as mirrorCardStateDebug } from '../channels/telegram-mirror/card-state.js';
@@ -83,7 +83,7 @@ onFinalText) {
83
83
  // v0.26.2 M1+M2 — feed the CC-CLI status line. The openai-compat path always
84
84
  // runs permissionMode 'bypassPermissions' (openai-compat.ts) so we assert it.
85
85
  // Quota is read once here (real status-tee snapshot or omitted).
86
- mirrorSetCardMeta({ model, bypassPermissions: true, ...mirrorReadQuotaMeta() });
86
+ mirrorSetCardMeta({ model, bypassPermissions: true, suppressToolErrors: getSuppressToolErrors(), ...mirrorReadQuotaMeta() });
87
87
  // #4 dual-surface seam — hoist once per turn (read off the hot delta loop).
88
88
  // Default OFF: card is the activity pane, gateway draft is the answer pane.
89
89
  const mirrorAnswerToCard = getCardAnswerMirrorEnabled();
@@ -37,6 +37,7 @@ import { defaultRegisterGuard } from '../lib/register-guard.js';
37
37
  import { isTestMode } from '../lib/test-mode.js';
38
38
  import { writePerfEvent } from '../observability/perf-telemetry.js';
39
39
  import { collapseSkillList } from '../lib/perf/skill-list-collapse.js';
40
+ import { decideCacheParityAction } from '../lib/cache-parity-decide.js';
40
41
  import { isCacheParityTrackB, isTokenTelemetryEnabled, isSyspromptDumpEnabled, getMaxConcurrentSessions, getSessionTtlMinutes, ensureUxBridgeAllSessionsDefault, } from '../lib/config.js';
41
42
  import { VENDOR_FILES } from '../lib/vendor-paths.js';
42
43
  import { OpenClawConfigSchema, findMainAgent, getAgentPrimaryModel, getDefaultsPrimaryModel, isClaudeRoutedModel, } from '../types/upstream.js';
@@ -99,6 +100,7 @@ const METRICS = {
99
100
  systemPromptInlined: 0,
100
101
  uxMetaSeeded: 0,
101
102
  cacheParityHits: 0,
103
+ cacheParityWarmHashHits: 0,
102
104
  cacheParityMisses: 0,
103
105
  cacheParityRegistryWrites: 0,
104
106
  cacheParityAppendInjections: 0,
@@ -143,6 +145,24 @@ function _setSystemInlineCache(key, val) {
143
145
  }
144
146
  _systemInlineCache.set(key, val);
145
147
  }
148
+ // ── Known sysprompt-hash set (v0.30.0 — content-addressed cache parity) ──────
149
+ // Cross-session record of every sysHash written to the cache-parity registry
150
+ // this process. Lets a brand-new session recognise the shared Savvy system
151
+ // prefix and ride the cached append path on turn 1 instead of inlining the full
152
+ // ~7K prompt — the dominant cold-start miss (see lib/cache-parity-decide.ts).
153
+ // Bounded FIFO: distinct prefixes are few (one per build), 64 is ample headroom.
154
+ const _knownSysHashes = new Set();
155
+ const KNOWN_SYS_HASH_MAX = 64;
156
+ function _rememberSysHash(hash) {
157
+ if (_knownSysHashes.has(hash))
158
+ return;
159
+ if (_knownSysHashes.size >= KNOWN_SYS_HASH_MAX) {
160
+ const oldest = _knownSysHashes.values().next().value;
161
+ if (oldest !== undefined)
162
+ _knownSysHashes.delete(oldest);
163
+ }
164
+ _knownSysHashes.add(hash);
165
+ }
146
166
  // ── Tool dump hash guard (v0.6.0 — per-session-key cache + fast-skip) ──
147
167
  // Pre-v0.6.0: a single global `_lastToolDumpHash` thrashed when multiple
148
168
  // sessions had different tool sets. JSON.stringify + SHA1 ran on EVERY
@@ -655,11 +675,30 @@ function applyRoutePatch(EmbeddedServer) {
655
675
  try {
656
676
  const reg = _readCacheParityRegistry();
657
677
  const entry = reg[sessionKey];
658
- if (entry && entry.hash === sysHash) {
678
+ // sessionIsNew: no live session in the manager ⇒ startSession will
679
+ // run on this request and inject appendSystemPrompt from the
680
+ // registry entry we write below. That guarantee is what makes the
681
+ // warm-hash strip safe (see lib/cache-parity-decide.ts). Default
682
+ // to false on any access failure — conservative: an unproven
683
+ // append guarantee falls back to the safe inline path.
684
+ let sessionIsNew = false;
685
+ try {
686
+ const mgr = this.manager;
687
+ sessionIsNew = !(mgr?.sessions?.has?.('openai-' + sessionKey) ?? false);
688
+ }
689
+ catch { /* keep sessionIsNew=false (inline fallback) */ }
690
+ const action = decideCacheParityAction({
691
+ entry: entry ? { hash: entry.hash } : undefined,
692
+ sysHash,
693
+ knownHash: _knownSysHashes.has(sysHash),
694
+ sessionIsNew,
695
+ });
696
+ if (action === 'hit') {
659
697
  body.messages = messages.filter(m => m?.role !== 'system');
660
698
  METRICS.cacheParityHits++;
661
699
  METRICS.systemPromptInlined++;
662
700
  cacheParityHandled = true;
701
+ _rememberSysHash(sysHash);
663
702
  writePerfEvent({
664
703
  event: 'cache_check',
665
704
  sessionKey,
@@ -668,8 +707,29 @@ function applyRoutePatch(EmbeddedServer) {
668
707
  sysHash,
669
708
  });
670
709
  }
710
+ else if (action === 'warm-hash-hit') {
711
+ // Cold-start win: new session + known-good Savvy prefix. Write
712
+ // the entry so startSession appends it, strip the redundant
713
+ // inline, ride the cached path on turn 1.
714
+ _writeCacheParityEntry(sessionKey, sysHash, sysContent);
715
+ body.messages = messages.filter(m => m?.role !== 'system');
716
+ METRICS.cacheParityWarmHashHits++;
717
+ METRICS.cacheParityRegistryWrites++;
718
+ METRICS.systemPromptInlined++;
719
+ cacheParityHandled = true;
720
+ _rememberSysHash(sysHash);
721
+ logger.info(`${TAG} cache-parity warm-hash hit: session=${sessionKey} hash=${sysHash} sysLen=${sysContent.length} (cold-start dedup; startSession will append)`);
722
+ writePerfEvent({
723
+ event: 'cache_check',
724
+ sessionKey,
725
+ outcome: 'hit',
726
+ cause: 'warm_hash',
727
+ sysHash,
728
+ });
729
+ }
671
730
  else {
672
731
  _writeCacheParityEntry(sessionKey, sysHash, sysContent);
732
+ _rememberSysHash(sysHash);
673
733
  METRICS.cacheParityMisses++;
674
734
  METRICS.cacheParityRegistryWrites++;
675
735
  logger.info(`${TAG} cache-parity miss: session=${sessionKey} oldHash=${entry?.hash || 'none'} newHash=${sysHash} sysLen=${sysContent.length} (registry updated; next session start will append)`);
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@a1hvdy/cc-openclaw",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "A1xAI's Anthropic CLI bridge plugin for OpenClaw",
5
5
  "author": "@a1cy",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "main": "dist/src/index.js",
9
- "types": "dist/src/index.d.ts",
10
9
  "homepage": "https://github.com/A1cy/cc-openclaw",
11
10
  "repository": "github:A1cy/cc-openclaw",
12
11
  "engines": {
@@ -23,6 +22,8 @@
23
22
  },
24
23
  "files": [
25
24
  "dist/",
25
+ "!dist/**/*.d.ts",
26
+ "!dist/**/*.d.ts.map",
26
27
  "configs/",
27
28
  "skills/",
28
29
  "openclaw.plugin.json",
@@ -1,103 +0,0 @@
1
- /**
2
- * Channel Adapter — generic interface for delivering session UX
3
- * to chat channels (Telegram, Discord, Signal, etc.).
4
- *
5
- * Telegram is the first concrete implementation (src/channels/telegram/).
6
- * Discord and Signal are deferred to v1.1 (Nice-to-Have).
7
- *
8
- * Each adapter:
9
- * - Renders live session cards (progress, tool calls, summaries)
10
- * - Receives inbound channel messages and forwards them as session inputs
11
- * - Sends session outputs back to the channel
12
- * - Provides idempotent register() to wire OpenClaw plugin lifecycle hooks
13
- *
14
- * AUDIT — B2 (PRP_v3 §11 vs reality, 2026-04-28):
15
- *
16
- * PRP_v3 §11.1 documents an event vocabulary (`session.created`,
17
- * `session.turn.start`, `session.tool.start`, `plugin.heartbeat`, etc.)
18
- * that does NOT exist in the actual codebase. Every `api.on()` call
19
- * site in src/ uses the openclaw-claude-code hook-event vocabulary
20
- * documented in `ChannelHookEvent` below. Likewise §11.2's
21
- * `api.emit('session.result', ...)` is unimplemented — no `api.emit`
22
- * call sites exist.
23
- *
24
- * Resolution: this audit treats the *code* as canonical and the spec
25
- * as drifted. The `ChannelHookEvent` literal union captures the actual
26
- * contract any future channel adapter must speak. The §11.x doc
27
- * sections need a v3.1 revision to match — tracked as a docs-tier
28
- * follow-up (Tier 3 §24), not a code-tier blocker.
29
- */
30
- /**
31
- * Canonical hook event vocabulary — every event name passed to `api.on()`
32
- * across src/channels/, src/command-router/, and src/session-bootstrap/
33
- * (verified by `rg "api\\.on\\(" src/`).
34
- *
35
- * This is the contract: any channel/handler that wants to subscribe to
36
- * gateway events MUST use one of these names. Adding a new event name
37
- * here without a corresponding gateway emitter is dead-code-typing.
38
- */
39
- type ChannelHookEvent = 'before_dispatch' | 'before_prompt_build' | 'before_model_resolve' | 'before_tool_call' | 'after_tool_call' | 'reply_dispatch' | 'message_sending';
40
- /** Hook handler signature — gateway passes (event, ctx?) positional args. */
41
- type ChannelHookHandler = (...args: unknown[]) => unknown | Promise<unknown>;
42
- export interface PluginApi {
43
- /**
44
- * Subscribe to a gateway hook event.
45
- *
46
- * Accepts `ChannelHookEvent` for compile-time event-name checking, or
47
- * `string` as an escape hatch for adapter-specific events not yet
48
- * canonicalized into the union. Future tightening: drop the `| string`
49
- * overload once all hook sites use the union.
50
- */
51
- on(event: ChannelHookEvent, handler: ChannelHookHandler): void;
52
- on(event: string, handler: ChannelHookHandler): void;
53
- }
54
- /**
55
- * Outbound message from session → channel.
56
- */
57
- export interface ChannelMessage {
58
- channel: string;
59
- target: string;
60
- text?: string;
61
- buttons?: ChannelButton[];
62
- metadata?: Record<string, unknown>;
63
- }
64
- export interface ChannelButton {
65
- text: string;
66
- callbackData?: string;
67
- url?: string;
68
- }
69
- /**
70
- * Inbound envelope from channel → session.
71
- */
72
- export interface ChannelInbound {
73
- channel: string;
74
- fromUserId: string;
75
- text: string;
76
- metadata?: Record<string, unknown>;
77
- }
78
- /**
79
- * Channel adapter contract. Each concrete adapter (e.g. Telegram) implements
80
- * this interface so cc-openclaw can route session events to any wired channel.
81
- */
82
- export interface ChannelAdapter {
83
- /** Adapter identifier — must be unique across registered adapters. */
84
- readonly id: string;
85
- /**
86
- * Idempotent registration of OpenClaw plugin lifecycle hooks.
87
- * Multiple calls with the same api MUST NOT stack listeners.
88
- */
89
- register(api: PluginApi): void;
90
- /** Send an outbound message to the channel. */
91
- send(message: ChannelMessage): Promise<void>;
92
- /** Update an existing live card in-place (rendering progress). */
93
- updateCard?(target: string, message: ChannelMessage): Promise<void>;
94
- /** Render a final completion summary to the channel. */
95
- completionSummary?(target: string, summary: ChannelMessage): Promise<void>;
96
- }
97
- /**
98
- * Helper to wire any ChannelAdapter via the plugin's main register() flow.
99
- * Centralizes the per-adapter `register()` call so src/index.ts can
100
- * iterate a list of adapters without coupling to each adapter's internals.
101
- */
102
- export declare function registerChannelAdapter(api: PluginApi, adapter: ChannelAdapter): void;
103
- export {};
@@ -1,107 +0,0 @@
1
- /**
2
- * src/channels/telegram-mirror/askuser.ts — v0.26.3 M5.
3
- *
4
- * Renders Claude Code's AskUserQuestion tool as native Telegram inline
5
- * keyboards, "next-turn model" (Path A, chosen by A1 2026-05-20):
6
- *
7
- * agent calls AskUserQuestion → turn ends
8
- * → we send a keyboard message (single-select: one button per option +
9
- * "✍️ Write something else"; multi-select: checkbox toggles + Submit)
10
- * → user taps → api.registerInteractiveHandler fires
11
- * → we answer the callback, edit the message to show the choice, and
12
- * api.enqueueNextTurnInjection() queues the answer as context for the
13
- * agent's NEXT turn (it is acted on when the next message runs — the
14
- * documented primitive injects context, it does not start a turn).
15
- *
16
- * Feasibility proven 2026-05-20: the headless subprocess DOES emit an
17
- * AskUserQuestion tool_use (pushToolUse name=AskUserQuestion). The tool emits
18
- * twice (dual content_block_start + assistant-block), so capture dedups by
19
- * tool_use id.
20
- *
21
- * Telegram has no ANSI color — checkboxes use ☐/☑ glyphs.
22
- */
23
- /** Namespace prefix for callback_data so api.registerInteractiveHandler routes
24
- * taps here. Matched at the first ':' by the gateway (must be [A-Za-z0-9._-]+). */
25
- export declare const ASKUSER_NS = "ccmirror";
26
- interface QOption {
27
- label: string;
28
- description?: string;
29
- }
30
- interface ParsedQuestion {
31
- header: string;
32
- question: string;
33
- multiSelect: boolean;
34
- options: QOption[];
35
- }
36
- /**
37
- * v0.28.0 — build a single inline button that, when tapped, resumes a Claude
38
- * Code session. Reuses the proven askuser callback path: the payload is stashed
39
- * in the shared (globalThis-anchored) CallbackMap and the callback_data is
40
- * `ccmirror:<id>`, so taps route through the same before_dispatch interceptor.
41
- */
42
- export declare function buildResumeButton(uuid: string, label: string): {
43
- text: string;
44
- callback_data: string;
45
- };
46
- /** Minimal subset of the registerInteractiveHandler ctx we use. */
47
- export interface InteractiveCtx {
48
- callback: {
49
- id?: string;
50
- data?: string;
51
- namespace?: string;
52
- payload?: string;
53
- };
54
- chatId?: string | number;
55
- conversationId?: string;
56
- sessionKey?: string;
57
- threadId?: number;
58
- respond?: {
59
- editMessage?: (text: string) => Promise<unknown>;
60
- answer?: (text?: string) => Promise<unknown>;
61
- };
62
- }
63
- /** Minimal subset of the plugin api we use (next-turn injection). */
64
- export interface InjectApi {
65
- enqueueNextTurnInjection?: (injection: {
66
- sessionKey: string;
67
- text: string;
68
- placement?: 'prepend_context' | 'append_context';
69
- idempotencyKey?: string;
70
- }) => unknown;
71
- }
72
- /** Record the real agent session key for a chat (called from inbound before_dispatch). */
73
- export declare function rememberSessionKey(chatId: string, sessionKey: string | undefined): void;
74
- /** Parse the FIRST question from an AskUserQuestion tool input. Defensive —
75
- * returns undefined if the shape isn't usable (no fabrication). */
76
- export declare function parseFirstQuestion(input: Record<string, unknown> | undefined): ParsedQuestion | undefined;
77
- /**
78
- * Capture an AskUserQuestion tool_use and render the keyboard. Called from
79
- * turn-bridge when name === 'AskUserQuestion'. Dedups the dual-emission by
80
- * tool_use id. Best-effort: a send failure must not break the turn.
81
- */
82
- export declare function captureAskUserQuestion(chatId: string, threadId: number | undefined, input: Record<string, unknown> | undefined, toolUseId: string | undefined): Promise<void>;
83
- /**
84
- * Handle an inline-button tap routed from api.registerInteractiveHandler.
85
- * Resolves the callback payload, updates state, and on a terminal choice
86
- * (single-select option, multi-select Submit) injects the answer for the next
87
- * turn. "Write something else" just prompts the user to type — their typed
88
- * message becomes the next turn naturally, so no injection is needed.
89
- */
90
- export declare function handleTap(ctx: InteractiveCtx, api: InjectApi): Promise<void>;
91
- /** True if an inbound message text is actually an AskUserQuestion button tap.
92
- * The gateway delivers taps on plugin-sent keyboards as before_dispatch
93
- * messages with text = the callback_data (`ccmirror:<id>`), NOT as routed
94
- * interactive-handler callbacks — so we intercept them in before_dispatch. */
95
- export declare function isAskUserCallback(text: string): boolean;
96
- /**
97
- * Handle a tap that arrived as a before_dispatch message (text=`ccmirror:<id>`).
98
- * Builds a synthetic ctx (conversationId = chatId resolves the remembered
99
- * session key) and runs the normal tap logic. Caller MUST claim the event so
100
- * the literal callback_data never reaches the agent.
101
- */
102
- export declare function handleTapData(text: string, chatId: string, api: InjectApi): Promise<void>;
103
- /** Test-only — reset module state. */
104
- export declare function _resetAskUserForTests(): void;
105
- /** Test-only — peek pending question count. */
106
- export declare function _pendingCount(): number;
107
- export {};