@a1hvdy/cc-openclaw 0.15.0 → 0.18.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.
Files changed (51) hide show
  1. package/dist/src/channels/telegram/completion-summary.js +24 -2
  2. package/dist/src/channels/telegram/completion-summary.js.map +1 -1
  3. package/dist/src/channels/telegram/event-reducer.js +15 -3
  4. package/dist/src/channels/telegram/event-reducer.js.map +1 -1
  5. package/dist/src/channels/telegram/live-card.d.ts +90 -7
  6. package/dist/src/channels/telegram/live-card.js +286 -25
  7. package/dist/src/channels/telegram/live-card.js.map +1 -1
  8. package/dist/src/channels/telegram/tool-tracker.d.ts +16 -2
  9. package/dist/src/channels/telegram/tool-tracker.js +81 -18
  10. package/dist/src/channels/telegram/tool-tracker.js.map +1 -1
  11. package/dist/src/config/loader.js +2 -0
  12. package/dist/src/config/loader.js.map +1 -1
  13. package/dist/src/config/schema.d.ts +20 -1
  14. package/dist/src/config/schema.js +12 -1
  15. package/dist/src/config/schema.js.map +1 -1
  16. package/dist/src/constants.d.ts +13 -1
  17. package/dist/src/constants.js +13 -1
  18. package/dist/src/constants.js.map +1 -1
  19. package/dist/src/lib/config.d.ts +6 -0
  20. package/dist/src/lib/config.js +81 -0
  21. package/dist/src/lib/config.js.map +1 -1
  22. package/dist/src/lib/sysprompt-strip.d.ts +14 -0
  23. package/dist/src/lib/sysprompt-strip.js +79 -2
  24. package/dist/src/lib/sysprompt-strip.js.map +1 -1
  25. package/dist/src/lib/trajectory.d.ts +1 -10
  26. package/dist/src/lib/trajectory.js +6 -37
  27. package/dist/src/lib/trajectory.js.map +1 -1
  28. package/dist/src/lib/turn-trace.d.ts +34 -0
  29. package/dist/src/lib/turn-trace.js +75 -0
  30. package/dist/src/lib/turn-trace.js.map +1 -0
  31. package/dist/src/observability/event-bus.d.ts +0 -10
  32. package/dist/src/observability/event-bus.js.map +1 -1
  33. package/dist/src/openai-compat/insight-format.d.ts +36 -0
  34. package/dist/src/openai-compat/insight-format.js +140 -0
  35. package/dist/src/openai-compat/insight-format.js.map +1 -0
  36. package/dist/src/openai-compat/non-streaming-handler.js +38 -58
  37. package/dist/src/openai-compat/non-streaming-handler.js.map +1 -1
  38. package/dist/src/openai-compat/openai-compat.js +23 -0
  39. package/dist/src/openai-compat/openai-compat.js.map +1 -1
  40. package/dist/src/openai-compat/response-formatter.d.ts +6 -1
  41. package/dist/src/openai-compat/response-formatter.js +9 -2
  42. package/dist/src/openai-compat/response-formatter.js.map +1 -1
  43. package/dist/src/openai-compat/streaming-handler.js +35 -82
  44. package/dist/src/openai-compat/streaming-handler.js.map +1 -1
  45. package/dist/src/session/session-manager.d.ts +8 -4
  46. package/dist/src/session/session-manager.js +86 -76
  47. package/dist/src/session/session-manager.js.map +1 -1
  48. package/dist/src/session-bootstrap/cwd-patch.js +35 -0
  49. package/dist/src/session-bootstrap/cwd-patch.js.map +1 -1
  50. package/mcp-tools.json +1 -1
  51. package/package.json +1 -1
