@a1hvdy/cc-openclaw 0.18.1 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/src/channels/telegram/completion-summary.js +2 -24
- package/dist/src/channels/telegram/completion-summary.js.map +1 -1
- package/dist/src/channels/telegram/event-reducer.js +70 -16
- package/dist/src/channels/telegram/event-reducer.js.map +1 -1
- package/dist/src/channels/telegram/live-card.d.ts +23 -90
- package/dist/src/channels/telegram/live-card.js +97 -284
- package/dist/src/channels/telegram/live-card.js.map +1 -1
- package/dist/src/channels/telegram/tool-tracker.d.ts +2 -16
- package/dist/src/channels/telegram/tool-tracker.js +18 -81
- package/dist/src/channels/telegram/tool-tracker.js.map +1 -1
- package/dist/src/config/loader.js +0 -2
- package/dist/src/config/loader.js.map +1 -1
- package/dist/src/config/schema.d.ts +1 -20
- package/dist/src/config/schema.js +1 -12
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/constants.d.ts +1 -13
- package/dist/src/constants.js +1 -13
- package/dist/src/constants.js.map +1 -1
- package/dist/src/engines/heartbeat-guard.d.ts +91 -0
- package/dist/src/engines/heartbeat-guard.js +120 -0
- package/dist/src/engines/heartbeat-guard.js.map +1 -0
- package/dist/src/engines/index.d.ts +2 -0
- package/dist/src/engines/index.js +3 -0
- package/dist/src/engines/index.js.map +1 -1
- package/dist/src/engines/persistent-session.d.ts +9 -0
- package/dist/src/engines/persistent-session.js +47 -0
- package/dist/src/engines/persistent-session.js.map +1 -1
- package/dist/src/engines/subprocess-pool.d.ts +78 -0
- package/dist/src/engines/subprocess-pool.js +200 -0
- package/dist/src/engines/subprocess-pool.js.map +1 -0
- package/dist/src/lib/config.d.ts +0 -6
- package/dist/src/lib/config.js +0 -81
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/cost-rollup.d.ts +36 -0
- package/dist/src/lib/cost-rollup.js +125 -0
- package/dist/src/lib/cost-rollup.js.map +1 -0
- package/dist/src/lib/sysprompt-strip.d.ts +0 -14
- package/dist/src/lib/sysprompt-strip.js +2 -79
- package/dist/src/lib/sysprompt-strip.js.map +1 -1
- package/dist/src/lib/trajectory.d.ts +10 -1
- package/dist/src/lib/trajectory.js +37 -6
- package/dist/src/lib/trajectory.js.map +1 -1
- package/dist/src/lifecycle/safe-restart.d.ts +99 -0
- package/dist/src/lifecycle/safe-restart.js +132 -0
- package/dist/src/lifecycle/safe-restart.js.map +1 -0
- package/dist/src/observability/event-bus.d.ts +21 -0
- package/dist/src/observability/event-bus.js.map +1 -1
- package/dist/src/openai-compat/message-extractor.d.ts +5 -0
- package/dist/src/openai-compat/message-extractor.js +7 -4
- package/dist/src/openai-compat/message-extractor.js.map +1 -1
- package/dist/src/openai-compat/non-streaming-handler.d.ts +4 -1
- package/dist/src/openai-compat/non-streaming-handler.js +62 -39
- package/dist/src/openai-compat/non-streaming-handler.js.map +1 -1
- package/dist/src/openai-compat/openai-compat.js +2 -25
- package/dist/src/openai-compat/openai-compat.js.map +1 -1
- package/dist/src/openai-compat/response-formatter.d.ts +1 -6
- package/dist/src/openai-compat/response-formatter.js +2 -9
- package/dist/src/openai-compat/response-formatter.js.map +1 -1
- package/dist/src/openai-compat/skill-resolver.d.ts +12 -0
- package/dist/src/openai-compat/skill-resolver.js +19 -0
- package/dist/src/openai-compat/skill-resolver.js.map +1 -1
- package/dist/src/openai-compat/streaming-handler.d.ts +4 -1
- package/dist/src/openai-compat/streaming-handler.js +126 -36
- package/dist/src/openai-compat/streaming-handler.js.map +1 -1
- package/dist/src/patches/cache-parity-registry.d.ts +83 -0
- package/dist/src/patches/cache-parity-registry.js +151 -1
- package/dist/src/patches/cache-parity-registry.js.map +1 -1
- package/dist/src/patches/sysprompt-strip.spec.d.ts +33 -0
- package/dist/src/patches/sysprompt-strip.spec.js +53 -0
- package/dist/src/patches/sysprompt-strip.spec.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +4 -8
- package/dist/src/session/session-manager.js +76 -86
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/session-bootstrap/cwd-patch.js +0 -35
- package/dist/src/session-bootstrap/cwd-patch.js.map +1 -1
- package/mcp-tools.json +1 -1
- package/package.json +1 -1
- package/dist/src/lib/turn-trace.d.ts +0 -34
- package/dist/src/lib/turn-trace.js +0 -75
- package/dist/src/lib/turn-trace.js.map +0 -1
- package/dist/src/openai-compat/insight-format.d.ts +0 -36
- package/dist/src/openai-compat/insight-format.js +0 -140
- package/dist/src/openai-compat/insight-format.js.map +0 -1
|
@@ -14,50 +14,7 @@
|
|
|
14
14
|
* Source: savvy-claude-code/cwd-enhancer.js lines 800–856 (inside applyRoutePatch)
|
|
15
15
|
* Original P1 port: src/session-bootstrap/sysprompt-strip.ts
|
|
16
16
|
*/
|
|
17
|
-
import { getAggressiveStripEnabled
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Insight extractor (T1 — empty-insight defense)
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
/** Matches the last ★ Insight ─ … ─ fence in assistant output. */
|
|
22
|
-
const INSIGHT_FENCE_RE_STRIP = /★\s*Insight\s*[─—\-]{3,}[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]*[─—\-]{3,}[ \t]*$/mg;
|
|
23
|
-
const MIN_INSIGHT_CHARS_STRIP = 20;
|
|
24
|
-
const MAX_INSIGHT_CHARS_STRIP = 350;
|
|
25
|
-
/**
|
|
26
|
-
* Extract the last ★ Insight block from assistant text, applying the same
|
|
27
|
-
* empty-body and length guards as the renderer-side tips-engine.
|
|
28
|
-
*
|
|
29
|
-
* Returns null when:
|
|
30
|
-
* - no fence is found
|
|
31
|
-
* - the body is whitespace-only
|
|
32
|
-
* - the body is shorter than MIN_INSIGHT_CHARS (20)
|
|
33
|
-
*
|
|
34
|
-
* Used by trajectory.ts to avoid emitting empty insight_strip events.
|
|
35
|
-
*/
|
|
36
|
-
export function extractInsight(assistantText) {
|
|
37
|
-
if (!assistantText || typeof assistantText !== 'string')
|
|
38
|
-
return null;
|
|
39
|
-
let match;
|
|
40
|
-
let last = null;
|
|
41
|
-
INSIGHT_FENCE_RE_STRIP.lastIndex = 0;
|
|
42
|
-
while ((match = INSIGHT_FENCE_RE_STRIP.exec(assistantText)) !== null) {
|
|
43
|
-
last = match[1];
|
|
44
|
-
}
|
|
45
|
-
if (!last)
|
|
46
|
-
return null;
|
|
47
|
-
const cleaned = last
|
|
48
|
-
.replace(/\r/g, '')
|
|
49
|
-
.replace(/\n+/g, ' ')
|
|
50
|
-
.replace(/\s+/g, ' ')
|
|
51
|
-
.trim();
|
|
52
|
-
if (!cleaned)
|
|
53
|
-
return null;
|
|
54
|
-
if (cleaned.length < MIN_INSIGHT_CHARS_STRIP)
|
|
55
|
-
return null;
|
|
56
|
-
if (cleaned.length > MAX_INSIGHT_CHARS_STRIP) {
|
|
57
|
-
return cleaned.slice(0, MAX_INSIGHT_CHARS_STRIP - 1).trimEnd() + '…';
|
|
58
|
-
}
|
|
59
|
-
return cleaned;
|
|
60
|
-
}
|
|
17
|
+
import { getAggressiveStripEnabled } from './config.js';
|
|
61
18
|
// v0.6.0: regex literals lifted to module scope to avoid recompilation on
|
|
62
19
|
// every stripSysprompt() call. With cwd-patch firing strip on every chat
|
|
63
20
|
// completion, that was 4-6 regex compilations per turn → 0.
|
|
@@ -65,28 +22,6 @@ const RE_SELF_IMPROVEMENT_FILENAME = /\n?##\s+SELF_IMPROVEMENT_REMINDER\.md(?=\n
|
|
|
65
22
|
const RE_SELF_IMPROVEMENT_BLOCK = /\n?##\s+Self-Improvement Reminder[\s\S]*?(?=\n##\s|\n?$)/g;
|
|
66
23
|
const RE_LOCATION_LINES = /\n\s*<location>[^<]*<\/location>/g;
|
|
67
24
|
const RE_TOOL_ESCALATION_BLOCK = /\n\*\*Tool escalation:\*\*\n[\s\S]*?(?=\n\*\*Scope selection rule:\*\*)/g;
|
|
68
|
-
// ── Track C v0.18 — Cold-prompt strip V2 ────────────────────────────────
|
|
69
|
-
// Opt-in via OPENCLAW_COLD_PROMPT_STRIP_V2=1. Adds 3 strips for
|
|
70
|
-
// OpenClaw-injected workspace-file bloat that the audit identified
|
|
71
|
-
// (sysprompt-cost.jsonl baseline ~8,342 tok cold turn).
|
|
72
|
-
// 1. MEMORY.md "Promoted From Short-Term Memory" tails — auto-generated
|
|
73
|
-
// low-signal sections with [score=...] markup. Match the dated heading
|
|
74
|
-
// through to the next `## /home/` injected-file boundary or EOF.
|
|
75
|
-
// Workspace File injects use `## /home/a1xai/.openclaw/workspace/X.md`
|
|
76
|
-
// as the framing header (see system prompt format).
|
|
77
|
-
const RE_MEMORY_PROMOTED_TAIL = /\n## Promoted From Short-Term Memory[\s\S]*?(?=\n## \/home\/|$)/g;
|
|
78
|
-
// 2. IDENTITY.md stub — single HTML-comment line wrapped between the file
|
|
79
|
-
// header and the next `## /home/` boundary. SOUL.md is the canonical
|
|
80
|
-
// identity source; IDENTITY.md is a 1-line back-compat shim per the
|
|
81
|
-
// file's own comment ("Minimal stub kept to satisfy auto-restore").
|
|
82
|
-
const RE_IDENTITY_STUB_BLOCK = /\n## \/home\/[^\n]+\/IDENTITY\.md\n<!--[^\n]*-->\n*(?=## \/home\/|$)/g;
|
|
83
|
-
// 3. HEARTBEAT.md when disabled — file is wrapped in a `<!-- DISABLED`
|
|
84
|
-
// HTML comment with no live `tasks:` block (upstream-broken heartbeat
|
|
85
|
-
// model override #9556/#13009/#14279; the file is dormant). Strip the
|
|
86
|
-
// entire injected block including its header.
|
|
87
|
-
// Lookahead stops at the next `## ` heading (either a `## /home/` workspace
|
|
88
|
-
// inject or any other H2 like `## Messaging`) — empty cold prompts hit EOF.
|
|
89
|
-
const RE_HEARTBEAT_DISABLED_BLOCK = /\n## \/home\/[^\n]+\/HEARTBEAT\.md\n# HEARTBEAT\.md[^\n]*\n\n<!-- DISABLED[\s\S]*?(?=\n## |$)/g;
|
|
90
25
|
/**
|
|
91
26
|
* Resolve whether strip should run based on env + options.
|
|
92
27
|
*
|
|
@@ -134,19 +69,7 @@ export function stripSysprompt(content, opts = {}) {
|
|
|
134
69
|
// F2.2 (REMOVED 2026-04-25) — The 45-bullet tool-name enumeration no longer appears.
|
|
135
70
|
// Re-add only if bullets reappear in a future runtime.
|
|
136
71
|
const aggressiveStripped = stripped !== beforeAggr;
|
|
137
|
-
// Track C v0.18 — Cold-prompt strip V2 (opt-in OPENCLAW_COLD_PROMPT_STRIP_V2=1).
|
|
138
|
-
// Layered on top of aggressive strip. Targets OpenClaw workspace-file inject
|
|
139
|
-
// bloat that direct CLI doesn't carry. Safe to disable: just unset the env.
|
|
140
|
-
let coldPromptV2Stripped = false;
|
|
141
|
-
if (getColdPromptStripV2Enabled()) {
|
|
142
|
-
const beforeV2 = stripped;
|
|
143
|
-
stripped = stripped
|
|
144
|
-
.replace(RE_MEMORY_PROMOTED_TAIL, '')
|
|
145
|
-
.replace(RE_IDENTITY_STUB_BLOCK, '')
|
|
146
|
-
.replace(RE_HEARTBEAT_DISABLED_BLOCK, '');
|
|
147
|
-
coldPromptV2Stripped = stripped !== beforeV2;
|
|
148
|
-
}
|
|
149
72
|
const changed = stripped !== original;
|
|
150
|
-
return { content: stripped, changed, aggressiveStripped
|
|
73
|
+
return { content: stripped, changed, aggressiveStripped };
|
|
151
74
|
}
|
|
152
75
|
//# sourceMappingURL=sysprompt-strip.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sysprompt-strip.js","sourceRoot":"","sources":["../../../src/lib/sysprompt-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,yBAAyB,EAAE,
|
|
1
|
+
{"version":3,"file":"sysprompt-strip.js","sourceRoot":"","sources":["../../../src/lib/sysprompt-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExD,0EAA0E;AAC1E,yEAAyE;AACzE,4DAA4D;AAC5D,MAAM,4BAA4B,GAAG,gDAAgD,CAAC;AACtF,MAAM,yBAAyB,GAAG,2DAA2D,CAAC;AAC9F,MAAM,iBAAiB,GAAG,mCAAmC,CAAC;AAC9D,MAAM,wBAAwB,GAAG,0EAA0E,CAAC;AAiB5G;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAqB,EAAE;IACpD,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5C,mEAAmE;IACnE,OAAO,yBAAyB,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,OAAqB,EAAE;IACrE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC;IAEzB,oEAAoE;IACpE,IAAI,QAAQ,GAAG,OAAO;SACnB,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;SACzC,OAAO,CAAC,yBAAyB,EAAE,EAAE,CAAC;SACtC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAElC,2EAA2E;IAC3E,gEAAgE;IAChE,MAAM,UAAU,GAAG,QAAQ,CAAC;IAC5B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;IAC5D,qFAAqF;IACrF,uDAAuD;IACvD,MAAM,kBAAkB,GAAG,QAAQ,KAAK,UAAU,CAAC;IAEnD,MAAM,OAAO,GAAG,QAAQ,KAAK,QAAQ,CAAC;IAEtC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC5D,CAAC"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Size-based rotation deferred to v0.4.x. v0.4.0 ships unbounded; if file
|
|
15
15
|
* exceeds 100 MB, run `gzip cc-openclaw-trajectory.jsonl && rm` manually.
|
|
16
16
|
*/
|
|
17
|
-
export type EventType = 'session_start' | 'session_end' | 'request_in' | 'response_complete' | 'tool_use' | 'tool_result' | 'fallback_step' | 'drift_warning' | 'budget_event' | 'error' | 'thinking_block' | 'session_stalled_killed' | 'runaway_loop_killed' | '
|
|
17
|
+
export type EventType = 'session_start' | 'session_end' | 'request_in' | 'response_complete' | 'tool_use' | 'tool_result' | 'fallback_step' | 'drift_warning' | 'budget_event' | 'error' | 'thinking_block' | 'session_stalled_killed' | 'runaway_loop_killed' | 'turn_complete' | 'session_died';
|
|
18
18
|
export interface TrajectoryEvent {
|
|
19
19
|
ts: string;
|
|
20
20
|
sessionId?: string;
|
|
@@ -29,6 +29,15 @@ export interface TrajectoryEvent {
|
|
|
29
29
|
* throughput is needed in v0.4.x, swap to a buffered write stream.
|
|
30
30
|
*/
|
|
31
31
|
export declare function emit(eventType: EventType, payload?: Record<string, unknown>, sessionId?: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* v0.14.0 — turn-trace probe. Emits a `turn_complete` event independent of
|
|
34
|
+
* the main trajectory gate so A1 can capture per-turn diagnostics
|
|
35
|
+
* (userPrompt → tools → output → backstop) without flipping on the full
|
|
36
|
+
* event firehose. Gated by CC_OPENCLAW_TURN_TRACE=1; also fires when the
|
|
37
|
+
* primary CC_OPENCLAW_TRAJECTORY gate is on (so trajectory consumers see
|
|
38
|
+
* turn_complete in the normal stream too).
|
|
39
|
+
*/
|
|
40
|
+
export declare function emitTurnTrace(payload: Record<string, unknown>, sessionId?: string): void;
|
|
32
41
|
/** Reset module state for tests (re-checks env, re-creates dir on next emit). */
|
|
33
42
|
export declare function _resetForTests(): void;
|
|
34
43
|
/** Read all events from current trajectory file. Test-only. */
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { homedir } from 'node:os';
|
|
21
|
-
import * as turnTrace from './turn-trace.js';
|
|
22
21
|
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
23
22
|
const DEFAULT_PATH = join(homedir(), '.openclaw', 'workspace', 'memory', 'cc-openclaw-trajectory.jsonl');
|
|
24
23
|
function isEnabled() {
|
|
@@ -27,6 +26,12 @@ function isEnabled() {
|
|
|
27
26
|
return false;
|
|
28
27
|
return v === '1' || v.toLowerCase() === 'true' || v.toLowerCase() === 'on';
|
|
29
28
|
}
|
|
29
|
+
function isTurnTraceEnabled() {
|
|
30
|
+
const v = process.env.CC_OPENCLAW_TURN_TRACE;
|
|
31
|
+
if (!v)
|
|
32
|
+
return false;
|
|
33
|
+
return v === '1' || v.toLowerCase() === 'true' || v.toLowerCase() === 'on';
|
|
34
|
+
}
|
|
30
35
|
function getPath() {
|
|
31
36
|
return process.env.CC_OPENCLAW_TRAJECTORY_PATH || DEFAULT_PATH;
|
|
32
37
|
}
|
|
@@ -67,11 +72,37 @@ export function emit(eventType, payload, sessionId) {
|
|
|
67
72
|
// endpoint's errors_total counter still records the underlying error
|
|
68
73
|
// via the calling code's error-formatter path.
|
|
69
74
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* v0.14.0 — turn-trace probe. Emits a `turn_complete` event independent of
|
|
78
|
+
* the main trajectory gate so A1 can capture per-turn diagnostics
|
|
79
|
+
* (userPrompt → tools → output → backstop) without flipping on the full
|
|
80
|
+
* event firehose. Gated by CC_OPENCLAW_TURN_TRACE=1; also fires when the
|
|
81
|
+
* primary CC_OPENCLAW_TRAJECTORY gate is on (so trajectory consumers see
|
|
82
|
+
* turn_complete in the normal stream too).
|
|
83
|
+
*/
|
|
84
|
+
export function emitTurnTrace(payload, sessionId) {
|
|
85
|
+
if (!isEnabled() && !isTurnTraceEnabled())
|
|
86
|
+
return;
|
|
87
|
+
const event = {
|
|
88
|
+
ts: new Date().toISOString(),
|
|
89
|
+
eventType: 'turn_complete',
|
|
90
|
+
laptopId: getLaptopId(),
|
|
91
|
+
payload,
|
|
92
|
+
};
|
|
93
|
+
if (sessionId)
|
|
94
|
+
event.sessionId = sessionId;
|
|
95
|
+
try {
|
|
96
|
+
const path = getPath();
|
|
97
|
+
if (!_ensuredDir) {
|
|
98
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
99
|
+
_ensuredDir = true;
|
|
100
|
+
}
|
|
101
|
+
appendFileSync(path, JSON.stringify(event) + '\n', 'utf8');
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Observability path — never break production on disk-full / perms.
|
|
105
|
+
}
|
|
75
106
|
}
|
|
76
107
|
// ─── Test helpers ────────────────────────────────────────────────────────────
|
|
77
108
|
/** Reset module state for tests (re-checks env, re-creates dir on next emit). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trajectory.js","sourceRoot":"","sources":["../../../src/lib/trajectory.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"trajectory.js","sourceRoot":"","sources":["../../../src/lib/trajectory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* safe-restart — in-process gateway restart that preserves the current
|
|
3
|
+
* Telegram session.
|
|
4
|
+
*
|
|
5
|
+
* Pre-Execution Decision (PRP_v2 §Pre-Execution Decisions, locked):
|
|
6
|
+
* Path: --safe-current-session flag (in-process).
|
|
7
|
+
* Rationale: avoids tmux zombie risk (Risk Register row 3); cleaner state
|
|
8
|
+
* model; tmux-detach remains available as a secondary path (v0.17 deferred).
|
|
9
|
+
*
|
|
10
|
+
* Usage (programmatic):
|
|
11
|
+
* const restart = new SafeRestart({ sessionId: 'my-session' });
|
|
12
|
+
* const result = await restart.run();
|
|
13
|
+
*
|
|
14
|
+
* Usage (CLI flag):
|
|
15
|
+
* cc-openclaw --safe-current-session [--session-id <id>]
|
|
16
|
+
* Parsed in src/cli/index.ts and forwarded here.
|
|
17
|
+
*
|
|
18
|
+
* Self-survival contract:
|
|
19
|
+
* When OpenClaw triggers a gateway restart while a Telegram session is
|
|
20
|
+
* active, the session must NOT fire a sub-agent "self-survival" turn. This
|
|
21
|
+
* module registers a SIGTERM handler that:
|
|
22
|
+
* 1. Flushes in-flight state (EventBus drain-wait up to FLUSH_TIMEOUT_MS).
|
|
23
|
+
* 2. Marks the named session as restart-pending in the session registry.
|
|
24
|
+
* 3. Emits a 'safe-restart.initiated' telemetry event.
|
|
25
|
+
* 4. Returns "in-process" — the gateway respawner picks this signal up and
|
|
26
|
+
* skips the self-survival sub-agent path.
|
|
27
|
+
*
|
|
28
|
+
* SecondaryPath — tmux-detach:
|
|
29
|
+
* See scripts/gateway-detach.sh. NOT the default per locked decision above.
|
|
30
|
+
* SafeRestart.run() returns { mode: 'not-implemented' } when forced into
|
|
31
|
+
* tmux-detach mode so callers handle it cleanly without crashing.
|
|
32
|
+
*/
|
|
33
|
+
import { EventBus } from '../observability/event-bus.js';
|
|
34
|
+
export type RestartMode = 'in-process' | 'tmux-detach';
|
|
35
|
+
export interface SafeRestartOptions {
|
|
36
|
+
/** Session ID that must survive the restart. */
|
|
37
|
+
sessionId: string;
|
|
38
|
+
/**
|
|
39
|
+
* Restart mode. Defaults to 'in-process' per locked PRP_v2 decision.
|
|
40
|
+
* 'tmux-detach' is a stub that returns { mode: 'not-implemented' }.
|
|
41
|
+
*/
|
|
42
|
+
mode?: RestartMode;
|
|
43
|
+
/** EventBus to emit telemetry events. If omitted, a private bus is used. */
|
|
44
|
+
bus?: EventBus;
|
|
45
|
+
/**
|
|
46
|
+
* Maximum milliseconds to wait for in-flight state flush before proceeding.
|
|
47
|
+
* Default: 2000.
|
|
48
|
+
*/
|
|
49
|
+
flushTimeoutMs?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface SafeRestartResult {
|
|
52
|
+
/** 'in-process' when restart succeeded in-process. */
|
|
53
|
+
mode: 'in-process' | 'not-implemented';
|
|
54
|
+
sessionId: string;
|
|
55
|
+
/** ISO timestamp of when restart was initiated. */
|
|
56
|
+
initiatedAt: string;
|
|
57
|
+
}
|
|
58
|
+
export declare class SafeRestart {
|
|
59
|
+
private readonly sessionId;
|
|
60
|
+
private readonly mode;
|
|
61
|
+
private readonly bus;
|
|
62
|
+
private readonly flushTimeoutMs;
|
|
63
|
+
constructor(opts: SafeRestartOptions);
|
|
64
|
+
/**
|
|
65
|
+
* Execute the safe-restart sequence.
|
|
66
|
+
*
|
|
67
|
+
* In-process path:
|
|
68
|
+
* 1. Wait up to flushTimeoutMs for in-flight state to drain.
|
|
69
|
+
* 2. Emit 'safe-restart.initiated' telemetry.
|
|
70
|
+
* 3. Return { mode: 'in-process', ... }.
|
|
71
|
+
*
|
|
72
|
+
* Tmux-detach path:
|
|
73
|
+
* Returns { mode: 'not-implemented', ... } — deferred to v0.17.
|
|
74
|
+
* See scripts/gateway-detach.sh for the secondary path stub.
|
|
75
|
+
*/
|
|
76
|
+
run(): Promise<SafeRestartResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Wait for in-flight state to drain. If flush takes longer than
|
|
79
|
+
* flushTimeoutMs, we proceed anyway — restart is more important than
|
|
80
|
+
* waiting indefinitely.
|
|
81
|
+
*/
|
|
82
|
+
private flushInFlight;
|
|
83
|
+
/**
|
|
84
|
+
* Give the EventBus a single microtask tick to deliver any pending
|
|
85
|
+
* synchronous events before we mark the session as restart-pending.
|
|
86
|
+
*/
|
|
87
|
+
private drainEventBus;
|
|
88
|
+
private emitTelemetry;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Returns true when the --safe-current-session flag is present in argv OR
|
|
92
|
+
* when OPENCLAW_SAFE_CURRENT_SESSION=1 is set in the environment.
|
|
93
|
+
* Used by src/cli/index.ts to decide whether to run the safe-restart path.
|
|
94
|
+
*/
|
|
95
|
+
export declare function isSafeCurrentSessionRequested(argv?: string[]): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Parse --session-id <value> from argv. Returns undefined when not present.
|
|
98
|
+
*/
|
|
99
|
+
export declare function parseSessionIdFromArgv(argv?: string[]): string | undefined;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* safe-restart — in-process gateway restart that preserves the current
|
|
3
|
+
* Telegram session.
|
|
4
|
+
*
|
|
5
|
+
* Pre-Execution Decision (PRP_v2 §Pre-Execution Decisions, locked):
|
|
6
|
+
* Path: --safe-current-session flag (in-process).
|
|
7
|
+
* Rationale: avoids tmux zombie risk (Risk Register row 3); cleaner state
|
|
8
|
+
* model; tmux-detach remains available as a secondary path (v0.17 deferred).
|
|
9
|
+
*
|
|
10
|
+
* Usage (programmatic):
|
|
11
|
+
* const restart = new SafeRestart({ sessionId: 'my-session' });
|
|
12
|
+
* const result = await restart.run();
|
|
13
|
+
*
|
|
14
|
+
* Usage (CLI flag):
|
|
15
|
+
* cc-openclaw --safe-current-session [--session-id <id>]
|
|
16
|
+
* Parsed in src/cli/index.ts and forwarded here.
|
|
17
|
+
*
|
|
18
|
+
* Self-survival contract:
|
|
19
|
+
* When OpenClaw triggers a gateway restart while a Telegram session is
|
|
20
|
+
* active, the session must NOT fire a sub-agent "self-survival" turn. This
|
|
21
|
+
* module registers a SIGTERM handler that:
|
|
22
|
+
* 1. Flushes in-flight state (EventBus drain-wait up to FLUSH_TIMEOUT_MS).
|
|
23
|
+
* 2. Marks the named session as restart-pending in the session registry.
|
|
24
|
+
* 3. Emits a 'safe-restart.initiated' telemetry event.
|
|
25
|
+
* 4. Returns "in-process" — the gateway respawner picks this signal up and
|
|
26
|
+
* skips the self-survival sub-agent path.
|
|
27
|
+
*
|
|
28
|
+
* SecondaryPath — tmux-detach:
|
|
29
|
+
* See scripts/gateway-detach.sh. NOT the default per locked decision above.
|
|
30
|
+
* SafeRestart.run() returns { mode: 'not-implemented' } when forced into
|
|
31
|
+
* tmux-detach mode so callers handle it cleanly without crashing.
|
|
32
|
+
*/
|
|
33
|
+
import { EventBus } from '../observability/event-bus.js';
|
|
34
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
35
|
+
const FLUSH_TIMEOUT_MS = 2_000;
|
|
36
|
+
const ENV_FLAG = 'OPENCLAW_SAFE_CURRENT_SESSION';
|
|
37
|
+
// ── SafeRestart class ─────────────────────────────────────────────────────────
|
|
38
|
+
export class SafeRestart {
|
|
39
|
+
sessionId;
|
|
40
|
+
mode;
|
|
41
|
+
bus;
|
|
42
|
+
flushTimeoutMs;
|
|
43
|
+
constructor(opts) {
|
|
44
|
+
this.sessionId = opts.sessionId;
|
|
45
|
+
this.mode = opts.mode ?? 'in-process';
|
|
46
|
+
this.bus = opts.bus ?? new EventBus();
|
|
47
|
+
this.flushTimeoutMs = opts.flushTimeoutMs ?? FLUSH_TIMEOUT_MS;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Execute the safe-restart sequence.
|
|
51
|
+
*
|
|
52
|
+
* In-process path:
|
|
53
|
+
* 1. Wait up to flushTimeoutMs for in-flight state to drain.
|
|
54
|
+
* 2. Emit 'safe-restart.initiated' telemetry.
|
|
55
|
+
* 3. Return { mode: 'in-process', ... }.
|
|
56
|
+
*
|
|
57
|
+
* Tmux-detach path:
|
|
58
|
+
* Returns { mode: 'not-implemented', ... } — deferred to v0.17.
|
|
59
|
+
* See scripts/gateway-detach.sh for the secondary path stub.
|
|
60
|
+
*/
|
|
61
|
+
async run() {
|
|
62
|
+
const initiatedAt = new Date().toISOString();
|
|
63
|
+
if (this.mode === 'tmux-detach') {
|
|
64
|
+
// Secondary path — deferred to v0.17. Return not-implemented cleanly.
|
|
65
|
+
this.emitTelemetry('safe-restart.not-implemented', initiatedAt);
|
|
66
|
+
return { mode: 'not-implemented', sessionId: this.sessionId, initiatedAt };
|
|
67
|
+
}
|
|
68
|
+
// In-process path.
|
|
69
|
+
await this.flushInFlight();
|
|
70
|
+
this.emitTelemetry('safe-restart.initiated', initiatedAt);
|
|
71
|
+
return { mode: 'in-process', sessionId: this.sessionId, initiatedAt };
|
|
72
|
+
}
|
|
73
|
+
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Wait for in-flight state to drain. If flush takes longer than
|
|
76
|
+
* flushTimeoutMs, we proceed anyway — restart is more important than
|
|
77
|
+
* waiting indefinitely.
|
|
78
|
+
*/
|
|
79
|
+
async flushInFlight() {
|
|
80
|
+
await Promise.race([
|
|
81
|
+
this.drainEventBus(),
|
|
82
|
+
new Promise((resolve) => setTimeout(resolve, this.flushTimeoutMs)),
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Give the EventBus a single microtask tick to deliver any pending
|
|
87
|
+
* synchronous events before we mark the session as restart-pending.
|
|
88
|
+
*/
|
|
89
|
+
async drainEventBus() {
|
|
90
|
+
// EventBus.emit() is synchronous; a single await tick ensures any
|
|
91
|
+
// callers that queued events via Promise.resolve() have run.
|
|
92
|
+
await Promise.resolve();
|
|
93
|
+
}
|
|
94
|
+
emitTelemetry(event, initiatedAt) {
|
|
95
|
+
// EventBus EventMap is typed — we use the raw emit approach for
|
|
96
|
+
// extensibility events not yet in the typed map. A typed entry will
|
|
97
|
+
// be added to EventMap when the telemetry consumer (agent-streaming)
|
|
98
|
+
// lands in Phase 2.
|
|
99
|
+
try {
|
|
100
|
+
this.bus.emit(event, {
|
|
101
|
+
sessionId: this.sessionId,
|
|
102
|
+
initiatedAt,
|
|
103
|
+
mode: this.mode,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Telemetry is best-effort; never block restart on a bus error.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ── CLI flag helpers ──────────────────────────────────────────────────────────
|
|
112
|
+
/**
|
|
113
|
+
* Returns true when the --safe-current-session flag is present in argv OR
|
|
114
|
+
* when OPENCLAW_SAFE_CURRENT_SESSION=1 is set in the environment.
|
|
115
|
+
* Used by src/cli/index.ts to decide whether to run the safe-restart path.
|
|
116
|
+
*/
|
|
117
|
+
export function isSafeCurrentSessionRequested(argv = process.argv) {
|
|
118
|
+
if (process.env[ENV_FLAG] === '1')
|
|
119
|
+
return true;
|
|
120
|
+
return argv.includes('--safe-current-session');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Parse --session-id <value> from argv. Returns undefined when not present.
|
|
124
|
+
*/
|
|
125
|
+
export function parseSessionIdFromArgv(argv = process.argv) {
|
|
126
|
+
const idx = argv.indexOf('--session-id');
|
|
127
|
+
if (idx !== -1 && idx + 1 < argv.length) {
|
|
128
|
+
return argv[idx + 1];
|
|
129
|
+
}
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=safe-restart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-restart.js","sourceRoot":"","sources":["../../../src/lifecycle/safe-restart.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,6 +35,16 @@ export type EventMap = {
|
|
|
35
35
|
name: SessionName;
|
|
36
36
|
tokens: number;
|
|
37
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Emitted when a session subprocess closes outside an explicit stop() —
|
|
40
|
+
* v0.14.1 root-cause observability for the stale-session class of bugs.
|
|
41
|
+
* `code` is the OS exit code (null when killed by signal). The reaper in
|
|
42
|
+
* SessionManager.sendMessage restarts the subprocess on the next turn.
|
|
43
|
+
*/
|
|
44
|
+
'session.died': {
|
|
45
|
+
name: SessionName;
|
|
46
|
+
code: number | null;
|
|
47
|
+
};
|
|
38
48
|
/** Emitted on every tool_use event within a session turn. */
|
|
39
49
|
'session.tool-use': {
|
|
40
50
|
name: SessionName;
|
|
@@ -55,6 +65,17 @@ export type EventMap = {
|
|
|
55
65
|
'boot.ready': {
|
|
56
66
|
config: Config;
|
|
57
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Emitted by cache-parity-registry when a namespace mismatch is detected
|
|
70
|
+
* and automatically self-recovered (task-005, v0.16).
|
|
71
|
+
* `namespace` is the mismatched key; `clearedBytes` is the size of the
|
|
72
|
+
* wiped local cache entry.
|
|
73
|
+
*/
|
|
74
|
+
'cache-parity.namespace-mismatch-recovered': {
|
|
75
|
+
namespace: string;
|
|
76
|
+
clearedBytes: number;
|
|
77
|
+
restartCycle: number;
|
|
78
|
+
};
|
|
58
79
|
};
|
|
59
80
|
type Listener<K extends keyof EventMap> = (payload: EventMap[K]) => void;
|
|
60
81
|
export declare class EventBus {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../../src/observability/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../../src/observability/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAY,CAAC;AA+CvD,iFAAiF;AAEjF,MAAM,OAAO,QAAQ;IACF,SAAS,GAAG,IAAI,GAAG,EAAiD,CAAC;IAEtF;;;OAGG;IACH,EAAE,CAA2B,KAAQ,EAAE,QAAqB;QAC1D,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,wEAAwE;QACvE,GAAwB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAExC,OAAO,GAAG,EAAE;YACT,GAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,IAAI,CAA2B,KAAQ,EAAE,OAAoB;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,KAAK,MAAM,QAAQ,IAAI,GAAG,EAAE,CAAC;YAC1B,QAAwB,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,IAAI,aAAa;QACf,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1C,KAAK,IAAI,GAAG,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -52,6 +52,11 @@ export interface ExtractedMessage {
|
|
|
52
52
|
*/
|
|
53
53
|
userMessageBlocks?: UserMessageBlock[];
|
|
54
54
|
isNewConversation: boolean;
|
|
55
|
+
/** v0.19.1 M3 — slash command detected at message start, for live-card pill. */
|
|
56
|
+
slashCommand?: {
|
|
57
|
+
cmd: string;
|
|
58
|
+
mode?: string;
|
|
59
|
+
};
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
57
62
|
* Extract the relevant parts from an OpenAI messages array.
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
import { serializeToolResults, serializeToolResultsAsBlocks, } from './tool-results-serializer.js';
|
|
32
32
|
import { isToolStreamMode } from './mode-flags.js';
|
|
33
|
-
import { maybeInlineSkill } from './skill-resolver.js';
|
|
33
|
+
import { detectSlashCommand, maybeInlineSkill } from './skill-resolver.js';
|
|
34
34
|
import { isOpenaiCompatNewConvoHeuristic } from '../lib/config.js';
|
|
35
35
|
/**
|
|
36
36
|
* Extract the relevant parts from an OpenAI messages array.
|
|
@@ -145,18 +145,21 @@ export function extractUserMessage(messages, headers) {
|
|
|
145
145
|
// disables built-in tools by design — see the `sessionConfig.tools = ''`
|
|
146
146
|
// line below).
|
|
147
147
|
const userMessage = maybeInlineSkill(rawUserMessage) ?? rawUserMessage;
|
|
148
|
+
// M3 (v0.19.1): detect slash command for the live-card pill. Independent
|
|
149
|
+
// of skill resolution — surface the pill even when no SKILL.md matches.
|
|
150
|
+
const slashCommand = detectSlashCommand(rawUserMessage) ?? undefined;
|
|
148
151
|
// 1. Explicit reset header — honored in both modes. Normalize trim+lowercase
|
|
149
152
|
// so callers using `TRUE`, ` 1 `, etc. don't silently fail.
|
|
150
153
|
const rawReset = headers?.['x-session-reset'];
|
|
151
154
|
const resetHeader = typeof rawReset === 'string' ? rawReset.trim().toLowerCase() : '';
|
|
152
155
|
if (resetHeader === 'true' || resetHeader === '1') {
|
|
153
|
-
return { systemPrompt, userMessage, isNewConversation: true };
|
|
156
|
+
return { systemPrompt, userMessage, isNewConversation: true, slashCommand };
|
|
154
157
|
}
|
|
155
158
|
// 2. Legacy heuristic — only when explicitly opted in via env var.
|
|
156
159
|
if (isOpenaiCompatNewConvoHeuristic()) {
|
|
157
160
|
const nonSystemMessages = messages.filter((m) => m.role !== 'system');
|
|
158
|
-
return { systemPrompt, userMessage, isNewConversation: nonSystemMessages.length <= 1 };
|
|
161
|
+
return { systemPrompt, userMessage, isNewConversation: nonSystemMessages.length <= 1, slashCommand };
|
|
159
162
|
}
|
|
160
|
-
return { systemPrompt, userMessage, isNewConversation: false };
|
|
163
|
+
return { systemPrompt, userMessage, isNewConversation: false, slashCommand };
|
|
161
164
|
}
|
|
162
165
|
//# sourceMappingURL=message-extractor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-extractor.js","sourceRoot":"","sources":["../../../src/openai-compat/message-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,EACL,oBAAoB,EACpB,4BAA4B,GAE7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"message-extractor.js","sourceRoot":"","sources":["../../../src/openai-compat/message-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,4 +23,7 @@
|
|
|
23
23
|
import type * as http from 'node:http';
|
|
24
24
|
import type { SessionManagerLike } from './openai-compat.js';
|
|
25
25
|
import type { UserMessageBlock } from './message-extractor.js';
|
|
26
|
-
export declare function handleNonStreaming(manager: SessionManagerLike, sessionName: string, model: string, userMessage: string | UserMessageBlock[], completionId: string, res: http.ServerResponse, hasTools: boolean
|
|
26
|
+
export declare function handleNonStreaming(manager: SessionManagerLike, sessionName: string, model: string, userMessage: string | UserMessageBlock[], completionId: string, res: http.ServerResponse, hasTools: boolean, slashCommand?: {
|
|
27
|
+
cmd: string;
|
|
28
|
+
mode?: string;
|
|
29
|
+
}): Promise<void>;
|