@a1hvdy/cc-openclaw 0.9.1 → 0.10.4
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/completion-summary.js +20 -1
- package/dist/src/channels/telegram/completion-summary.js.map +1 -1
- package/dist/src/constants.d.ts +33 -2
- package/dist/src/constants.js +33 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/lib/config.d.ts +3 -0
- package/dist/src/lib/config.js +46 -0
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/trajectory.d.ts +1 -1
- package/dist/src/lib/trajectory.js.map +1 -1
- package/dist/src/openai-compat/non-streaming-handler.js +52 -3
- package/dist/src/openai-compat/non-streaming-handler.js.map +1 -1
- package/dist/src/openai-compat/openai-compat.js +64 -2
- package/dist/src/openai-compat/openai-compat.js.map +1 -1
- package/dist/src/openai-compat/skill-resolver.d.ts +15 -3
- package/dist/src/openai-compat/skill-resolver.js +30 -6
- package/dist/src/openai-compat/skill-resolver.js.map +1 -1
- package/dist/src/openai-compat/streaming-handler.js +107 -1
- package/dist/src/openai-compat/streaming-handler.js.map +1 -1
- package/dist/src/openai-compat/voice-recovery.d.ts +56 -0
- package/dist/src/openai-compat/voice-recovery.js +231 -0
- package/dist/src/openai-compat/voice-recovery.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +51 -0
- package/dist/src/session/session-manager.js +165 -1
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/session-bootstrap/cwd-patch.d.ts.map +1 -1
- package/dist/tests/auto-recovery.test.js +4 -8
- package/dist/tests/auto-recovery.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/lib/debug-tap.d.ts.map +0 -1
|
@@ -18,9 +18,15 @@
|
|
|
18
18
|
* gets forwarded to Claude CLI via sessionConfig.tools (R3). Both fire,
|
|
19
19
|
* neither blocks the other.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* v0.9.2 (2026-05-11): SKILL.md **bodies** are re-read from disk on
|
|
22
|
+
* every slash invocation; only the directory listing (filename →
|
|
23
|
+
* frontmatter `name:` mapping) is cached, keyed by directory mtime.
|
|
24
|
+
* Pre-v0.9.2 the body was also cached, which surfaced stale content
|
|
25
|
+
* when a user edited a SKILL.md file in place (the dir mtime stayed
|
|
26
|
+
* unchanged, so the cache wasn't refreshed). Analogous in spirit to
|
|
27
|
+
* the upstream OpenClaw 2026.5.7 "clear cached skills snapshots on
|
|
28
|
+
* /new" fix, but narrower. SKILL.md files are small (~few KB); a
|
|
29
|
+
* per-invocation read is ~1ms, well-bounded against rare slash usage.
|
|
24
30
|
*
|
|
25
31
|
* Override path with OPENCLAW_WORKSPACE_SKILLS_DIR env var.
|
|
26
32
|
*/
|
|
@@ -55,6 +61,9 @@ function parseFrontmatter(text) {
|
|
|
55
61
|
}
|
|
56
62
|
return { frontmatter: fm, body: text.slice(m[0].length) };
|
|
57
63
|
}
|
|
64
|
+
/** Build the filename ↔ name index by scanning the workspace skills dir.
|
|
65
|
+
* Reads each SKILL.md only to extract its frontmatter `name:` field — does
|
|
66
|
+
* NOT cache the body. */
|
|
58
67
|
function rebuildIndex() {
|
|
59
68
|
const byName = new Map();
|
|
60
69
|
let mtimeMs = 0;
|
|
@@ -79,11 +88,11 @@ function rebuildIndex() {
|
|
|
79
88
|
catch {
|
|
80
89
|
continue;
|
|
81
90
|
}
|
|
82
|
-
const { frontmatter
|
|
91
|
+
const { frontmatter } = parseFrontmatter(fullText);
|
|
83
92
|
const name = String(frontmatter.name || entry).toLowerCase().trim();
|
|
84
93
|
if (!name)
|
|
85
94
|
continue;
|
|
86
|
-
byName.set(name, { skillDir, skillMd
|
|
95
|
+
byName.set(name, { skillDir, skillMd });
|
|
87
96
|
}
|
|
88
97
|
return { mtimeMs, dir, byName };
|
|
89
98
|
}
|
|
@@ -105,7 +114,22 @@ export function resolveSkillForSlash(name) {
|
|
|
105
114
|
if (!cache || dirMtime > cache.mtimeMs || cache.dir !== currentDir) {
|
|
106
115
|
cache = rebuildIndex();
|
|
107
116
|
}
|
|
108
|
-
|
|
117
|
+
const ref = cache.byName.get(normalized);
|
|
118
|
+
if (!ref)
|
|
119
|
+
return null;
|
|
120
|
+
// v0.9.2: read the SKILL.md body fresh from disk so in-place edits to
|
|
121
|
+
// an existing SKILL.md (no add/remove, dir mtime unchanged) surface
|
|
122
|
+
// immediately. Pre-v0.9.2 the cache stored a stale body until process
|
|
123
|
+
// restart or skill add/remove.
|
|
124
|
+
let fullText;
|
|
125
|
+
try {
|
|
126
|
+
fullText = fs.readFileSync(ref.skillMd, 'utf8');
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const { body } = parseFrontmatter(fullText);
|
|
132
|
+
return { skillDir: ref.skillDir, skillMd: ref.skillMd, body, fullText };
|
|
109
133
|
}
|
|
110
134
|
export function maybeInlineSkill(userText) {
|
|
111
135
|
if (typeof userText !== 'string')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-resolver.js","sourceRoot":"","sources":["../../../src/openai-compat/skill-resolver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"skill-resolver.js","sourceRoot":"","sources":["../../../src/openai-compat/skill-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,6 +41,25 @@ import { formatCompletionChunk } from './response-formatter.js';
|
|
|
41
41
|
import { isToolStreamMode } from './mode-flags.js';
|
|
42
42
|
import { emit as emitTrajectory } from '../lib/trajectory.js';
|
|
43
43
|
import { formatError, ERROR_CODES } from '../lib/error-formatter.js';
|
|
44
|
+
import { getTtsAutoMode } from '../lib/config.js';
|
|
45
|
+
import { applyVoiceRecovery, detectVoiceIntent, hasTtsMarkers, _logVoiceDebug } from './voice-recovery.js';
|
|
46
|
+
/** Coerce a userMessage (string | UserMessageBlock[]) to a flat string
|
|
47
|
+
* for voice-intent detection. Tool-result blocks aren't user prompts. */
|
|
48
|
+
function userMessageToText(msg) {
|
|
49
|
+
if (typeof msg === 'string')
|
|
50
|
+
return msg;
|
|
51
|
+
if (!Array.isArray(msg))
|
|
52
|
+
return '';
|
|
53
|
+
return msg
|
|
54
|
+
.map((b) => {
|
|
55
|
+
if (b && typeof b === 'object' && 'type' in b && b.type === 'text' && typeof b.text === 'string') {
|
|
56
|
+
return b.text;
|
|
57
|
+
}
|
|
58
|
+
return '';
|
|
59
|
+
})
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join('\n');
|
|
62
|
+
}
|
|
44
63
|
export async function handleStreaming(manager, sessionName, model,
|
|
45
64
|
// Phase 2 R4 wire-up: accepts native content-block arrays in tool-stream mode.
|
|
46
65
|
userMessage, completionId, res, hasTools) {
|
|
@@ -89,6 +108,25 @@ userMessage, completionId, res, hasTools) {
|
|
|
89
108
|
// text chunks directly for low latency.
|
|
90
109
|
let bufferedText = '';
|
|
91
110
|
let toolCallsEmitted = 0;
|
|
111
|
+
// v0.10.3: Voice-recovery for streaming path. When user explicitly asks
|
|
112
|
+
// for a voice note AND TTS is configured non-off, switch the no-tools
|
|
113
|
+
// path from "stream chunks immediately" to "buffer chunks then recover
|
|
114
|
+
// + emit at end". The trade-off: ~2s perceived latency vs guaranteed
|
|
115
|
+
// [[tts:text]]...[[/tts:text]] markers reaching OpenClaw's TTS
|
|
116
|
+
// pipeline. Acceptable for voice prompts which are usually short.
|
|
117
|
+
const ttsAuto = getTtsAutoMode();
|
|
118
|
+
const userText = userMessageToText(userMessage);
|
|
119
|
+
const voiceIntent = ttsAuto !== 'off' && detectVoiceIntent(userText);
|
|
120
|
+
_logVoiceDebug('streaming.entry', {
|
|
121
|
+
sessionName,
|
|
122
|
+
userTextPreview: userText.slice(0, 200),
|
|
123
|
+
userTextLen: userText.length,
|
|
124
|
+
ttsAuto,
|
|
125
|
+
voiceIntent,
|
|
126
|
+
useToolStream,
|
|
127
|
+
hasTools,
|
|
128
|
+
});
|
|
129
|
+
let chunkLogCount = 0;
|
|
92
130
|
// v0.7.2 streaming-path backstop: track whether *any* visible content
|
|
93
131
|
// (text chunk OR tool_calls SSE chunk) was ever streamed. If the model
|
|
94
132
|
// uses only CLI built-in tools (Bash/Read/Write) without producing text,
|
|
@@ -102,6 +140,33 @@ userMessage, completionId, res, hasTools) {
|
|
|
102
140
|
await manager.sendMessage(sessionName, userMessage, {
|
|
103
141
|
onChunk: (chunk) => {
|
|
104
142
|
if (useToolStream || !hasTools) {
|
|
143
|
+
// v0.10.3: when voice intent is detected, BUFFER instead of stream.
|
|
144
|
+
// Recovery runs at stream end before final-chunk emission.
|
|
145
|
+
if (voiceIntent && !useToolStream) {
|
|
146
|
+
bufferedText += chunk;
|
|
147
|
+
if (chunk.length > 0)
|
|
148
|
+
streamedAnything = true;
|
|
149
|
+
if (chunkLogCount < 3) {
|
|
150
|
+
_logVoiceDebug('streaming.chunk.buffered', {
|
|
151
|
+
sessionName,
|
|
152
|
+
chunkLen: chunk.length,
|
|
153
|
+
bufferedTextLen: bufferedText.length,
|
|
154
|
+
chunkIdx: chunkLogCount,
|
|
155
|
+
});
|
|
156
|
+
chunkLogCount += 1;
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (chunkLogCount < 3) {
|
|
161
|
+
_logVoiceDebug('streaming.chunk.streamed', {
|
|
162
|
+
sessionName,
|
|
163
|
+
chunkLen: chunk.length,
|
|
164
|
+
voiceIntent,
|
|
165
|
+
useToolStream,
|
|
166
|
+
chunkIdx: chunkLogCount,
|
|
167
|
+
});
|
|
168
|
+
chunkLogCount += 1;
|
|
169
|
+
}
|
|
105
170
|
// Stream text deltas immediately. Tool-stream mode interleaves
|
|
106
171
|
// text and tool_calls chunks naturally — Claude CLI emits text
|
|
107
172
|
// between tool_use blocks, OpenClaw client handles that fine.
|
|
@@ -211,7 +276,48 @@ userMessage, completionId, res, hasTools) {
|
|
|
211
276
|
writeSSE(JSON.stringify(formatCompletionChunk(completionId, model, { content: 'Done.' }, null)));
|
|
212
277
|
streamedAnything = true;
|
|
213
278
|
}
|
|
214
|
-
|
|
279
|
+
// v0.10.3: voice-recovery emission. When voice intent was detected AND
|
|
280
|
+
// chunks were buffered (not streamed), apply recovery to bufferedText
|
|
281
|
+
// (translate <tool_calls> XML for voice tools, or auto-wrap if no
|
|
282
|
+
// [[tts:text]] markers present), then emit the recovered text as a
|
|
283
|
+
// single content chunk before the final chunk. Sets a flag so the
|
|
284
|
+
// other finalization branches don't double-emit.
|
|
285
|
+
let voiceRecoveryEmitted = false;
|
|
286
|
+
_logVoiceDebug('streaming.finalize', {
|
|
287
|
+
sessionName,
|
|
288
|
+
voiceIntent,
|
|
289
|
+
useToolStream,
|
|
290
|
+
hasTools,
|
|
291
|
+
bufferedTextLen: bufferedText.length,
|
|
292
|
+
bufferedTextHasMarkersBefore: hasTtsMarkers(bufferedText),
|
|
293
|
+
streamedAnything,
|
|
294
|
+
toolCallsEmitted,
|
|
295
|
+
});
|
|
296
|
+
if (voiceIntent && !useToolStream && bufferedText) {
|
|
297
|
+
const recovery = applyVoiceRecovery(userText, bufferedText);
|
|
298
|
+
const finalText = recovery.text;
|
|
299
|
+
_logVoiceDebug('streaming.recovery', {
|
|
300
|
+
sessionName,
|
|
301
|
+
recovered: recovery.recovered,
|
|
302
|
+
via: recovery.via,
|
|
303
|
+
finalLen: finalText.length,
|
|
304
|
+
finalSnippet: finalText.slice(0, 300),
|
|
305
|
+
finalHasMarkers: hasTtsMarkers(finalText),
|
|
306
|
+
});
|
|
307
|
+
if (recovery.recovered) {
|
|
308
|
+
emitTrajectory('tool_use', { name: '_voice_recovery', inputKeys: [recovery.via] }, sessionName);
|
|
309
|
+
}
|
|
310
|
+
writeSSE(JSON.stringify(formatCompletionChunk(completionId, model, { content: finalText }, null)));
|
|
311
|
+
const finalChunk = formatCompletionChunk(completionId, model, {}, 'stop');
|
|
312
|
+
if (usage)
|
|
313
|
+
finalChunk.usage = usage;
|
|
314
|
+
writeSSE(JSON.stringify(finalChunk));
|
|
315
|
+
voiceRecoveryEmitted = true;
|
|
316
|
+
}
|
|
317
|
+
if (voiceRecoveryEmitted) {
|
|
318
|
+
// Already finalized via voice-recovery path. Skip the standard branches.
|
|
319
|
+
}
|
|
320
|
+
else if (useToolStream) {
|
|
215
321
|
// R1+R2: tool-stream mode — text + tool_calls already streamed inline.
|
|
216
322
|
// Just emit the final chunk with the right finish_reason.
|
|
217
323
|
const finishReason = toolCallsEmitted > 0 ? 'tool_calls' : 'stop';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming-handler.js","sourceRoot":"","sources":["../../../src/openai-compat/streaming-handler.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streaming-handler.js","sourceRoot":"","sources":["../../../src/openai-compat/streaming-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* voice-recovery — server-side bridge that ensures voice delivery via
|
|
3
|
+
* `[[tts:text]]...[[/tts:text]]` markers even when Savvy doesn't emit them.
|
|
4
|
+
*
|
|
5
|
+
* v0.10.3 turns cc-openclaw from a passive text proxy into an active
|
|
6
|
+
* voice-aware bridge for three failure modes:
|
|
7
|
+
*
|
|
8
|
+
* 1. Savvy emits `<tool_calls>` XML for a native OpenClaw voice tool
|
|
9
|
+
* (e.g. `message.voice`) that cc-openclaw can't execute. The XML
|
|
10
|
+
* lands as raw text in the SSE stream. → `translateVoiceToolCalls`
|
|
11
|
+
* rewrites it as `[[tts:text]]` markers.
|
|
12
|
+
*
|
|
13
|
+
* 2. User explicitly asks for voice but Savvy produces markerless text
|
|
14
|
+
* (the dominant failure observed in the 2026-05-11 evening test).
|
|
15
|
+
* → `autoWrapMissingMarkers` wraps the first sentence/paragraph
|
|
16
|
+
* in markers so OpenClaw's `maybeApplyTtsToPayload` triggers.
|
|
17
|
+
*
|
|
18
|
+
* 3. Hint-loss between turns. → `detectVoiceIntent` gates recovery so
|
|
19
|
+
* it only fires on explicit voice requests, never on accidental
|
|
20
|
+
* voice-keyword matches in non-voice prompts.
|
|
21
|
+
*
|
|
22
|
+
* All functions are PURE — no side effects, no IO, no logging. Tests
|
|
23
|
+
* exercise them directly via `tests/voice-recovery.test.ts`.
|
|
24
|
+
*/
|
|
25
|
+
/** Default max chars inside the spoken block. Matches the TTS_RULE budget. */
|
|
26
|
+
export declare const DEFAULT_MAX_SPOKEN_CHARS = 500;
|
|
27
|
+
/** v0.10.4 — env-gated debug logger for triangulating voice-recovery
|
|
28
|
+
* failure mode on Telegram vs. direct-probe paths. Single-line JSON for
|
|
29
|
+
* grep-ability via `pm2 logs openclaw-gateway | grep _voice_debug`.
|
|
30
|
+
* Disabled unless `CC_OPENCLAW_VOICE_DEBUG=1`. Safe to leave in code
|
|
31
|
+
* permanently — the gate is a single string equality check. */
|
|
32
|
+
export declare function _logVoiceDebug(label: string, fields: Record<string, unknown>): void;
|
|
33
|
+
/** Detect whether the user's prompt explicitly requests a voice note. */
|
|
34
|
+
export declare function detectVoiceIntent(userPrompt: string): boolean;
|
|
35
|
+
/** Check whether the reply text already contains a complete `[[tts:text]]`
|
|
36
|
+
* block (open + close pair). */
|
|
37
|
+
export declare function hasTtsMarkers(text: string): boolean;
|
|
38
|
+
/** If `text` contains a `<tool_calls>` XML block targeting a voice-delivery
|
|
39
|
+
* tool, extract the spoken-text argument. Returns the spoken text or null. */
|
|
40
|
+
export declare function extractTtsToolCallText(text: string): string | null;
|
|
41
|
+
/** Rewrite `text`: if a voice-tool XML block is present, remove it and
|
|
42
|
+
* insert a `[[tts:text]]<spoken>[[/tts:text]]` block in its place. Idempotent
|
|
43
|
+
* when no voice-tool XML is present. */
|
|
44
|
+
export declare function translateVoiceToolCalls(text: string): string;
|
|
45
|
+
/** If `text` is missing voice markers, find a reasonable spoken summary
|
|
46
|
+
* (first paragraph up to `maxSpokenChars`) and wrap it. Returns text with
|
|
47
|
+
* the marker block prepended; the original first paragraph is left in place
|
|
48
|
+
* as text expansion. Idempotent when markers already present. */
|
|
49
|
+
export declare function autoWrapMissingMarkers(text: string, maxSpokenChars?: number): string;
|
|
50
|
+
/** Convenience helper: run the full recovery pipeline on a reply when the
|
|
51
|
+
* user has expressed voice intent. Returns the (possibly rewritten) text. */
|
|
52
|
+
export declare function applyVoiceRecovery(userPrompt: string, replyText: string, maxSpokenChars?: number): {
|
|
53
|
+
text: string;
|
|
54
|
+
recovered: boolean;
|
|
55
|
+
via: 'none' | 'tool-translate' | 'auto-wrap';
|
|
56
|
+
};
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* voice-recovery — server-side bridge that ensures voice delivery via
|
|
3
|
+
* `[[tts:text]]...[[/tts:text]]` markers even when Savvy doesn't emit them.
|
|
4
|
+
*
|
|
5
|
+
* v0.10.3 turns cc-openclaw from a passive text proxy into an active
|
|
6
|
+
* voice-aware bridge for three failure modes:
|
|
7
|
+
*
|
|
8
|
+
* 1. Savvy emits `<tool_calls>` XML for a native OpenClaw voice tool
|
|
9
|
+
* (e.g. `message.voice`) that cc-openclaw can't execute. The XML
|
|
10
|
+
* lands as raw text in the SSE stream. → `translateVoiceToolCalls`
|
|
11
|
+
* rewrites it as `[[tts:text]]` markers.
|
|
12
|
+
*
|
|
13
|
+
* 2. User explicitly asks for voice but Savvy produces markerless text
|
|
14
|
+
* (the dominant failure observed in the 2026-05-11 evening test).
|
|
15
|
+
* → `autoWrapMissingMarkers` wraps the first sentence/paragraph
|
|
16
|
+
* in markers so OpenClaw's `maybeApplyTtsToPayload` triggers.
|
|
17
|
+
*
|
|
18
|
+
* 3. Hint-loss between turns. → `detectVoiceIntent` gates recovery so
|
|
19
|
+
* it only fires on explicit voice requests, never on accidental
|
|
20
|
+
* voice-keyword matches in non-voice prompts.
|
|
21
|
+
*
|
|
22
|
+
* All functions are PURE — no side effects, no IO, no logging. Tests
|
|
23
|
+
* exercise them directly via `tests/voice-recovery.test.ts`.
|
|
24
|
+
*/
|
|
25
|
+
// ── Constants ─────────────────────────────────────────────────────────────
|
|
26
|
+
/** Word-boundary regex for explicit voice-delivery intent in user prompts.
|
|
27
|
+
* Kept TIGHT to avoid false positives: "voice" alone is too broad (could be
|
|
28
|
+
* asking about TTS config); "send a voice note" / "speak" / "in voice" are
|
|
29
|
+
* the intended triggers. */
|
|
30
|
+
const VOICE_INTENT_REGEX = /\b(voice\s*notes?|voice\s*messages?|send\s+(?:me\s+)?(?:a\s+)?voice|in\s+voice|speak\s+(?:it|the|me|to|aloud)|say\s+(?:it|the|me).*aloud|tell\s+me\s+(?:out\s+loud|aloud)|read\s+(?:it|the|this)\s+aloud)\b/i;
|
|
31
|
+
/** Markers OpenClaw's `maybeApplyTtsToPayload` looks for. */
|
|
32
|
+
const TTS_OPEN = '[[tts:text]]';
|
|
33
|
+
const TTS_CLOSE = '[[/tts:text]]';
|
|
34
|
+
/** Default max chars inside the spoken block. Matches the TTS_RULE budget. */
|
|
35
|
+
export const DEFAULT_MAX_SPOKEN_CHARS = 500;
|
|
36
|
+
// ── Diagnostic logging (v0.10.4) ──────────────────────────────────────────
|
|
37
|
+
/** v0.10.4 — env-gated debug logger for triangulating voice-recovery
|
|
38
|
+
* failure mode on Telegram vs. direct-probe paths. Single-line JSON for
|
|
39
|
+
* grep-ability via `pm2 logs openclaw-gateway | grep _voice_debug`.
|
|
40
|
+
* Disabled unless `CC_OPENCLAW_VOICE_DEBUG=1`. Safe to leave in code
|
|
41
|
+
* permanently — the gate is a single string equality check. */
|
|
42
|
+
export function _logVoiceDebug(label, fields) {
|
|
43
|
+
if (process.env.CC_OPENCLAW_VOICE_DEBUG !== '1')
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
const payload = { _voice_debug: label, ...fields, ts: new Date().toISOString() };
|
|
47
|
+
// eslint-disable-next-line no-console
|
|
48
|
+
console.error(JSON.stringify(payload));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Logging must never throw into the request path
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ── Public API ────────────────────────────────────────────────────────────
|
|
55
|
+
/** Detect whether the user's prompt explicitly requests a voice note. */
|
|
56
|
+
export function detectVoiceIntent(userPrompt) {
|
|
57
|
+
if (!userPrompt)
|
|
58
|
+
return false;
|
|
59
|
+
return VOICE_INTENT_REGEX.test(userPrompt);
|
|
60
|
+
}
|
|
61
|
+
/** Check whether the reply text already contains a complete `[[tts:text]]`
|
|
62
|
+
* block (open + close pair). */
|
|
63
|
+
export function hasTtsMarkers(text) {
|
|
64
|
+
if (!text)
|
|
65
|
+
return false;
|
|
66
|
+
const openIdx = text.indexOf(TTS_OPEN);
|
|
67
|
+
if (openIdx === -1)
|
|
68
|
+
return false;
|
|
69
|
+
const closeIdx = text.indexOf(TTS_CLOSE, openIdx + TTS_OPEN.length);
|
|
70
|
+
return closeIdx !== -1;
|
|
71
|
+
}
|
|
72
|
+
/** Pattern matching `<tool_calls>` or `<tool_use>` or `<function_calls>` XML
|
|
73
|
+
* blocks whose `name` attribute looks like a voice/TTS delivery tool.
|
|
74
|
+
* Matches names: `voice`, `speak`, `tts`, `sendVoice`, `send_voice`,
|
|
75
|
+
* `message.voice`, `message_voice`, `messageVoice`, and `message.send` if
|
|
76
|
+
* it carries an `audioAsVoice` or `as_voice` argument. Permissive on
|
|
77
|
+
* purpose — Phase H of the ship plan analyzes the actual tool name from
|
|
78
|
+
* a sysprompt dump and may narrow this in v0.10.4. */
|
|
79
|
+
const VOICE_TOOL_NAME_REGEX = /(?:voice|speak|tts|send[\s_-]?voice|message[.\s_-]?voice|message[.\s_-]?send.*?(?:audioAsVoice|as[\s_-]?voice))/i;
|
|
80
|
+
/** Loose regex catching the common Anthropic-style tool-call XML envelopes. */
|
|
81
|
+
const TOOL_CALL_XML_BLOCK_REGEX = /<(tool_calls?|tool_use|function_calls?)\b[^>]*>([\s\S]*?)<\/\1>/gi;
|
|
82
|
+
/** Inside a tool-call XML block (or the full <tag name="..."> opening),
|
|
83
|
+
* find the `name` attribute or `<name>` field or JSON-style `"name":"..."`. */
|
|
84
|
+
const TOOL_NAME_REGEX = /name\s*=\s*"([^"]+)"|<name>\s*([^<]+?)\s*<\/name>|"name"\s*:\s*"([^"]+)"/i;
|
|
85
|
+
/** Inside a tool-call XML block, extract the spoken text argument. Tries
|
|
86
|
+
* several common shapes: `<text>X</text>`, `<input>X</input>`,
|
|
87
|
+
* `<content>X</content>`, `"text":"X"`, `"input":"X"`, `"content":"X"`.
|
|
88
|
+
* Returns the first non-empty match, trimmed. */
|
|
89
|
+
function extractSpokenText(xmlBody) {
|
|
90
|
+
const xmlTagPatterns = [/<text>([\s\S]*?)<\/text>/i, /<input>([\s\S]*?)<\/input>/i, /<content>([\s\S]*?)<\/content>/i, /<speech>([\s\S]*?)<\/speech>/i];
|
|
91
|
+
for (const re of xmlTagPatterns) {
|
|
92
|
+
const m = xmlBody.match(re);
|
|
93
|
+
if (m && m[1]) {
|
|
94
|
+
const trimmed = m[1].trim();
|
|
95
|
+
if (trimmed)
|
|
96
|
+
return trimmed;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const jsonPatterns = [/"text"\s*:\s*"((?:[^"\\]|\\.)*)"/i, /"input"\s*:\s*"((?:[^"\\]|\\.)*)"/i, /"content"\s*:\s*"((?:[^"\\]|\\.)*)"/i, /"speech"\s*:\s*"((?:[^"\\]|\\.)*)"/i];
|
|
100
|
+
for (const re of jsonPatterns) {
|
|
101
|
+
const m = xmlBody.match(re);
|
|
102
|
+
if (m && m[1]) {
|
|
103
|
+
const decoded = m[1].replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\\\/g, '\\').trim();
|
|
104
|
+
if (decoded)
|
|
105
|
+
return decoded;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/** If `text` contains a `<tool_calls>` XML block targeting a voice-delivery
|
|
111
|
+
* tool, extract the spoken-text argument. Returns the spoken text or null. */
|
|
112
|
+
export function extractTtsToolCallText(text) {
|
|
113
|
+
if (!text)
|
|
114
|
+
return null;
|
|
115
|
+
TOOL_CALL_XML_BLOCK_REGEX.lastIndex = 0;
|
|
116
|
+
let match;
|
|
117
|
+
while ((match = TOOL_CALL_XML_BLOCK_REGEX.exec(text)) !== null) {
|
|
118
|
+
const body = match[2] ?? '';
|
|
119
|
+
// Search the FULL match (including opening tag) so `name="..."` attributes
|
|
120
|
+
// on the outer <tool_use name="speak"> tag are caught. Also handles
|
|
121
|
+
// JSON-style `"name":"..."` payloads inside the body.
|
|
122
|
+
const nameMatch = match[0].match(TOOL_NAME_REGEX);
|
|
123
|
+
const toolName = (nameMatch?.[1] ?? nameMatch?.[2] ?? nameMatch?.[3] ?? '').trim();
|
|
124
|
+
if (toolName && VOICE_TOOL_NAME_REGEX.test(toolName)) {
|
|
125
|
+
const spoken = extractSpokenText(body);
|
|
126
|
+
if (spoken)
|
|
127
|
+
return spoken;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/** Rewrite `text`: if a voice-tool XML block is present, remove it and
|
|
133
|
+
* insert a `[[tts:text]]<spoken>[[/tts:text]]` block in its place. Idempotent
|
|
134
|
+
* when no voice-tool XML is present. */
|
|
135
|
+
export function translateVoiceToolCalls(text) {
|
|
136
|
+
if (!text)
|
|
137
|
+
return text;
|
|
138
|
+
TOOL_CALL_XML_BLOCK_REGEX.lastIndex = 0;
|
|
139
|
+
// Walk the matches, build a replacement.
|
|
140
|
+
let result = '';
|
|
141
|
+
let lastEnd = 0;
|
|
142
|
+
let match;
|
|
143
|
+
TOOL_CALL_XML_BLOCK_REGEX.lastIndex = 0;
|
|
144
|
+
while ((match = TOOL_CALL_XML_BLOCK_REGEX.exec(text)) !== null) {
|
|
145
|
+
const body = match[2] ?? '';
|
|
146
|
+
// Search the FULL match (including opening tag) so `name="..."` attributes
|
|
147
|
+
// on the outer <tool_use name="speak"> tag are caught. Also handles
|
|
148
|
+
// JSON-style `"name":"..."` payloads inside the body.
|
|
149
|
+
const nameMatch = match[0].match(TOOL_NAME_REGEX);
|
|
150
|
+
const toolName = (nameMatch?.[1] ?? nameMatch?.[2] ?? nameMatch?.[3] ?? '').trim();
|
|
151
|
+
if (toolName && VOICE_TOOL_NAME_REGEX.test(toolName)) {
|
|
152
|
+
const spoken = extractSpokenText(body);
|
|
153
|
+
if (spoken) {
|
|
154
|
+
result += text.slice(lastEnd, match.index);
|
|
155
|
+
result += `${TTS_OPEN}${spoken}${TTS_CLOSE}`;
|
|
156
|
+
lastEnd = match.index + match[0].length;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
result += text.slice(lastEnd);
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
/** Truncate at a sentence boundary if possible, otherwise hard-cut.
|
|
164
|
+
* Used to keep the spoken portion within the voice-budget. */
|
|
165
|
+
function truncateAtSentence(text, maxChars) {
|
|
166
|
+
if (text.length <= maxChars)
|
|
167
|
+
return text;
|
|
168
|
+
const slice = text.slice(0, maxChars);
|
|
169
|
+
// Try last sentence-ending punctuation
|
|
170
|
+
const lastSentence = Math.max(slice.lastIndexOf('. '), slice.lastIndexOf('! '), slice.lastIndexOf('? '));
|
|
171
|
+
if (lastSentence > maxChars * 0.5)
|
|
172
|
+
return slice.slice(0, lastSentence + 1);
|
|
173
|
+
// Otherwise hard-cut at last space
|
|
174
|
+
const lastSpace = slice.lastIndexOf(' ');
|
|
175
|
+
if (lastSpace > maxChars * 0.5)
|
|
176
|
+
return slice.slice(0, lastSpace) + '…';
|
|
177
|
+
return slice + '…';
|
|
178
|
+
}
|
|
179
|
+
/** If `text` is missing voice markers, find a reasonable spoken summary
|
|
180
|
+
* (first paragraph up to `maxSpokenChars`) and wrap it. Returns text with
|
|
181
|
+
* the marker block prepended; the original first paragraph is left in place
|
|
182
|
+
* as text expansion. Idempotent when markers already present. */
|
|
183
|
+
export function autoWrapMissingMarkers(text, maxSpokenChars = DEFAULT_MAX_SPOKEN_CHARS) {
|
|
184
|
+
if (!text)
|
|
185
|
+
return text;
|
|
186
|
+
if (hasTtsMarkers(text))
|
|
187
|
+
return text;
|
|
188
|
+
const trimmed = text.trim();
|
|
189
|
+
if (!trimmed)
|
|
190
|
+
return text;
|
|
191
|
+
// Pick the first paragraph (up to the first blank line). The ★ Insight
|
|
192
|
+
// block typically appears below — leave it alone.
|
|
193
|
+
const firstBlankLine = trimmed.search(/\n\s*\n/);
|
|
194
|
+
const firstPara = firstBlankLine === -1 ? trimmed : trimmed.slice(0, firstBlankLine).trim();
|
|
195
|
+
// Strip leading boilerplate Savvy sometimes adds ("Let me send the voice note now.")
|
|
196
|
+
const filtered = firstPara
|
|
197
|
+
.replace(/^(?:i (?:have|got) what i need\.?\s*)?let me send (?:the\s+)?voice (?:note|message)(?:\s+now)?\.?\s*/i, '')
|
|
198
|
+
.replace(/^(?:here(?:'s|\s+is)|on\s+it|sending\s+(?:a\s+)?voice(?:\s+note)?)[:.]?\s*/i, '')
|
|
199
|
+
.trim();
|
|
200
|
+
// Fall back to the original first paragraph if filtering removed everything
|
|
201
|
+
const spokenRaw = filtered || firstPara;
|
|
202
|
+
const spoken = truncateAtSentence(spokenRaw, maxSpokenChars);
|
|
203
|
+
if (!spoken)
|
|
204
|
+
return text;
|
|
205
|
+
// Prepend the marker block; keep the rest of the original text as text expansion.
|
|
206
|
+
// Use \n\n separator so the marker block is visually distinct.
|
|
207
|
+
return `${TTS_OPEN}${spoken}${TTS_CLOSE}\n\n${text}`;
|
|
208
|
+
}
|
|
209
|
+
/** Convenience helper: run the full recovery pipeline on a reply when the
|
|
210
|
+
* user has expressed voice intent. Returns the (possibly rewritten) text. */
|
|
211
|
+
export function applyVoiceRecovery(userPrompt, replyText, maxSpokenChars = DEFAULT_MAX_SPOKEN_CHARS) {
|
|
212
|
+
if (!detectVoiceIntent(userPrompt)) {
|
|
213
|
+
return { text: replyText, recovered: false, via: 'none' };
|
|
214
|
+
}
|
|
215
|
+
// First try translating any tool-call XML
|
|
216
|
+
const translated = translateVoiceToolCalls(replyText);
|
|
217
|
+
if (translated !== replyText && hasTtsMarkers(translated)) {
|
|
218
|
+
return { text: translated, recovered: true, via: 'tool-translate' };
|
|
219
|
+
}
|
|
220
|
+
// If markers already present, nothing to do
|
|
221
|
+
if (hasTtsMarkers(translated)) {
|
|
222
|
+
return { text: translated, recovered: false, via: 'none' };
|
|
223
|
+
}
|
|
224
|
+
// Auto-wrap the first paragraph
|
|
225
|
+
const wrapped = autoWrapMissingMarkers(translated, maxSpokenChars);
|
|
226
|
+
if (wrapped !== translated && hasTtsMarkers(wrapped)) {
|
|
227
|
+
return { text: wrapped, recovered: true, via: 'auto-wrap' };
|
|
228
|
+
}
|
|
229
|
+
return { text: translated, recovered: false, via: 'none' };
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=voice-recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-recovery.js","sourceRoot":"","sources":["../../../src/openai-compat/voice-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,6EAA6E;AAE7E;;;6BAG6B;AAC7B,MAAM,kBAAkB,GACtB,8MAA8M,CAAC;AAEjN,6DAA6D;AAC7D,MAAM,QAAQ,GAAG,cAAc,CAAC;AAChC,MAAM,SAAS,GAAG,eAAe,CAAC;AAElC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C,6EAA6E;AAE7E;;;;gEAIgE;AAChE,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,MAA+B;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG;QAAE,OAAO;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QACjF,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;AACH,CAAC;AAED,6EAA6E;AAE7E,yEAAyE;AACzE,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9B,OAAO,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC7C,CAAC;AAED;iCACiC;AACjC,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,OAAO,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpE,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC;AACzB,CAAC;AAED;;;;;;uDAMuD;AACvD,MAAM,qBAAqB,GACzB,kHAAkH,CAAC;AAErH,+EAA+E;AAC/E,MAAM,yBAAyB,GAC7B,mEAAmE,CAAC;AAEtE;gFACgF;AAChF,MAAM,eAAe,GACnB,2EAA2E,CAAC;AAE9E;;;kDAGkD;AAClD,SAAS,iBAAiB,CAAC,OAAe;IACxC,MAAM,cAAc,GAAG,CAAC,2BAA2B,EAAE,6BAA6B,EAAE,iCAAiC,EAAE,+BAA+B,CAAC,CAAC;IACxJ,KAAK,MAAM,EAAE,IAAI,cAAc,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO;gBAAE,OAAO,OAAO,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,CAAC,mCAAmC,EAAE,oCAAoC,EAAE,sCAAsC,EAAE,qCAAqC,CAAC,CAAC;IAChL,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9F,IAAI,OAAO;gBAAE,OAAO,OAAO,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,yBAAyB,CAAC,SAAS,GAAG,CAAC,CAAC;IACxC,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5B,2EAA2E;QAC3E,oEAAoE;QACpE,sDAAsD;QACtD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACnF,IAAI,QAAQ,IAAI,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;yCAEyC;AACzC,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,yBAAyB,CAAC,SAAS,GAAG,CAAC,CAAC;IACxC,yCAAyC;IACzC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,KAA6B,CAAC;IAClC,yBAAyB,CAAC,SAAS,GAAG,CAAC,CAAC;IACxC,OAAO,CAAC,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5B,2EAA2E;QAC3E,oEAAoE;QACpE,sDAAsD;QACtD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACnF,IAAI,QAAQ,IAAI,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC3C,MAAM,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,EAAE,CAAC;gBAC7C,OAAO,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAC1C,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;+DAC+D;AAC/D,SAAS,kBAAkB,CAAC,IAAY,EAAE,QAAgB;IACxD,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACtC,uCAAuC;IACvC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACzG,IAAI,YAAY,GAAG,QAAQ,GAAG,GAAG;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,GAAG,CAAC,CAAC,CAAC;IAC3E,mCAAmC;IACnC,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,SAAS,GAAG,QAAQ,GAAG,GAAG;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,GAAG,CAAC;IACvE,OAAO,KAAK,GAAG,GAAG,CAAC;AACrB,CAAC;AAED;;;kEAGkE;AAClE,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,iBAAyB,wBAAwB;IAEjD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,uEAAuE;IACvE,kDAAkD;IAClD,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5F,qFAAqF;IACrF,MAAM,QAAQ,GAAG,SAAS;SACvB,OAAO,CAAC,uGAAuG,EAAE,EAAE,CAAC;SACpH,OAAO,CAAC,6EAA6E,EAAE,EAAE,CAAC;SAC1F,IAAI,EAAE,CAAC;IACV,4EAA4E;IAC5E,MAAM,SAAS,GAAG,QAAQ,IAAI,SAAS,CAAC;IACxC,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC7D,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,kFAAkF;IAClF,+DAA+D;IAC/D,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,OAAO,IAAI,EAAE,CAAC;AACvD,CAAC;AAED;8EAC8E;AAC9E,MAAM,UAAU,kBAAkB,CAChC,UAAkB,EAClB,SAAiB,EACjB,iBAAyB,wBAAwB;IAEjD,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAC5D,CAAC;IACD,0CAA0C;IAC1C,MAAM,UAAU,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,UAAU,KAAK,SAAS,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC;IACtE,CAAC;IACD,4CAA4C;IAC5C,IAAI,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAC7D,CAAC;IACD,gCAAgC;IAChC,MAAM,OAAO,GAAG,sBAAsB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACnE,IAAI,OAAO,KAAK,UAAU,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;IAC9D,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AAC7D,CAAC"}
|
|
@@ -28,6 +28,8 @@ export declare class SessionManager {
|
|
|
28
28
|
private sessions;
|
|
29
29
|
private _pendingSessions;
|
|
30
30
|
private cleanupTimer;
|
|
31
|
+
private stalledWatchTimer;
|
|
32
|
+
private _recentSpawns;
|
|
31
33
|
private pluginConfig;
|
|
32
34
|
private persistedSessions;
|
|
33
35
|
private _debouncedSave;
|
|
@@ -207,5 +209,54 @@ export declare class SessionManager {
|
|
|
207
209
|
}): UltrareviewResult;
|
|
208
210
|
ultrareviewStatus(id: string): UltrareviewResult | undefined;
|
|
209
211
|
private _cleanupIdleSessions;
|
|
212
|
+
/**
|
|
213
|
+
* v0.10.0 — runtime stalled-session watchdog.
|
|
214
|
+
*
|
|
215
|
+
* Fires every STALLED_WATCH_INTERVAL_MS. For each session that is
|
|
216
|
+
* currently `isBusy === true` (mid-turn) AND whose underlying
|
|
217
|
+
* PersistentClaudeSession has not received any subprocess event for
|
|
218
|
+
* STALLED_SESSION_KILL_MS, the watchdog:
|
|
219
|
+
*
|
|
220
|
+
* 1. Logs the stall
|
|
221
|
+
* 2. Emits a `session_stalled_killed` trajectory event
|
|
222
|
+
* 3. Calls session.stop() (SIGTERM, then SIGKILL after STOP_SIGKILL_DELAY_MS)
|
|
223
|
+
* 4. Removes the entry from the in-memory `sessions` Map
|
|
224
|
+
*
|
|
225
|
+
* The in-flight `sendMessage()` promise will reject with the existing
|
|
226
|
+
* `TURN_TIMEOUT_MS` error or a session-stop error. The outer agent-runner
|
|
227
|
+
* then fast-fails to the cross-engine fallback (`openai-codex/gpt-5.4`)
|
|
228
|
+
* rather than waiting the full provider envelope (900s).
|
|
229
|
+
*
|
|
230
|
+
* Threshold is overridable via `CC_OPENCLAW_STALLED_KILL_MS` env var.
|
|
231
|
+
*
|
|
232
|
+
* Mirrors `gateway-pm2-wrapper.sh:53-60` boot-time orphan reaper.
|
|
233
|
+
*/
|
|
234
|
+
private _watchStalledSessions;
|
|
235
|
+
/**
|
|
236
|
+
* Resolve the stalled-session kill threshold. Env var
|
|
237
|
+
* `CC_OPENCLAW_STALLED_KILL_MS` overrides STALLED_SESSION_KILL_MS at
|
|
238
|
+
* runtime so the value can be tuned without rebuild.
|
|
239
|
+
*/
|
|
240
|
+
private _stalledThresholdMs;
|
|
241
|
+
/**
|
|
242
|
+
* v0.10.1 — record a fresh subprocess spawn for the runaway-loop
|
|
243
|
+
* watchdog. Trims entries older than RUNAWAY_LOOP_WINDOW_MS so the
|
|
244
|
+
* array length is bounded.
|
|
245
|
+
*/
|
|
246
|
+
private _recordSpawn;
|
|
247
|
+
/**
|
|
248
|
+
* v0.10.1 — true when the spawn rate over RUNAWAY_LOOP_WINDOW_MS
|
|
249
|
+
* exceeds the configured threshold (env-overridable). Called by
|
|
250
|
+
* `_doStartSession` BEFORE recording the new spawn — i.e. the check
|
|
251
|
+
* fires when the (N+1)-th spawn attempt would push the count over.
|
|
252
|
+
*/
|
|
253
|
+
private _isRunawayLoop;
|
|
254
|
+
/**
|
|
255
|
+
* v0.10.1 — resolve the runaway-loop spawn threshold. Env var
|
|
256
|
+
* `CC_OPENCLAW_LOOP_MAX_SUBPROCS` overrides RUNAWAY_LOOP_MAX_SUBPROCS
|
|
257
|
+
* for runtime tuning without rebuild. Clamped to [2, 20] to prevent
|
|
258
|
+
* accidental disable (0/1) or unbounded raise.
|
|
259
|
+
*/
|
|
260
|
+
private _runawayThreshold;
|
|
210
261
|
}
|
|
211
262
|
export {};
|