@@ -14,6 +14,18 @@
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
+ /**
18
+ * Extract the last ★ Insight block from assistant text, applying the same
19
+ * empty-body and length guards as the renderer-side tips-engine.
20
+ *
21
+ * Returns null when:
22
+ * - no fence is found
23
+ * - the body is whitespace-only
24
+ * - the body is shorter than MIN_INSIGHT_CHARS (20)
25
+ *
26
+ * Used by trajectory.ts to avoid emitting empty insight_strip events.
27
+ */
28
+ export declare function extractInsight(assistantText: string | null | undefined): string | null;
17
29
  export interface StripOptions {
18
30
  /** Override env detection — force strip enabled. Used by tests + lifecycle wiring. */
19
31
  forceEnabled?: boolean;
@@ -26,6 +38,8 @@ export interface StripResult {
26
38
  changed: boolean;
27
39
  /** true if aggressive strip made additional changes (always true when enabled) */
28
40
  aggressiveStripped: boolean;
41
+ /** true if Track C v2 cold-prompt strips applied (opt-in via OPENCLAW_COLD_PROMPT_STRIP_V2=1) */
42
+ coldPromptV2Stripped?: boolean;
29
43
  }
30
44
  /**
31
45
  * Resolve whether strip should run based on env + options.
@@ -14,7 +14,50 @@
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 } from './config.js';
17
+ import { getAggressiveStripEnabled, getColdPromptStripV2Enabled } from './config.js';
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
+ }
18
61
  // v0.6.0: regex literals lifted to module scope to avoid recompilation on
19
62
  // every stripSysprompt() call. With cwd-patch firing strip on every chat
20
63
  // completion, that was 4-6 regex compilations per turn → 0.
@@ -22,6 +65,28 @@ const RE_SELF_IMPROVEMENT_FILENAME = /\n?##\s+SELF_IMPROVEMENT_REMINDER\.md(?=\n
22
65
  const RE_SELF_IMPROVEMENT_BLOCK = /\n?##\s+Self-Improvement Reminder[\s\S]*?(?=\n##\s|\n?$)/g;
23
66
  const RE_LOCATION_LINES = /\n\s*<location>[^<]*<\/location>/g;
24
67
  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;
25
90
  /**
26
91
  * Resolve whether strip should run based on env + options.
27
92
  *
@@ -69,7 +134,19 @@ export function stripSysprompt(content, opts = {}) {
69
134
  // F2.2 (REMOVED 2026-04-25) — The 45-bullet tool-name enumeration no longer appears.
70
135
  // Re-add only if bullets reappear in a future runtime.
71
136
  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
+ }
72
149
  const changed = stripped !== original;
73
- return { content: stripped, changed, aggressiveStripped };
150
+ return { content: stripped, changed, aggressiveStripped, coldPromptV2Stripped };
74
151
  }
75
152
  //# 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,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"}
1
+ {"version":3,"file":"sysprompt-strip.js","sourceRoot":"","sources":["../../../src/lib/sysprompt-strip.ts"],"names":[],"mappings":"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"}
@@ -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' | 'turn_complete' | 'session_died';
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_preflight' | 'insight_strip' | 'false_done_backstop' | 'card_collapse_fired' | 'no_greet_strip';
18
18
  export interface TrajectoryEvent {
19
19
  ts: string;
20
20
  sessionId?: string;
@@ -29,15 +29,6 @@ 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;
41
32
  /** Reset module state for tests (re-checks env, re-creates dir on next emit). */
42
33
  export declare function _resetForTests(): void;
43
34
  /** Read all events from current trajectory file. Test-only. */
@@ -18,6 +18,7 @@
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';
21
22
  // ─── Config ──────────────────────────────────────────────────────────────────
22
23
  const DEFAULT_PATH = join(homedir(), '.openclaw', 'workspace', 'memory', 'cc-openclaw-trajectory.jsonl');
23
24
  function isEnabled() {
@@ -26,12 +27,6 @@ function isEnabled() {
26
27
  return false;
27
28
  return v === '1' || v.toLowerCase() === 'true' || v.toLowerCase() === 'on';
28
29
  }
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
- }
35
30
  function getPath() {
36
31
  return process.env.CC_OPENCLAW_TRAJECTORY_PATH || DEFAULT_PATH;
37
32
  }
@@ -72,37 +67,11 @@ export function emit(eventType, payload, sessionId) {
72
67
  // endpoint's errors_total counter still records the underlying error
73
68
  // via the calling code's error-formatter path.
74
69
  }
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
- }
70
+ // v0.15.0 Slice 1 — fan out to the per-session turn-trace sink. Independent
71
+ // env (OPENCLAW_TURN_TRACE) so users can enable shardable per-session traces
72
+ // without keeping the unbounded global trajectory log on. Sink is no-op when
73
+ // disabled and swallows its own errors.
74
+ turnTrace.capture(event);
106
75
  }
