@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.
- 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/cache-parity-decide.js +54 -0
- 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/dist/src/session-bootstrap/cwd-patch.js +61 -1
- 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.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/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;
|
|
@@ -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
|
+
}
|
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();
|
|
@@ -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
|
-
|
|
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.
|
|
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 {};
|