@a1hvdy/cc-openclaw 0.30.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.
- package/dist/src/channels/telegram-mirror/card-renderer.js +9 -5
- package/dist/src/channels/telegram-mirror/commands.js +0 -8
- package/dist/src/channels/telegram-mirror/status-line.js +32 -2
- package/dist/src/constants.js +16 -2
- package/dist/src/lib/config.js +40 -0
- package/dist/src/openai-compat/non-streaming-handler.js +2 -2
- package/dist/src/openai-compat/streaming-handler.js +2 -2
- package/package.json +3 -2
- package/dist/src/channels/adapter.d.ts +0 -103
- package/dist/src/channels/telegram-mirror/askuser.d.ts +0 -107
- package/dist/src/channels/telegram-mirror/burst-accumulator.d.ts +0 -96
- package/dist/src/channels/telegram-mirror/callback-mapping.d.ts +0 -61
- package/dist/src/channels/telegram-mirror/card-renderer.d.ts +0 -68
- package/dist/src/channels/telegram-mirror/card-state.d.ts +0 -83
- package/dist/src/channels/telegram-mirror/commands.d.ts +0 -183
- package/dist/src/channels/telegram-mirror/compose-buffer.d.ts +0 -71
- package/dist/src/channels/telegram-mirror/cost-views.d.ts +0 -58
- package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.d.ts +0 -21
- package/dist/src/channels/telegram-mirror/failure/gateway-down.d.ts +0 -15
- package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.d.ts +0 -15
- package/dist/src/channels/telegram-mirror/failure/index.d.ts +0 -23
- package/dist/src/channels/telegram-mirror/failure/model-5xx.d.ts +0 -16
- package/dist/src/channels/telegram-mirror/failure/network-blip.d.ts +0 -17
- package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.d.ts +0 -15
- package/dist/src/channels/telegram-mirror/failure/rate-limit.d.ts +0 -16
- package/dist/src/channels/telegram-mirror/failure/returning-after-24h.d.ts +0 -14
- package/dist/src/channels/telegram-mirror/failure/types.d.ts +0 -30
- package/dist/src/channels/telegram-mirror/inbound-handler.d.ts +0 -73
- package/dist/src/channels/telegram-mirror/index.d.ts +0 -32
- package/dist/src/channels/telegram-mirror/plan-attachment.d.ts +0 -120
- package/dist/src/channels/telegram-mirror/quota-reader.d.ts +0 -42
- package/dist/src/channels/telegram-mirror/sessions-keyboard.d.ts +0 -84
- package/dist/src/channels/telegram-mirror/soak-log.d.ts +0 -99
- package/dist/src/channels/telegram-mirror/state-machine.d.ts +0 -113
- package/dist/src/channels/telegram-mirror/status-line.d.ts +0 -51
- package/dist/src/channels/telegram-mirror/sync-commands.d.ts +0 -100
- package/dist/src/channels/telegram-mirror/threshold-watcher.d.ts +0 -54
- package/dist/src/channels/telegram-mirror/turn-bridge.d.ts +0 -125
- package/dist/src/cli/checks/bridge-wiring.d.ts +0 -14
- package/dist/src/cli/checks/config-schema.d.ts +0 -11
- package/dist/src/cli/checks/critical-openclaw-json-keys.d.ts +0 -21
- package/dist/src/cli/checks/install-path.d.ts +0 -11
- package/dist/src/cli/checks/patch-scaffold.d.ts +0 -17
- package/dist/src/cli/doctor.d.ts +0 -20
- package/dist/src/cli/index.d.ts +0 -8
- package/dist/src/cli/migrate.d.ts +0 -29
- package/dist/src/command-router/cc-handler.d.ts +0 -67
- package/dist/src/command-router/index.d.ts +0 -2
- package/dist/src/command-router/launch-policy.d.ts +0 -92
- package/dist/src/command-router/resume-policy.d.ts +0 -18
- package/dist/src/command-router/turn-formatter.d.ts +0 -19
- package/dist/src/config/loader.d.ts +0 -8
- package/dist/src/config/schema.d.ts +0 -192
- package/dist/src/constants.d.ts +0 -191
- package/dist/src/council/build-agent-prompt.d.ts +0 -11
- package/dist/src/council/cleanup-worktrees.d.ts +0 -10
- package/dist/src/council/consensus.d.ts +0 -20
- package/dist/src/council/council.d.ts +0 -67
- package/dist/src/council/index.d.ts +0 -2
- package/dist/src/council/system-prompt.d.ts +0 -16
- package/dist/src/council/write-worktree-claude-md.d.ts +0 -10
- package/dist/src/engines/base-oneshot-session.d.ts +0 -87
- package/dist/src/engines/heartbeat-guard.d.ts +0 -93
- package/dist/src/engines/index.d.ts +0 -8
- package/dist/src/engines/persistent-codex-session.d.ts +0 -16
- package/dist/src/engines/persistent-cursor-session.d.ts +0 -21
- package/dist/src/engines/persistent-custom-session.d.ts +0 -78
- package/dist/src/engines/persistent-gemini-session.d.ts +0 -21
- package/dist/src/engines/persistent-session.d.ts +0 -95
- package/dist/src/engines/resolve-bin.d.ts +0 -14
- package/dist/src/engines/subprocess-pool.d.ts +0 -78
- package/dist/src/health/handler.d.ts +0 -39
- package/dist/src/health/index.d.ts +0 -1
- package/dist/src/health/metrics.d.ts +0 -52
- package/dist/src/index.d.ts +0 -57
- package/dist/src/lib/auto-recovery.d.ts +0 -43
- package/dist/src/lib/cache-parity-decide.d.ts +0 -64
- package/dist/src/lib/cache-parity.d.ts +0 -38
- package/dist/src/lib/cc-cli-scan.d.ts +0 -52
- package/dist/src/lib/circuit-breaker.d.ts +0 -21
- package/dist/src/lib/config-service.d.ts +0 -106
- package/dist/src/lib/config.d.ts +0 -136
- package/dist/src/lib/cost-rollup.d.ts +0 -36
- package/dist/src/lib/debounce.d.ts +0 -12
- package/dist/src/lib/debug-tap.d.ts +0 -13
- package/dist/src/lib/domain-error.d.ts +0 -59
- package/dist/src/lib/drift-detector.d.ts +0 -46
- package/dist/src/lib/env-overrides.d.ts +0 -47
- package/dist/src/lib/error-formatter.d.ts +0 -91
- package/dist/src/lib/error-renderer.d.ts +0 -20
- package/dist/src/lib/heartbeat-config.d.ts +0 -34
- package/dist/src/lib/heartbeat-workaround.d.ts +0 -44
- package/dist/src/lib/html-render.d.ts +0 -50
- package/dist/src/lib/http-agent.d.ts +0 -47
- package/dist/src/lib/index.d.ts +0 -7
- package/dist/src/lib/index.js +0 -10
- package/dist/src/lib/json-array.d.ts +0 -10
- package/dist/src/lib/markdown-to-mdv2.d.ts +0 -53
- package/dist/src/lib/markdown-v2.d.ts +0 -27
- package/dist/src/lib/perf/async-compact.d.ts +0 -26
- package/dist/src/lib/perf/direct-sdk.d.ts +0 -26
- package/dist/src/lib/perf/haiku-route.d.ts +0 -19
- package/dist/src/lib/perf/predictive-continuation.d.ts +0 -18
- package/dist/src/lib/perf/read-batch.d.ts +0 -33
- package/dist/src/lib/perf/skill-list-collapse.d.ts +0 -22
- package/dist/src/lib/perf/speculative-bubble.d.ts +0 -27
- package/dist/src/lib/perf/typing-prefetch.d.ts +0 -25
- package/dist/src/lib/probes.d.ts +0 -50
- package/dist/src/lib/register-guard.d.ts +0 -56
- package/dist/src/lib/req-shape-log.d.ts +0 -31
- package/dist/src/lib/safe-upstream-probes.d.ts +0 -25
- package/dist/src/lib/session-registry.d.ts +0 -66
- package/dist/src/lib/spawn-async.d.ts +0 -18
- package/dist/src/lib/status-tee-reader.d.ts +0 -29
- package/dist/src/lib/sysprompt-strip.d.ts +0 -53
- package/dist/src/lib/telegram-bot-api.d.ts +0 -146
- package/dist/src/lib/telemetry.d.ts +0 -38
- package/dist/src/lib/test-mode.d.ts +0 -26
- package/dist/src/lib/trajectory.d.ts +0 -44
- package/dist/src/lib/vendor-paths.d.ts +0 -12
- package/dist/src/lifecycle/boot.d.ts +0 -48
- package/dist/src/lifecycle/patch-manifest.d.ts +0 -82
- package/dist/src/lifecycle/phase-import-upstream.d.ts +0 -12
- package/dist/src/lifecycle/phase-install-patches.d.ts +0 -12
- package/dist/src/lifecycle/phase-schedule-jobs.d.ts +0 -12
- package/dist/src/lifecycle/phase-start-server.d.ts +0 -11
- package/dist/src/lifecycle/phase-validate-config.d.ts +0 -9
- package/dist/src/lifecycle/phase-validate-upstream.d.ts +0 -11
- package/dist/src/lifecycle/phase-wire-handlers.d.ts +0 -12
- package/dist/src/lifecycle/safe-restart.d.ts +0 -99
- package/dist/src/logger.d.ts +0 -14
- package/dist/src/mcp/bridge.d.ts +0 -21
- package/dist/src/mcp/index.d.ts +0 -2
- package/dist/src/models.d.ts +0 -68
- package/dist/src/observability/event-bus.d.ts +0 -86
- package/dist/src/observability/get-event-bus.d.ts +0 -25
- package/dist/src/observability/observability-service.d.ts +0 -19
- package/dist/src/observability/perf-telemetry.d.ts +0 -65
- package/dist/src/observability/subscribers/metrics.d.ts +0 -11
- package/dist/src/observability/subscribers/session-capture.d.ts +0 -15
- package/dist/src/openai-compat/autonomy-rule.d.ts +0 -26
- package/dist/src/openai-compat/bridges/allowlist.d.ts +0 -19
- package/dist/src/openai-compat/bridges/factory.d.ts +0 -30
- package/dist/src/openai-compat/bridges/media-bridge.d.ts +0 -34
- package/dist/src/openai-compat/bridges/openclaw-api-shim.d.ts +0 -54
- package/dist/src/openai-compat/bridges/openclaw-native-tools.d.ts +0 -61
- package/dist/src/openai-compat/bridges/openclaw-tool-registry.d.ts +0 -26
- package/dist/src/openai-compat/bridges/tts-media-bridge.d.ts +0 -19
- package/dist/src/openai-compat/chat-cwd.d.ts +0 -22
- package/dist/src/openai-compat/cli-stream-parser.d.ts +0 -134
- package/dist/src/openai-compat/index.d.ts +0 -1
- package/dist/src/openai-compat/message-extractor.d.ts +0 -84
- package/dist/src/openai-compat/mode-flags.d.ts +0 -34
- package/dist/src/openai-compat/non-streaming-handler.d.ts +0 -29
- package/dist/src/openai-compat/openai-chunk-types.d.ts +0 -35
- package/dist/src/openai-compat/openai-compat.d.ts +0 -49
- package/dist/src/openai-compat/openai-types.d.ts +0 -71
- package/dist/src/openai-compat/parse-route-body.d.ts +0 -24
- package/dist/src/openai-compat/prompts.d.ts +0 -47
- package/dist/src/openai-compat/request-coalescer.d.ts +0 -77
- package/dist/src/openai-compat/response-formatter.d.ts +0 -33
- package/dist/src/openai-compat/session-key-resolver.d.ts +0 -41
- package/dist/src/openai-compat/skill-resolver.d.ts +0 -59
- package/dist/src/openai-compat/sse-translator.d.ts +0 -51
- package/dist/src/openai-compat/status-reporter.d.ts +0 -30
- package/dist/src/openai-compat/streaming-handler.d.ts +0 -52
- package/dist/src/openai-compat/tool-calls-parser.d.ts +0 -34
- package/dist/src/openai-compat/tool-results-serializer.d.ts +0 -60
- package/dist/src/openai-compat/tts-rule.d.ts +0 -20
- package/dist/src/openai-compat/voice-recovery.d.ts +0 -56
- package/dist/src/patches/cache-parity-registry.d.ts +0 -103
- package/dist/src/patches/claude-md-injection.d.ts +0 -10
- package/dist/src/patches/cwd-redirect.d.ts +0 -10
- package/dist/src/patches/embedded-server-route.d.ts +0 -23
- package/dist/src/patches/pricing-overrides.d.ts +0 -10
- package/dist/src/patches/resume-registry-restore.d.ts +0 -11
- package/dist/src/patches/session-pid-tracking.d.ts +0 -10
- package/dist/src/patches/sysprompt-strip.d.ts +0 -46
- package/dist/src/patches/tools-restoration.d.ts +0 -12
- package/dist/src/persistence/migration-v0.d.ts +0 -24
- package/dist/src/persistence/session-registry.d.ts +0 -58
- package/dist/src/proxy/anthropic-adapter.d.ts +0 -136
- package/dist/src/proxy/handler.d.ts +0 -39
- package/dist/src/proxy/index.d.ts +0 -4
- package/dist/src/proxy/schema-cleaner.d.ts +0 -11
- package/dist/src/proxy/thought-cache.d.ts +0 -19
- package/dist/src/session/embedded-server.d.ts +0 -25
- package/dist/src/session/inbox-manager.d.ts +0 -38
- package/dist/src/session/index.d.ts +0 -3
- package/dist/src/session/persisted-sessions.d.ts +0 -50
- package/dist/src/session/session-manager.d.ts +0 -247
- package/dist/src/session/watchdogs.d.ts +0 -92
- package/dist/src/session-bootstrap/boot-self-heal.d.ts +0 -32
- package/dist/src/session-bootstrap/cwd-patch.d.ts +0 -50
- package/dist/src/session-bootstrap/index.d.ts +0 -3
- package/dist/src/session-bootstrap/resume-registry.d.ts +0 -27
- package/dist/src/session-bootstrap/session-hygiene.d.ts +0 -23
- package/dist/src/session-bootstrap/sysprompt-strip.d.ts +0 -24
- package/dist/src/session-bootstrap/think-conflict-resolver.d.ts +0 -32
- package/dist/src/types/route.d.ts +0 -11
- package/dist/src/types/runtime-config.d.ts +0 -208
- package/dist/src/types/sse.d.ts +0 -29
- package/dist/src/types/tool-bridge.d.ts +0 -82
- package/dist/src/types/upstream.d.ts +0 -580
- package/dist/src/types.d.ts +0 -498
- 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
|
-
|
|
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;
|
package/dist/src/constants.js
CHANGED
|
@@ -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;
|
package/dist/src/lib/config.js
CHANGED
|
@@ -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();
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a1hvdy/cc-openclaw",
|
|
3
|
-
"version": "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 {};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* src/channels/telegram-mirror/burst-accumulator.ts — v0.25.0 M8.
|
|
3
|
-
*
|
|
4
|
-
* Per-chat 5-second settle window for non-slash inbound messages. When
|
|
5
|
-
* the user fires multiple message bubbles within 5s of each other, the
|
|
6
|
-
* accumulator concatenates them into one user-message payload (joined by
|
|
7
|
-
* "\n\n") before delivery to the engine. A gap > 5s flushes the prior
|
|
8
|
-
* buffer immediately and starts a new one.
|
|
9
|
-
*
|
|
10
|
-
* Decision: ADR-007 — 5s is the empirical saddle point. <3s gaps are
|
|
11
|
-
* almost always related thoughts; >8s gaps are almost always new turns.
|
|
12
|
-
* 5s matches the WARMUP_COOLDOWN_MS in typing-prefetch.ts (consistency).
|
|
13
|
-
*
|
|
14
|
-
* Routing responsibility lives in the inbound dispatcher (later milestone):
|
|
15
|
-
* • Slash messages bypass this class — dispatched immediately via
|
|
16
|
-
* dispatchCommand (commands.ts).
|
|
17
|
-
* • Compose-active chats bypass this class — drafts go to ComposeBuffer
|
|
18
|
-
* (compose-buffer.ts). M7 takes priority over M8 per ADR-007.
|
|
19
|
-
* • Everything else flows through submit() here.
|
|
20
|
-
*
|
|
21
|
-
* The flush flow has TWO triggers:
|
|
22
|
-
* 1. A new submit() that arrives AFTER the settle window — that submit
|
|
23
|
-
* returns the prior buffer in `flushed` and starts a fresh buffer.
|
|
24
|
-
* 2. A periodic flushIfReady() check that runs on a timer when no new
|
|
25
|
-
* inbound has arrived since the window opened.
|
|
26
|
-
*
|
|
27
|
-
* Tests inject a deterministic clock; production passes Date.now.
|
|
28
|
-
*/
|
|
29
|
-
export interface BurstAccumulatorOptions {
|
|
30
|
-
/** Settle window in milliseconds (default 5000 per ADR-007). */
|
|
31
|
-
settleMs?: number;
|
|
32
|
-
/** Injectable clock for tests; defaults to Date.now. */
|
|
33
|
-
now?: () => number;
|
|
34
|
-
/** Custom separator between concatenated drafts (default "\n\n"). */
|
|
35
|
-
separator?: string;
|
|
36
|
-
}
|
|
37
|
-
export interface SubmitResult {
|
|
38
|
-
/**
|
|
39
|
-
* When the new submit() arrives AFTER the prior buffer's settle window,
|
|
40
|
-
* the prior buffer is returned here for immediate delivery; the new
|
|
41
|
-
* message starts a fresh buffer in its place. Undefined otherwise.
|
|
42
|
-
*/
|
|
43
|
-
flushed?: string;
|
|
44
|
-
}
|
|
45
|
-
export declare class BurstAccumulator {
|
|
46
|
-
private readonly sessions;
|
|
47
|
-
private readonly settleMs;
|
|
48
|
-
private readonly now;
|
|
49
|
-
private readonly separator;
|
|
50
|
-
constructor(opts?: BurstAccumulatorOptions);
|
|
51
|
-
/**
|
|
52
|
-
* Submit a non-slash inbound message. Returns `flushed` populated when
|
|
53
|
-
* the prior buffer for the chat had timed out and this submit forced
|
|
54
|
-
* its flush; in that case the new message starts a fresh buffer.
|
|
55
|
-
*
|
|
56
|
-
* The returned `flushed` payload is the only value the caller delivers
|
|
57
|
-
* to the engine — the new message itself stays buffered for at least
|
|
58
|
-
* `settleMs` more, so it gets a chance to coalesce with follow-ups.
|
|
59
|
-
*/
|
|
60
|
-
submit(chatId: string, text: string): SubmitResult;
|
|
61
|
-
/**
|
|
62
|
-
* Periodic check — when the settle window has elapsed without any new
|
|
63
|
-
* inbound, returns the buffered payload and clears the session. When
|
|
64
|
-
* the window has NOT yet elapsed, returns undefined and leaves the
|
|
65
|
-
* buffer intact. Returns undefined for chats without an active buffer.
|
|
66
|
-
*
|
|
67
|
-
* Production wiring: call from a 1Hz interval timer per known chatId
|
|
68
|
-
* (or from the inbound dispatcher's idle-tick).
|
|
69
|
-
*/
|
|
70
|
-
flushIfReady(chatId: string): string | undefined;
|
|
71
|
-
/**
|
|
72
|
-
* Force-flush the buffer for a chat regardless of timing. Used when an
|
|
73
|
-
* explicit signal arrives that the current buffer must deliver now —
|
|
74
|
-
* e.g. a slash command was typed (its dispatcher flushes burst first,
|
|
75
|
-
* then routes the slash, so the bursted text reaches the engine before
|
|
76
|
-
* the slash response).
|
|
77
|
-
*/
|
|
78
|
-
flushNow(chatId: string): string | undefined;
|
|
79
|
-
/**
|
|
80
|
-
* True when a buffer exists for the chat (regardless of settle state).
|
|
81
|
-
* Used by the inbound dispatcher to decide whether a slash arrival
|
|
82
|
-
* should trigger flushNow before routing.
|
|
83
|
-
*/
|
|
84
|
-
hasBuffer(chatId: string): boolean;
|
|
85
|
-
/**
|
|
86
|
-
* Number of drafts currently buffered for the chat. Useful for status
|
|
87
|
-
* surfaces and for tests asserting the buffer state.
|
|
88
|
-
*/
|
|
89
|
-
draftCount(chatId: string): number;
|
|
90
|
-
/**
|
|
91
|
-
* Clear all sessions. Test helper.
|
|
92
|
-
*/
|
|
93
|
-
clear(): void;
|
|
94
|
-
private fresh;
|
|
95
|
-
private format;
|
|
96
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* src/channels/telegram-mirror/callback-mapping.ts — v0.25.0 M3.
|
|
3
|
-
*
|
|
4
|
-
* ADR-004 — Telegram's callback_data is capped at 64 bytes UTF-8. For
|
|
5
|
-
* payloads larger than that (most action+slug combinations are), the bot
|
|
6
|
-
* writes a short opaque base36 id into callback_data and stashes the real
|
|
7
|
-
* payload here. TTL prevents unbounded memory growth as users tap through
|
|
8
|
-
* keyboards or never tap at all.
|
|
9
|
-
*
|
|
10
|
-
* Default TTL is 10 minutes per ADR-004 monitoring signal (callback miss
|
|
11
|
-
* rate > 2% in soak → revisit TTL). The class accepts an injectable clock
|
|
12
|
-
* so tests can fast-forward without setTimeout games.
|
|
13
|
-
*
|
|
14
|
-
* No I/O, no global state. Each subsystem that hands out callback_data
|
|
15
|
-
* owns its own CallbackMap instance — keeps ownership boundaries clean
|
|
16
|
-
* for M4 (per-command), M9 (plan keyboards), M11 (cost subview).
|
|
17
|
-
*/
|
|
18
|
-
export interface CallbackMapOptions {
|
|
19
|
-
/** TTL in milliseconds (default 10 minutes per ADR-004). */
|
|
20
|
-
ttlMs?: number;
|
|
21
|
-
/** Injectable clock for tests; defaults to Date.now. */
|
|
22
|
-
now?: () => number;
|
|
23
|
-
/** Length of the opaque id string (default 8 base36 chars ≈ 41 bits entropy). */
|
|
24
|
-
idLength?: number;
|
|
25
|
-
}
|
|
26
|
-
export declare class CallbackMap {
|
|
27
|
-
private readonly entries;
|
|
28
|
-
private readonly ttlMs;
|
|
29
|
-
private readonly now;
|
|
30
|
-
private readonly idLength;
|
|
31
|
-
constructor(opts?: CallbackMapOptions);
|
|
32
|
-
/**
|
|
33
|
-
* Allocate a fresh opaque id and stash the payload behind it. Returns the
|
|
34
|
-
* id, which fits in a Telegram callback_data field with room to spare.
|
|
35
|
-
* Idempotent only at the data-shape level — calling create() with the
|
|
36
|
-
* same payload twice produces two distinct ids.
|
|
37
|
-
*/
|
|
38
|
-
create(payload: unknown): string;
|
|
39
|
-
/**
|
|
40
|
-
* Resolve a callback id to its stashed payload. Returns undefined if the
|
|
41
|
-
* id was never minted OR if it has expired since. Stale entries are
|
|
42
|
-
* dropped lazily on access — no background timer needed.
|
|
43
|
-
*/
|
|
44
|
-
get(id: string): unknown | undefined;
|
|
45
|
-
/**
|
|
46
|
-
* Manually evict an id (e.g., one-shot Approve/Reject buttons after the
|
|
47
|
-
* user taps once). Returns true if an entry was deleted.
|
|
48
|
-
*/
|
|
49
|
-
delete(id: string): boolean;
|
|
50
|
-
/**
|
|
51
|
-
* Current entry count (after pruning expired). Useful for monitoring
|
|
52
|
-
* and the 2% callback-miss-rate signal in soak.
|
|
53
|
-
*/
|
|
54
|
-
size(): number;
|
|
55
|
-
/**
|
|
56
|
-
* Drop everything. Test helper; production code never calls this.
|
|
57
|
-
*/
|
|
58
|
-
clear(): void;
|
|
59
|
-
private prune;
|
|
60
|
-
private randomId;
|
|
61
|
-
}
|