107
76
  // ─── Test helpers ────────────────────────────────────────────────────────────
108
77
  /** 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":"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"}
1
+ {"version":3,"file":"trajectory.js","sourceRoot":"","sources":["../../../src/lib/trajectory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * turn-trace — per-session disk sink for cc-openclaw trajectory events.
3
+ *
4
+ * Mirrors every `trajectory.emit()` call into a per-session JSONL file at
5
+ * `~/.openclaw/workspace/debug/turn-trace/<sessionId>.jsonl`. The global
6
+ * trajectory log keeps working as before; this sink adds shardable output
7
+ * for debugging "what did session X actually see and emit".
8
+ *
9
+ * Motivation: v0.14.0's NO_REPLY 4-layer defense interacted badly with
10
+ * short questions, producing greeting-only output ("Hey A1.") that looked
11
+ * just as broken as silence. A1 needs ground-truth to tune the classifier
12
+ * without guessing — this sink captures the data.
13
+ *
14
+ * Default OFF — set OPENCLAW_TURN_TRACE=1 in env to opt in. Independent
15
+ * from CC_OPENCLAW_TRAJECTORY (the global sink); either, both, or neither
16
+ * can run.
17
+ *
18
+ * v0.15.0 ships per-session sharding (one file per sessionId lifecycle).
19
+ * v0.15.1 will switch to per-turn sharding once a turnId is threaded
20
+ * through the request_in → response_complete event chain.
21
+ */
22
+ import type { TrajectoryEvent } from './trajectory.js';
23
+ /**
24
+ * Append a trajectory event to the per-session JSONL file. No-op if disabled
25
+ * or if the event has no sessionId (can't shard without a key).
26
+ *
27
+ * Writes are sync (matches trajectory.emit semantics — preserves ordering).
28
+ * File-system errors are swallowed: observability must never break prod.
29
+ */
30
+ export declare function capture(event: TrajectoryEvent): void;
31
+ /** Reset module state for tests (re-creates dir on next capture). */
32
+ export declare function _resetForTests(): void;
33
+ /** Compute the on-disk path a given sessionId would resolve to. Test-only. */
34
+ export declare function _pathForSession(sessionId: string): string;
@@ -0,0 +1,75 @@
1
+ // phase: v0.15.0 | pillar: Observability | agent: Primary
2
+ /**
3
+ * turn-trace — per-session disk sink for cc-openclaw trajectory events.
4
+ *
5
+ * Mirrors every `trajectory.emit()` call into a per-session JSONL file at
6
+ * `~/.openclaw/workspace/debug/turn-trace/<sessionId>.jsonl`. The global
7
+ * trajectory log keeps working as before; this sink adds shardable output
8
+ * for debugging "what did session X actually see and emit".
9
+ *
10
+ * Motivation: v0.14.0's NO_REPLY 4-layer defense interacted badly with
11
+ * short questions, producing greeting-only output ("Hey A1.") that looked
12
+ * just as broken as silence. A1 needs ground-truth to tune the classifier
13
+ * without guessing — this sink captures the data.
14
+ *
15
+ * Default OFF — set OPENCLAW_TURN_TRACE=1 in env to opt in. Independent
16
+ * from CC_OPENCLAW_TRAJECTORY (the global sink); either, both, or neither
17
+ * can run.
18
+ *
19
+ * v0.15.0 ships per-session sharding (one file per sessionId lifecycle).
20
+ * v0.15.1 will switch to per-turn sharding once a turnId is threaded
21
+ * through the request_in → response_complete event chain.
22
+ */
23
+ import { appendFileSync, mkdirSync } from 'node:fs';
24
+ import { join } from 'node:path';
25
+ import { homedir } from 'node:os';
26
+ // ─── Config ──────────────────────────────────────────────────────────────────
27
+ const DEFAULT_DIR = join(homedir(), '.openclaw', 'workspace', 'debug', 'turn-trace');
28
+ function isEnabled() {
29
+ const v = process.env.OPENCLAW_TURN_TRACE;
30
+ if (!v)
31
+ return false;
32
+ return v === '1' || v.toLowerCase() === 'true' || v.toLowerCase() === 'on';
33
+ }
34
+ function getDir() {
35
+ return process.env.OPENCLAW_TURN_TRACE_DIR || DEFAULT_DIR;
36
+ }
37
+ // ─── Sink ────────────────────────────────────────────────────────────────────
38
+ let _ensuredDir = false;
39
+ /**
40
+ * Append a trajectory event to the per-session JSONL file. No-op if disabled
41
+ * or if the event has no sessionId (can't shard without a key).
42
+ *
43
+ * Writes are sync (matches trajectory.emit semantics — preserves ordering).
44
+ * File-system errors are swallowed: observability must never break prod.
45
+ */
46
+ export function capture(event) {
47
+ if (!isEnabled())
48
+ return;
49
+ if (!event.sessionId)
50
+ return;
51
+ try {
52
+ const dir = getDir();
53
+ if (!_ensuredDir) {
54
+ mkdirSync(dir, { recursive: true });
55
+ _ensuredDir = true;
56
+ }
57
+ const safeKey = event.sessionId.replace(/[^a-zA-Z0-9_.-]/g, '_');
58
+ const path = join(dir, `${safeKey}.jsonl`);
59
+ appendFileSync(path, JSON.stringify(event) + '\n', 'utf8');
60
+ }
61
+ catch {
62
+ // observability — never break production
63
+ }
64
+ }
65
+ // ─── Test helpers ────────────────────────────────────────────────────────────
66
+ /** Reset module state for tests (re-creates dir on next capture). */
67
+ export function _resetForTests() {
68
+ _ensuredDir = false;
69
+ }
70
+ /** Compute the on-disk path a given sessionId would resolve to. Test-only. */
71
+ export function _pathForSession(sessionId) {
72
+ const safeKey = sessionId.replace(/[^a-zA-Z0-9_.-]/g, '_');
73
+ return join(getDir(), `${safeKey}.jsonl`);
74
+ }
75
+ //# sourceMappingURL=turn-trace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-trace.js","sourceRoot":"","sources":["../../../src/lib/turn-trace.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAW,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,gFAAgF;AAEhF,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAErF,SAAS,SAAS;IAChB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC1C,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrB,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC;AAC7E,CAAC;AAED,SAAS,MAAM;IACb,OAAO,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,WAAW,CAAC;AAC5D,CAAC;AAED,gFAAgF;AAEhF,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,KAAsB;IAC5C,IAAI,CAAC,SAAS,EAAE;QAAE,OAAO;IACzB,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO;IAE7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QACrB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,QAAQ,CAAC,CAAC;QAC3C,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;AACH,CAAC;AAED,gFAAgF;AAEhF,qEAAqE;AACrE,MAAM,UAAU,cAAc;IAC5B,WAAW,GAAG,KAAK,CAAC;AACtB,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,QAAQ,CAAC,CAAC;AAC5C,CAAC"}
@@ -35,16 +35,6 @@ 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
- };
48
38
  /** Emitted on every tool_use event within a session turn. */
49
39
  'session.tool-use': {
50
40
  name: SessionName;
@@ -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;AAoCvD,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"}
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;AA6BvD,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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Insight + greeting filters applied to model output before it's emitted to
3
+ * the OpenAI-compat client. Three pure transforms, gated by env flags:
4
+ *
5
+ * 1. `stripEmptyInsights` — v0.14.0. Removes ★ Insight ─ blocks that are
6
+ * empty, filler ("memory loaded"), or oversized. Server-side backstop
7
+ * for SOUL.md NO_EMPTY_INSIGHT.
8
+ *
9
+ * 2. `stripGreeting` — v0.18 Slice 1 / Track A3. Strips opener lines like
10
+ * "Hey A1.", "Hi A1,", "Sure thing A1 —" from the very start of an
11
+ * assistant message. Belt-and-suspenders for SOUL.md NO_GREET.
12
+ * Telemetry event: `no_greet_strip`.
13
+ *
14
+ * 3. `normalizeInsightFences` — v0.17 F2. Rewrites long U+2500 fences
15
+ * to exactly 3 chars so mobile Telegram doesn't wrap the divider.
16
+ *
17
+ * Pure transforms — no I/O. `stripEmptyInsights` and `stripGreeting`
18
+ * emit trajectory events when they remove content, but trajectory is
19
+ * append-only and observability-only.
20
+ *
21
+ * Apply order: strip empty → strip greeting → normalize fences. Empty
22
+ * blocks must be removed before fence normalization so the normalized
23
+ * block doesn't tempt the next emit pass to keep it.
24
+ */
25
+ export declare function stripEmptyInsights(text: string, sessionId: string): string;
26
+ export declare function stripGreeting(text: string, sessionId: string): string;
27
+ export declare function isInsightNormalizeEnabled(): boolean;
28
+ export declare function normalizeInsightFences(text: string): string;
29
+ /**
30
+ * Apply all three filters in the canonical order:
31
+ * strip empty insights → strip greeting → normalize fences.
32
+ *
33
+ * Call this at every emit site instead of stitching the three calls by
34
+ * hand. Single source of truth for the order.
35
+ */
36
+ export declare function applyInsightAndGreetingFilters(text: string, sessionId: string): string;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Insight + greeting filters applied to model output before it's emitted to
3
+ * the OpenAI-compat client. Three pure transforms, gated by env flags:
4
+ *
5
+ * 1. `stripEmptyInsights` — v0.14.0. Removes ★ Insight ─ blocks that are
6
+ * empty, filler ("memory loaded"), or oversized. Server-side backstop
7
+ * for SOUL.md NO_EMPTY_INSIGHT.
8
+ *
9
+ * 2. `stripGreeting` — v0.18 Slice 1 / Track A3. Strips opener lines like
10
+ * "Hey A1.", "Hi A1,", "Sure thing A1 —" from the very start of an
11
+ * assistant message. Belt-and-suspenders for SOUL.md NO_GREET.
12
+ * Telemetry event: `no_greet_strip`.
13
+ *
14
+ * 3. `normalizeInsightFences` — v0.17 F2. Rewrites long U+2500 fences
15
+ * to exactly 3 chars so mobile Telegram doesn't wrap the divider.
16
+ *
17
+ * Pure transforms — no I/O. `stripEmptyInsights` and `stripGreeting`
18
+ * emit trajectory events when they remove content, but trajectory is
19
+ * append-only and observability-only.
20
+ *
21
+ * Apply order: strip empty → strip greeting → normalize fences. Empty
22
+ * blocks must be removed before fence normalization so the normalized
23
+ * block doesn't tempt the next emit pass to keep it.
24
+ */
25
+ import { emit as emitTrajectory } from '../lib/trajectory.js';
26
+ // ─── Empty-insight stripper ───────────────────────────────────────────────────
27
+ // v0.18 Slice 1: use `[ \t]*` (horizontal whitespace) instead of `\s*`
28
+ // around the opening and closing fence newlines. The original v0.14.0
29
+ // regex used `\s*` which crosses newlines, causing an empty insight
30
+ // followed by another insight (Hey A1 → empty insight → substantive
31
+ // insight) to swallow BOTH blocks as one huge non-greedy match instead
32
+ // of stripping the empty one and keeping the substantive one. Switching
33
+ // to horizontal-only whitespace anchors each fence to its own line.
34
+ const INSIGHT_BLOCK_RE = /★\s*Insight\s*[─\-]{3,}[ \t]*\n([\s\S]*?)\n[ \t]*[─\-]{3,}/g;
35
+ const INSIGHT_FILLER_RE = /^(memory loaded|session active|ready|loading|done)\.?$/i;
36
+ export function stripEmptyInsights(text, sessionId) {
37
+ if (!text || text.indexOf('★') === -1)
38
+ return text;
39
+ let stripped = false;
40
+ let reason = '';
41
+ let result = text.replace(INSIGHT_BLOCK_RE, (match, inner) => {
42
+ const innerTrimmed = (inner ?? '').trim();
43
+ let shouldStrip = false;
44
+ if (innerTrimmed.length < 20) {
45
+ shouldStrip = true;
46
+ reason = 'empty-or-short';
47
+ }
48
+ else if (INSIGHT_FILLER_RE.test(innerTrimmed)) {
49
+ shouldStrip = true;
50
+ reason = 'filler-phrase';
51
+ }
52
+ else if (match.length > 600) {
53
+ shouldStrip = true;
54
+ reason = 'oversized';
55
+ }
56
+ if (shouldStrip) {
57
+ stripped = true;
58
+ return '';
59
+ }
60
+ return match;
61
+ });
62
+ if (stripped) {
63
+ result = result.replace(/\n{3,}/g, '\n\n').trim();
64
+ try {
65
+ emitTrajectory('insight_strip', { event: 'insight-strip', reason, lenIn: text.length, lenOut: result.length }, sessionId);
66
+ }
67
+ catch {
68
+ // Trajectory is observability — must never break production
69
+ }
70
+ }
71
+ return result;
72
+ }
73
+ // ─── NO_GREET stripper (Track A3) ─────────────────────────────────────────────
74
+ /**
75
+ * Match a greeting opener at the very start of the message. Examples:
76
+ * "Hey A1.\n..."
77
+ * "Hi A1,"
78
+ * "Sure, A1 — ..."
79
+ * "Hey there A1!"
80
+ * "Hello A1: ..."
81
+ *
82
+ * The pattern requires the greeting word + an optional separator + the
83
+ * literal token "A1" + a small trailing punctuation/whitespace set. It
84
+ * is anchored to the message start (`^`) with `s` flag DISABLED so that
85
+ * "A1" mid-paragraph is never matched (we only strip the opener line).
86
+ *
87
+ * The trailing `(?:\n+|\s*—\s*|\s*-\s*)` swallows the line-break (or
88
+ * em-dash continuation) that joined the greeting to the substantive
89
+ * reply, so the cleaned text starts at the real content.
90
+ */
91
+ const GREETING_RE = /^(?:Hey there|Hey|Hi there|Hi|Hello|Sure thing|Sure|Of course|Okay|OK)[,!:.\s]+A1\b[.,!:]?(?:\n+|\s*—\s*|\s*-\s*)/i;
92
+ export function stripGreeting(text, sessionId) {
93
+ if (!text)
94
+ return text;
95
+ const match = text.match(GREETING_RE);
96
+ if (!match)
97
+ return text;
98
+ const result = text.slice(match[0].length).replace(/^\s+/, '');
99
+ try {
100
+ emitTrajectory('no_greet_strip', { matched: match[0].slice(0, 80), lenIn: text.length, lenOut: result.length }, sessionId);
101
+ }
102
+ catch {
103
+ // Trajectory is observability — must never break production
104
+ }
105
+ return result;
106
+ }
107
+ // ─── Insight fence normalization (v0.17 F2, unchanged) ───────────────────────
108
+ /** Short fence used in normalized output. 3 chars satisfies the
109
+ * extractor regex `[─—\-]{3,}` and avoids the wrap-to-`<hr>` glitch. */
110
+ const SHORT_FENCE = '───';
111
+ const INSIGHT_FENCE_RE = /(★\s*Insight\s*)[─—\-]{3,}([ \t]*\r?\n[\s\S]*?\r?\n[ \t]*)[─—\-]{3,}([ \t]*)$/gm;
112
+ export function isInsightNormalizeEnabled() {
113
+ return process.env.CC_OPENCLAW_INSIGHT_NORMALIZE !== '0';
114
+ }
115
+ export function normalizeInsightFences(text) {
116
+ if (!text)
117
+ return text;
118
+ if (!isInsightNormalizeEnabled())
119
+ return text;
120
+ if (text.indexOf('★') === -1)
121
+ return text;
122
+ return text.replace(INSIGHT_FENCE_RE, (_match, header, body, trailing) => `${header}${SHORT_FENCE}${body}${SHORT_FENCE}${trailing}`);
123
+ }
124
+ // ─── Composite filter ─────────────────────────────────────────────────────────
125
+ /**
126
+ * Apply all three filters in the canonical order:
127
+ * strip empty insights → strip greeting → normalize fences.
128
+ *
129
+ * Call this at every emit site instead of stitching the three calls by
130
+ * hand. Single source of truth for the order.
131
+ */
132
+ export function applyInsightAndGreetingFilters(text, sessionId) {
133
+ if (!text)
134
+ return text;
135
+ let out = stripEmptyInsights(text, sessionId);
136
+ out = stripGreeting(out, sessionId);
137
+ out = normalizeInsightFences(out);
138
+ return out;
139
+ }
140
+ //# sourceMappingURL=insight-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insight-format.js","sourceRoot":"","sources":["../../../src/openai-compat/insight-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE9D,iFAAiF;AAEjF,uEAAuE;AACvE,sEAAsE;AACtE,oEAAoE;AACpE,oEAAoE;AACpE,uEAAuE;AACvE,wEAAwE;AACxE,oEAAoE;AACpE,MAAM,gBAAgB,GAAG,6DAA6D,CAAC;AACvF,MAAM,iBAAiB,GAAG,yDAAyD,CAAC;AAEpF,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,SAAiB;IAChE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,KAAa,EAAE,EAAE;QACnE,MAAM,YAAY,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,WAAW,GAAG,KAAK,CAAC;QAExB,IAAI,YAAY,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC7B,WAAW,GAAG,IAAI,CAAC;YACnB,MAAM,GAAG,gBAAgB,CAAC;QAC5B,CAAC;aAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YAChD,WAAW,GAAG,IAAI,CAAC;YACnB,MAAM,GAAG,eAAe,CAAC;QAC3B,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC9B,WAAW,GAAG,IAAI,CAAC;YACnB,MAAM,GAAG,WAAW,CAAC;QACvB,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,QAAQ,GAAG,IAAI,CAAC;YAChB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,IAAI,CAAC;YACH,cAAc,CACZ,eAAe,EACf,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAC7E,SAAS,CACV,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,iFAAiF;AAEjF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,GACf,oHAAoH,CAAC;AAEvH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,SAAiB;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC;QACH,cAAc,CACZ,gBAAgB,EAChB,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAC7E,SAAS,CACV,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gFAAgF;AAEhF;yEACyE;AACzE,MAAM,WAAW,GAAG,KAAK,CAAC;AAE1B,MAAM,gBAAgB,GACpB,iFAAiF,CAAC;AAEpF,MAAM,UAAU,yBAAyB;IACvC,OAAO,OAAO,CAAC,GAAG,CAAC,6BAA6B,KAAK,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,CAAC,yBAAyB,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,IAAI,CAAC,OAAO,CACjB,gBAAgB,EAChB,CAAC,MAAM,EAAE,MAAc,EAAE,IAAY,EAAE,QAAgB,EAAE,EAAE,CACzD,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,GAAG,WAAW,GAAG,QAAQ,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAAY,EAAE,SAAiB;IAC5E,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,GAAG,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC9C,GAAG,GAAG,aAAa,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACpC,GAAG,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC"}