@agentdeck/shared 0.1.0 → 0.2.3

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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapter.d.ts +18 -3
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js +62 -0
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/command-builders.d.ts +35 -0
  7. package/dist/command-builders.d.ts.map +1 -0
  8. package/dist/command-builders.js +75 -0
  9. package/dist/command-builders.js.map +1 -0
  10. package/dist/connection-status.d.ts +42 -0
  11. package/dist/connection-status.d.ts.map +1 -0
  12. package/dist/connection-status.js +52 -0
  13. package/dist/connection-status.js.map +1 -0
  14. package/dist/d200h-layout.d.ts +131 -0
  15. package/dist/d200h-layout.d.ts.map +1 -0
  16. package/dist/d200h-layout.js +627 -0
  17. package/dist/d200h-layout.js.map +1 -0
  18. package/dist/design-tokens.d.ts +259 -0
  19. package/dist/design-tokens.d.ts.map +1 -0
  20. package/dist/design-tokens.js +156 -0
  21. package/dist/design-tokens.js.map +1 -0
  22. package/dist/eval-schema.d.ts +235 -0
  23. package/dist/eval-schema.d.ts.map +1 -0
  24. package/dist/eval-schema.js +23 -0
  25. package/dist/eval-schema.js.map +1 -0
  26. package/dist/format-utils.d.ts +52 -0
  27. package/dist/format-utils.d.ts.map +1 -0
  28. package/dist/format-utils.js +165 -0
  29. package/dist/format-utils.js.map +1 -0
  30. package/dist/gateway-protocol.d.ts +391 -0
  31. package/dist/gateway-protocol.d.ts.map +1 -0
  32. package/dist/gateway-protocol.js +19 -0
  33. package/dist/gateway-protocol.js.map +1 -0
  34. package/dist/index.d.ts +19 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +19 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/llm-settings.d.ts +52 -0
  39. package/dist/llm-settings.d.ts.map +1 -0
  40. package/dist/llm-settings.js +118 -0
  41. package/dist/llm-settings.js.map +1 -0
  42. package/dist/net-utils.d.ts +2 -1
  43. package/dist/net-utils.d.ts.map +1 -1
  44. package/dist/net-utils.js +100 -3
  45. package/dist/net-utils.js.map +1 -1
  46. package/dist/pricing.d.ts +45 -0
  47. package/dist/pricing.d.ts.map +1 -0
  48. package/dist/pricing.js +93 -0
  49. package/dist/pricing.js.map +1 -0
  50. package/dist/protocol.d.ts +496 -2
  51. package/dist/protocol.d.ts.map +1 -1
  52. package/dist/protocol.js +30 -1
  53. package/dist/protocol.js.map +1 -1
  54. package/dist/sample.d.ts +152 -0
  55. package/dist/sample.d.ts.map +1 -0
  56. package/dist/sample.js +22 -0
  57. package/dist/sample.js.map +1 -0
  58. package/dist/session-utils.d.ts +142 -0
  59. package/dist/session-utils.d.ts.map +1 -0
  60. package/dist/session-utils.js +304 -0
  61. package/dist/session-utils.js.map +1 -0
  62. package/dist/state-colors.d.ts +16 -0
  63. package/dist/state-colors.d.ts.map +1 -0
  64. package/dist/state-colors.js +48 -0
  65. package/dist/state-colors.js.map +1 -0
  66. package/dist/states.d.ts +2 -0
  67. package/dist/states.d.ts.map +1 -1
  68. package/dist/states.js +6 -1
  69. package/dist/states.js.map +1 -1
  70. package/dist/svg-renderers/agent-logos.d.ts +45 -0
  71. package/dist/svg-renderers/agent-logos.d.ts.map +1 -0
  72. package/dist/svg-renderers/agent-logos.js +238 -0
  73. package/dist/svg-renderers/agent-logos.js.map +1 -0
  74. package/dist/svg-renderers/index.d.ts +4 -0
  75. package/dist/svg-renderers/index.d.ts.map +1 -0
  76. package/dist/svg-renderers/index.js +4 -0
  77. package/dist/svg-renderers/index.js.map +1 -0
  78. package/dist/svg-renderers/session-slot-renderer.d.ts +78 -0
  79. package/dist/svg-renderers/session-slot-renderer.d.ts.map +1 -0
  80. package/dist/svg-renderers/session-slot-renderer.js +506 -0
  81. package/dist/svg-renderers/session-slot-renderer.js.map +1 -0
  82. package/dist/svg-renderers/text-utils.d.ts +29 -0
  83. package/dist/svg-renderers/text-utils.d.ts.map +1 -0
  84. package/dist/svg-renderers/text-utils.js +122 -0
  85. package/dist/svg-renderers/text-utils.js.map +1 -0
  86. package/dist/telemetry-envelope.d.ts +108 -0
  87. package/dist/telemetry-envelope.d.ts.map +1 -0
  88. package/dist/telemetry-envelope.js +34 -0
  89. package/dist/telemetry-envelope.js.map +1 -0
  90. package/dist/timeline-icons.d.ts +70 -0
  91. package/dist/timeline-icons.d.ts.map +1 -0
  92. package/dist/timeline-icons.js +161 -0
  93. package/dist/timeline-icons.js.map +1 -0
  94. package/dist/timeline-label.d.ts +38 -0
  95. package/dist/timeline-label.d.ts.map +1 -0
  96. package/dist/timeline-label.js +95 -0
  97. package/dist/timeline-label.js.map +1 -0
  98. package/dist/timeline-markdown.d.ts +80 -0
  99. package/dist/timeline-markdown.d.ts.map +1 -0
  100. package/dist/timeline-markdown.js +221 -0
  101. package/dist/timeline-markdown.js.map +1 -0
  102. package/dist/timeline-summarizer.d.ts +37 -0
  103. package/dist/timeline-summarizer.d.ts.map +1 -0
  104. package/dist/timeline-summarizer.js +125 -0
  105. package/dist/timeline-summarizer.js.map +1 -0
  106. package/dist/timeline.d.ts +135 -0
  107. package/dist/timeline.d.ts.map +1 -0
  108. package/dist/timeline.js +633 -0
  109. package/dist/timeline.js.map +1 -0
  110. package/dist/voice-paths.d.ts +4 -0
  111. package/dist/voice-paths.d.ts.map +1 -1
  112. package/dist/voice-paths.js +5 -0
  113. package/dist/voice-paths.js.map +1 -1
  114. package/package.json +9 -3
@@ -0,0 +1,29 @@
1
+ /**
2
+ * CJK-aware text measurement utilities.
3
+ * Shared across all SVG renderers (Stream Deck plugin, D200H bridge, etc.).
4
+ */
5
+ /** True for CJK / fullwidth characters (Hangul, Kanji, Kana, CJK symbols). */
6
+ export declare function isWide(code: number): boolean;
7
+ /** Pixel width of a single character at given font size (Arial approximation). */
8
+ export declare function charPx(ch: string, fontSize: number): number;
9
+ /** Estimate pixel width of a string at given font size (Arial). */
10
+ export declare function measureTextWidth(text: string, fontSize: number): number;
11
+ /** Slice string so first part fits within maxPx. */
12
+ export declare function sliceByPx(s: string, maxPx: number, fontSize: number): [string, string];
13
+ /** Word-wrap text by estimated pixel width (works for all scripts). */
14
+ export declare function wrapTextByWidth(text: string, maxWidthPx: number, fontSize: number): string[];
15
+ /**
16
+ * Strip character sequences that must never reach a text surface: ANSI escape
17
+ * sequences (PTY-derived goals/activity strings carry them), XML-invalid
18
+ * control characters, and lone surrogates. Safe for non-SVG surfaces too
19
+ * (serial/e-ink/plain text) — it only removes garbage, never visible text.
20
+ */
21
+ export declare function stripUnsafeText(s: string): string;
22
+ /**
23
+ * The one escape function every SVG renderer must use for interpolated text.
24
+ * Escaping alone is NOT enough: a single raw control character (common in
25
+ * PTY-derived session goal/activity strings) makes resvg reject the entire
26
+ * SVG, which downstream turns into a blank frame on TRMNL/D200H panels.
27
+ */
28
+ export declare function escSvgText(s: string): string;
29
+ //# sourceMappingURL=text-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-utils.d.ts","sourceRoot":"","sources":["../../src/svg-renderers/text-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,8EAA8E;AAC9E,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS5C;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED,oDAAoD;AACpD,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAUtF;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAsC5F;AAeD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO5C"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * CJK-aware text measurement utilities.
3
+ * Shared across all SVG renderers (Stream Deck plugin, D200H bridge, etc.).
4
+ */
5
+ const COMBINING_RE = /\p{M}/u;
6
+ /** True for CJK / fullwidth characters (Hangul, Kanji, Kana, CJK symbols). */
7
+ export function isWide(code) {
8
+ return ((code >= 0x1100 && code <= 0x11FF) || // Hangul Jamo
9
+ (code >= 0x2E80 && code <= 0x9FFF) || // CJK radicals, symbols, ideographs
10
+ (code >= 0xAC00 && code <= 0xD7AF) || // Hangul Syllables
11
+ (code >= 0xF900 && code <= 0xFAFF) || // CJK Compatibility Ideographs
12
+ (code >= 0xFF00 && code <= 0xFF60) || // Fullwidth forms
13
+ (code >= 0xFFE0 && code <= 0xFFE6) // Fullwidth signs
14
+ );
15
+ }
16
+ /** Pixel width of a single character at given font size (Arial approximation). */
17
+ export function charPx(ch, fontSize) {
18
+ if (COMBINING_RE.test(ch))
19
+ return 0; // combining marks: zero width
20
+ if (isWide(ch.charCodeAt(0)))
21
+ return fontSize; // CJK: ~1em
22
+ return fontSize * 0.55; // Latin, etc.: ~0.55em
23
+ }
24
+ /** Estimate pixel width of a string at given font size (Arial). */
25
+ export function measureTextWidth(text, fontSize) {
26
+ let px = 0;
27
+ for (const ch of text)
28
+ px += charPx(ch, fontSize);
29
+ return px;
30
+ }
31
+ /** Slice string so first part fits within maxPx. */
32
+ export function sliceByPx(s, maxPx, fontSize) {
33
+ let px = 0;
34
+ let i = 0;
35
+ for (const ch of s) {
36
+ const cw = charPx(ch, fontSize);
37
+ if (cw > 0 && px + cw > maxPx)
38
+ break;
39
+ px += cw;
40
+ i += ch.length;
41
+ }
42
+ return [s.slice(0, i), s.slice(i)];
43
+ }
44
+ /** Word-wrap text by estimated pixel width (works for all scripts). */
45
+ export function wrapTextByWidth(text, maxWidthPx, fontSize) {
46
+ const words = text.split(/\s+/).filter(Boolean);
47
+ const lines = [];
48
+ let current = '';
49
+ let currentPx = 0;
50
+ const spacePx = fontSize * 0.28;
51
+ for (const word of words) {
52
+ const wordPx = measureTextWidth(word, fontSize);
53
+ if (current && currentPx + spacePx + wordPx > maxWidthPx) {
54
+ lines.push(current);
55
+ current = '';
56
+ currentPx = 0;
57
+ }
58
+ if (wordPx > maxWidthPx) {
59
+ if (current) {
60
+ lines.push(current);
61
+ current = '';
62
+ currentPx = 0;
63
+ }
64
+ let rem = word;
65
+ while (measureTextWidth(rem, fontSize) > maxWidthPx) {
66
+ const [chunk, rest] = sliceByPx(rem, maxWidthPx, fontSize);
67
+ lines.push(chunk);
68
+ rem = rest;
69
+ }
70
+ current = rem;
71
+ currentPx = measureTextWidth(rem, fontSize);
72
+ }
73
+ else {
74
+ if (current) {
75
+ current += ' ' + word;
76
+ currentPx += spacePx + wordPx;
77
+ }
78
+ else {
79
+ current = word;
80
+ currentPx = wordPx;
81
+ }
82
+ }
83
+ }
84
+ if (current)
85
+ lines.push(current);
86
+ return lines;
87
+ }
88
+ // --- SVG text sanitization ---
89
+ /** ANSI escape sequences (CSI like `\x1b[31m`, OSC, and 2-char `\x1bX` forms). */
90
+ const ANSI_RE = /\x1b(?:\[[0-9;:?<=>]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\)?|[@-_])/g;
91
+ /**
92
+ * Characters XML 1.0 forbids even when escaped: C0 controls (minus \t \n \r),
93
+ * DEL, and the noncharacters U+FFFE/U+FFFF. resvg hard-fails the whole SVG
94
+ * parse on any of these, so they must be stripped, not entity-escaped.
95
+ */
96
+ const XML_INVALID_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\ufffe\uffff]/g;
97
+ /** Lone UTF-16 surrogate halves (e.g. an emoji cut by a byte-length slice). */
98
+ const LONE_SURROGATE_RE = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/g;
99
+ /**
100
+ * Strip character sequences that must never reach a text surface: ANSI escape
101
+ * sequences (PTY-derived goals/activity strings carry them), XML-invalid
102
+ * control characters, and lone surrogates. Safe for non-SVG surfaces too
103
+ * (serial/e-ink/plain text) — it only removes garbage, never visible text.
104
+ */
105
+ export function stripUnsafeText(s) {
106
+ return String(s).replace(ANSI_RE, '').replace(XML_INVALID_RE, '').replace(LONE_SURROGATE_RE, '');
107
+ }
108
+ /**
109
+ * The one escape function every SVG renderer must use for interpolated text.
110
+ * Escaping alone is NOT enough: a single raw control character (common in
111
+ * PTY-derived session goal/activity strings) makes resvg reject the entire
112
+ * SVG, which downstream turns into a blank frame on TRMNL/D200H panels.
113
+ */
114
+ export function escSvgText(s) {
115
+ return stripUnsafeText(s)
116
+ .replace(/&/g, '&amp;')
117
+ .replace(/</g, '&lt;')
118
+ .replace(/>/g, '&gt;')
119
+ .replace(/"/g, '&quot;')
120
+ .replace(/'/g, '&apos;');
121
+ }
122
+ //# sourceMappingURL=text-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-utils.js","sourceRoot":"","sources":["../../src/svg-renderers/text-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * AgentDeck telemetry envelope — internal OTel-shape struct for APME ingestion.
3
+ *
4
+ * Goal: every agent's lifecycle events (Claude hooks, Claude PTY parser, OpenClaw/
5
+ * OpenCode timeline, Codex OTLP, Codex PTY) emit the same `TelemetrySpan` struct.
6
+ * The APME collector consumes only this struct via `ingestSpan(...)`. New agents
7
+ * plug in by writing one adapter; collector plumbing stays untouched.
8
+ *
9
+ * **Not an external wire format.** This struct is bridge-internal. Codex CLI's
10
+ * real OTLP/HTTP traffic is handled separately by the Swift daemon
11
+ * (`apple/AgentDeck/Daemon/Modules/CodexOtelRoutes.swift`).
12
+ *
13
+ * Naming: where a sensible OpenTelemetry GenAI semantic convention exists
14
+ * (`gen_ai.request.model`, `gen_ai.tool.name`, etc.) we mirror it. Where it
15
+ * doesn't (response_kind, vibe, judge axes), we keep our `agentdeck.*` namespace.
16
+ * GenAI semconv is currently in "Development" status — we reference but do not
17
+ * pin to it. See docs/otel-standardization-study.md.
18
+ */
19
+ import type { AgentType } from './adapter.js';
20
+ /** Discriminator for what a span describes. Each kind has a contract about
21
+ * which attributes are required (see `TelemetryAttributes`). */
22
+ export type TelemetrySpanKind =
23
+ /** A user prompt was received → open a new turn. Carries `agentdeck.prompt_text`. */
24
+ 'turn_start'
25
+ /** Assistant response captured → set on the active turn. Carries `agentdeck.response_text`. */
26
+ | 'turn_response'
27
+ /** Explicit turn close (rare — most sources auto-close on next `turn_start`). */
28
+ | 'turn_end'
29
+ /** A tool was invoked. Carries `gen_ai.tool.name`. */
30
+ | 'tool_call'
31
+ /** A tool finished. May carry `gen_ai.tool.name` if available. */
32
+ | 'tool_result'
33
+ /** The agent declared task boundary. Carries `agentdeck.boundary_signal`. */
34
+ | 'task_boundary'
35
+ /** Model id resolved or usage snapshot updated. Carries either
36
+ * `gen_ai.request.model` or `agentdeck.usage.*`. */
37
+ | 'session_meta'
38
+ /** Generic step — record verbatim into `steps` table without lifecycle
39
+ * side effects. Carries `agentdeck.raw_event` + `agentdeck.raw_payload`. */
40
+ | 'raw_step';
41
+ /** OTel-shape attributes. Keys mirror GenAI semconv where available;
42
+ * AgentDeck-specific data lives under `agentdeck.*`. All keys are optional —
43
+ * consumers should defensively check for the ones their kind requires. */
44
+ export interface TelemetryAttributes {
45
+ /** Model id, e.g. "claude-opus-4-7". */
46
+ 'gen_ai.request.model'?: string;
47
+ /** Tool name for tool_call / tool_result. */
48
+ 'gen_ai.tool.name'?: string;
49
+ /** Provider/system, e.g. "anthropic", "openai". */
50
+ 'gen_ai.system'?: string;
51
+ 'agentdeck.agent_type'?: AgentType;
52
+ 'agentdeck.cwd'?: string;
53
+ /** User prompt text for turn_start. */
54
+ 'agentdeck.prompt_text'?: string;
55
+ /** Assistant response text for turn_response. */
56
+ 'agentdeck.response_text'?: string;
57
+ /** Task boundary discriminator. */
58
+ 'agentdeck.boundary_signal'?: 'todo_complete' | 'clear' | 'session_end' | 'manual' | 'idle_gap';
59
+ /** Original event name for raw_step. */
60
+ 'agentdeck.raw_event'?: string;
61
+ /** Original event payload for raw_step (JSON-serializable). */
62
+ 'agentdeck.raw_payload'?: Record<string, unknown>;
63
+ /** Tool name as reported by the source (Claude hooks use `tool_name`). */
64
+ 'agentdeck.tool_name'?: string;
65
+ 'agentdeck.usage.input_tokens'?: number;
66
+ 'agentdeck.usage.output_tokens'?: number;
67
+ 'agentdeck.usage.cost_usd'?: number;
68
+ /** Open extension. Adapters may attach extra string-keyed scalars. */
69
+ [k: string]: string | number | boolean | Record<string, unknown> | undefined;
70
+ }
71
+ /** A single ingestion event. `traceId` ≈ run id (one trace per session/run);
72
+ * `spanId` is a unique id per emitted event; `parentSpanId` optionally points
73
+ * to the active turn. The collector currently uses the `kind`/attributes
74
+ * directly; the trace/span ids are present for future OTLP export. */
75
+ export interface TelemetrySpan {
76
+ traceId: string;
77
+ spanId: string;
78
+ parentSpanId?: string;
79
+ /** Human-readable span name, e.g. "agentdeck.turn", "agentdeck.tool.call". */
80
+ name: string;
81
+ kind: TelemetrySpanKind;
82
+ /** Wall-clock millis when the event happened. Adapters use Date.now() if
83
+ * the source doesn't provide a timestamp. */
84
+ ts: number;
85
+ attributes: TelemetryAttributes;
86
+ }
87
+ /** Source kind passed to adapters. Mostly informational — used by the collector
88
+ * for debug logs and by tests for fixture filtering. */
89
+ export type TelemetrySource = 'claude-hook' | 'claude-pty' | 'timeline' | 'codex-pty' | 'codex-otlp';
90
+ /** Opaque context passed to adapters. Holds the session-stable identifiers an
91
+ * adapter would otherwise have to thread through every call. */
92
+ export interface AdapterContext {
93
+ sessionId: string;
94
+ agentType: AgentType;
95
+ /** trace id for this session/run. Adapters should reuse this for every span
96
+ * they emit during the session — the collector dispatches by sessionId so
97
+ * trace_id is mostly informational, but keeping it stable lets future OTLP
98
+ * exports tie spans together. */
99
+ traceId: string;
100
+ cwd?: string;
101
+ /** Optional: current active turn id, populated by the collector after
102
+ * `turn_start` is processed. Adapters can read but should not write. */
103
+ activeTurnId?: string;
104
+ }
105
+ /** Canonical span name for a kind. Used by adapters so all spans for the same
106
+ * kind share a name — makes log filtering and OTLP queries predictable. */
107
+ export declare function spanNameForKind(kind: TelemetrySpanKind): string;
108
+ //# sourceMappingURL=telemetry-envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-envelope.d.ts","sourceRoot":"","sources":["../src/telemetry-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;iEACiE;AACjE,MAAM,MAAM,iBAAiB;AAC3B,qFAAqF;AACnF,YAAY;AACd,+FAA+F;GAC7F,eAAe;AACjB,iFAAiF;GAC/E,UAAU;AACZ,sDAAsD;GACpD,WAAW;AACb,kEAAkE;GAChE,aAAa;AACf,6EAA6E;GAC3E,eAAe;AACjB;qDACqD;GACnD,cAAc;AAChB;6EAC6E;GAC3E,UAAU,CAAC;AAEf;;2EAE2E;AAC3E,MAAM,WAAW,mBAAmB;IAElC,wCAAwC;IACxC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,sBAAsB,CAAC,EAAE,SAAS,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,iDAAiD;IACjD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,mCAAmC;IACnC,2BAA2B,CAAC,EAAE,eAAe,GAAG,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC;IAChG,wCAAwC;IACxC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,+DAA+D;IAC/D,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClD,0EAA0E;IAC1E,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC,sEAAsE;IACtE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC9E;AAED;;;uEAGuE;AACvE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;IACxB;kDAC8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,mBAAmB,CAAC;CACjC;AAID;yDACyD;AACzD,MAAM,MAAM,eAAe,GACvB,aAAa,GACb,YAAY,GACZ,UAAU,GACV,WAAW,GACX,YAAY,CAAC;AAEjB;iEACiE;AACjE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB;;;sCAGkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;6EACyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;4EAC4E;AAC5E,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAW/D"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * AgentDeck telemetry envelope — internal OTel-shape struct for APME ingestion.
3
+ *
4
+ * Goal: every agent's lifecycle events (Claude hooks, Claude PTY parser, OpenClaw/
5
+ * OpenCode timeline, Codex OTLP, Codex PTY) emit the same `TelemetrySpan` struct.
6
+ * The APME collector consumes only this struct via `ingestSpan(...)`. New agents
7
+ * plug in by writing one adapter; collector plumbing stays untouched.
8
+ *
9
+ * **Not an external wire format.** This struct is bridge-internal. Codex CLI's
10
+ * real OTLP/HTTP traffic is handled separately by the Swift daemon
11
+ * (`apple/AgentDeck/Daemon/Modules/CodexOtelRoutes.swift`).
12
+ *
13
+ * Naming: where a sensible OpenTelemetry GenAI semantic convention exists
14
+ * (`gen_ai.request.model`, `gen_ai.tool.name`, etc.) we mirror it. Where it
15
+ * doesn't (response_kind, vibe, judge axes), we keep our `agentdeck.*` namespace.
16
+ * GenAI semconv is currently in "Development" status — we reference but do not
17
+ * pin to it. See docs/otel-standardization-study.md.
18
+ */
19
+ // ─── Naming helpers ──────────────────────────────────────────────────────────
20
+ /** Canonical span name for a kind. Used by adapters so all spans for the same
21
+ * kind share a name — makes log filtering and OTLP queries predictable. */
22
+ export function spanNameForKind(kind) {
23
+ switch (kind) {
24
+ case 'turn_start': return 'agentdeck.turn.start';
25
+ case 'turn_response': return 'agentdeck.turn.response';
26
+ case 'turn_end': return 'agentdeck.turn.end';
27
+ case 'tool_call': return 'agentdeck.tool.call';
28
+ case 'tool_result': return 'agentdeck.tool.result';
29
+ case 'task_boundary': return 'agentdeck.task.boundary';
30
+ case 'session_meta': return 'agentdeck.session.meta';
31
+ case 'raw_step': return 'agentdeck.step.raw';
32
+ }
33
+ }
34
+ //# sourceMappingURL=telemetry-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-envelope.js","sourceRoot":"","sources":["../src/telemetry-envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA2GH,gFAAgF;AAEhF;4EAC4E;AAC5E,MAAM,UAAU,eAAe,CAAC,IAAuB;IACrD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY,CAAC,CAAI,OAAO,sBAAsB,CAAC;QACpD,KAAK,eAAe,CAAC,CAAC,OAAO,yBAAyB,CAAC;QACvD,KAAK,UAAU,CAAC,CAAM,OAAO,oBAAoB,CAAC;QAClD,KAAK,WAAW,CAAC,CAAK,OAAO,qBAAqB,CAAC;QACnD,KAAK,aAAa,CAAC,CAAG,OAAO,uBAAuB,CAAC;QACrD,KAAK,eAAe,CAAC,CAAC,OAAO,yBAAyB,CAAC;QACvD,KAAK,cAAc,CAAC,CAAE,OAAO,wBAAwB,CAAC;QACtD,KAAK,UAAU,CAAC,CAAM,OAAO,oBAAoB,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Shared icon-key system for timeline entries.
3
+ *
4
+ * Each platform maps the abstract key to its native form:
5
+ * - Apple: SF Symbols (`checkmark.circle.fill`, etc.)
6
+ * - Android tablet: Material Icons (`Icons.Filled.CheckCircle`)
7
+ * - Android e-ink: ASCII bracket markers (`[OK]`, `[T ]`) — high black coverage,
8
+ * no thin glyph strokes that ghost on partial refresh.
9
+ *
10
+ * This is the single source of truth. If you add a new key, add the platform
11
+ * mapping in the same commit (apple TimelineStripView.swift,
12
+ * android TimelineStrip.kt + EinkTimelinePanel.kt).
13
+ */
14
+ import type { TimelineEntry, TimelineEntryType } from './timeline.js';
15
+ export type TimelineIconKey = 'success' | 'error' | 'running' | 'awaiting' | 'tool' | 'model' | 'user' | 'task' | 'scheduled' | 'memory';
16
+ /**
17
+ * Map a timeline entry to its semantic icon key.
18
+ * Looks at both the entry type and (for tool_request) the approval status.
19
+ */
20
+ export declare function timelineIconKey(entry: Pick<TimelineEntry, 'type' | 'status'>): TimelineIconKey;
21
+ /** All known icon keys, in stable order (used for tests/registries). */
22
+ export declare const TIMELINE_ICON_KEYS: readonly TimelineIconKey[];
23
+ /**
24
+ * True when `entry` is a `task_start` whose matching `task_end` (same `taskId`)
25
+ * has not yet appeared among `siblings`. Lets clients render in-flight task
26
+ * hierarchy markers with the rotating "running" treatment instead of the
27
+ * static `task` icon. Mirrored in Apple `isInFlightTask` and Android
28
+ * `isInFlightTask`.
29
+ */
30
+ export declare function isInFlightTask(entry: Pick<TimelineEntry, 'type' | 'taskId'>, siblings: ReadonlyArray<Pick<TimelineEntry, 'type' | 'taskId'>>): boolean;
31
+ /**
32
+ * Age cap for the rotating "running" treatment on turn rows. Turn-completion
33
+ * signals are best-effort (Stop hook, transcript tail, PTY marker) and can be
34
+ * lost across daemon handoffs — a chat_start older than this stops spinning
35
+ * even without an explicit completion so dead turns don't animate forever.
36
+ * Matches Apple's `chatStartMaxAgeSec` (TimelineStripView.swift). Task
37
+ * hierarchy markers are handled by the daemon-side orphan reaper plus Apple's
38
+ * own in-flight cap.
39
+ */
40
+ export declare const ROTATING_ENTRY_MAX_AGE_MS: number;
41
+ type RotatingSibling = Pick<TimelineEntry, 'type' | 'taskId'> & Partial<Pick<TimelineEntry, 'sessionId' | 'ts'>>;
42
+ /**
43
+ * True when a turn row should rotate its leading icon. Combines the static
44
+ * `running` icon-key (chat_start, unknown types) with the in-flight
45
+ * task-hierarchy signal so an open `task_start` also spins until its
46
+ * `task_end` arrives. `siblings` should be the entries the row sees in its
47
+ * group/list — passing `[]` falls back to icon-key only.
48
+ *
49
+ * A chat_start only spins while its turn is plausibly still open:
50
+ * - no later same-session completion (chat_response / chat_end / model_response),
51
+ * - no later same-session chat_start (a new prompt supersedes the turn even
52
+ * when its completion signal was lost — Stop hooks are best-effort),
53
+ * - younger than ROTATING_ENTRY_MAX_AGE_MS.
54
+ * Mirrored in Apple TimelineStripView.swift and Android TimelineIcons.kt —
55
+ * update all three in the same commit.
56
+ */
57
+ export declare function isRotatingEntry(entry: Pick<TimelineEntry, 'type' | 'status' | 'taskId'> & Partial<Pick<TimelineEntry, 'sessionId' | 'ts'>>, siblings: ReadonlyArray<RotatingSibling>, nowMs?: number): boolean;
58
+ /**
59
+ * E-ink ASCII glyphs — bracket-padded so total width is constant (4 chars)
60
+ * for column alignment on monospace bitmap fonts.
61
+ */
62
+ export declare const EINK_ICON_GLYPHS: Record<TimelineIconKey, string>;
63
+ /**
64
+ * Whether a timeline entry type carries a content body worth showing in the
65
+ * detail pane. task_start/task_end/task_milestone are hierarchy markers —
66
+ * their `raw` is the summary, no extra detail expected.
67
+ */
68
+ export declare function entryHasDetailBody(type: TimelineEntryType): boolean;
69
+ export {};
70
+ //# sourceMappingURL=timeline-icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-icons.d.ts","sourceRoot":"","sources":["../src/timeline-icons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEtE,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,OAAO,GACP,SAAS,GACT,UAAU,GACV,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,WAAW,GACX,QAAQ,CAAC;AAEb;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,GAAG,eAAe,CAoC9F;AAED,wEAAwE;AACxE,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAI/C,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,EAC7C,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,GAC9D,OAAO,CAOT;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,QAAiB,CAAC;AAExD,KAAK,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC;AASnD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC,GACtD,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,EAClD,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,EACxC,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAiBT;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAW5D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAEnE"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Shared icon-key system for timeline entries.
3
+ *
4
+ * Each platform maps the abstract key to its native form:
5
+ * - Apple: SF Symbols (`checkmark.circle.fill`, etc.)
6
+ * - Android tablet: Material Icons (`Icons.Filled.CheckCircle`)
7
+ * - Android e-ink: ASCII bracket markers (`[OK]`, `[T ]`) — high black coverage,
8
+ * no thin glyph strokes that ghost on partial refresh.
9
+ *
10
+ * This is the single source of truth. If you add a new key, add the platform
11
+ * mapping in the same commit (apple TimelineStripView.swift,
12
+ * android TimelineStrip.kt + EinkTimelinePanel.kt).
13
+ */
14
+ /**
15
+ * Map a timeline entry to its semantic icon key.
16
+ * Looks at both the entry type and (for tool_request) the approval status.
17
+ */
18
+ export function timelineIconKey(entry) {
19
+ switch (entry.type) {
20
+ case 'task_start':
21
+ case 'task_end':
22
+ return 'task';
23
+ case 'task_milestone':
24
+ return 'success';
25
+ case 'chat_start':
26
+ return 'running';
27
+ case 'chat_end':
28
+ case 'chat_response':
29
+ case 'model_response':
30
+ return 'success';
31
+ case 'model_call':
32
+ return 'model';
33
+ case 'tool_request':
34
+ if (entry.status === 'approved')
35
+ return 'success';
36
+ if (entry.status === 'denied')
37
+ return 'error';
38
+ return 'awaiting';
39
+ case 'tool_resolved':
40
+ return 'success';
41
+ case 'tool_exec':
42
+ return 'tool';
43
+ case 'error':
44
+ return 'error';
45
+ case 'user_action':
46
+ return 'user';
47
+ case 'scheduled':
48
+ return 'scheduled';
49
+ case 'memory_recall':
50
+ return 'memory';
51
+ case 'eval_result':
52
+ return entry.status === 'denied' ? 'error' : 'success';
53
+ default:
54
+ return 'running';
55
+ }
56
+ }
57
+ /** All known icon keys, in stable order (used for tests/registries). */
58
+ export const TIMELINE_ICON_KEYS = [
59
+ 'success', 'error', 'running', 'awaiting',
60
+ 'tool', 'model', 'user', 'task',
61
+ 'scheduled', 'memory',
62
+ ];
63
+ /**
64
+ * True when `entry` is a `task_start` whose matching `task_end` (same `taskId`)
65
+ * has not yet appeared among `siblings`. Lets clients render in-flight task
66
+ * hierarchy markers with the rotating "running" treatment instead of the
67
+ * static `task` icon. Mirrored in Apple `isInFlightTask` and Android
68
+ * `isInFlightTask`.
69
+ */
70
+ export function isInFlightTask(entry, siblings) {
71
+ if (entry.type !== 'task_start')
72
+ return false;
73
+ if (!entry.taskId)
74
+ return false;
75
+ for (const s of siblings) {
76
+ if (s.type === 'task_end' && s.taskId === entry.taskId)
77
+ return false;
78
+ }
79
+ return true;
80
+ }
81
+ /**
82
+ * Age cap for the rotating "running" treatment on turn rows. Turn-completion
83
+ * signals are best-effort (Stop hook, transcript tail, PTY marker) and can be
84
+ * lost across daemon handoffs — a chat_start older than this stops spinning
85
+ * even without an explicit completion so dead turns don't animate forever.
86
+ * Matches Apple's `chatStartMaxAgeSec` (TimelineStripView.swift). Task
87
+ * hierarchy markers are handled by the daemon-side orphan reaper plus Apple's
88
+ * own in-flight cap.
89
+ */
90
+ export const ROTATING_ENTRY_MAX_AGE_MS = 10 * 60 * 1000;
91
+ /** Same-session test mirroring the turn-merge rule: both ids equal, or both
92
+ * absent (legacy single-session emitters). */
93
+ function sameRotatingSession(a, b) {
94
+ if (!a && !b)
95
+ return true;
96
+ return !!a && !!b && a === b;
97
+ }
98
+ /**
99
+ * True when a turn row should rotate its leading icon. Combines the static
100
+ * `running` icon-key (chat_start, unknown types) with the in-flight
101
+ * task-hierarchy signal so an open `task_start` also spins until its
102
+ * `task_end` arrives. `siblings` should be the entries the row sees in its
103
+ * group/list — passing `[]` falls back to icon-key only.
104
+ *
105
+ * A chat_start only spins while its turn is plausibly still open:
106
+ * - no later same-session completion (chat_response / chat_end / model_response),
107
+ * - no later same-session chat_start (a new prompt supersedes the turn even
108
+ * when its completion signal was lost — Stop hooks are best-effort),
109
+ * - younger than ROTATING_ENTRY_MAX_AGE_MS.
110
+ * Mirrored in Apple TimelineStripView.swift and Android TimelineIcons.kt —
111
+ * update all three in the same commit.
112
+ */
113
+ export function isRotatingEntry(entry, siblings, nowMs) {
114
+ if (timelineIconKey(entry) === 'running') {
115
+ if (entry.type !== 'chat_start')
116
+ return true;
117
+ const ts = entry.ts;
118
+ if (ts != null) {
119
+ const now = nowMs ?? Date.now();
120
+ if (now - ts > ROTATING_ENTRY_MAX_AGE_MS)
121
+ return false;
122
+ for (const s of siblings) {
123
+ if (s.ts == null || s.ts < ts)
124
+ continue;
125
+ if (!sameRotatingSession(entry.sessionId, s.sessionId))
126
+ continue;
127
+ if (s.type === 'chat_response' || s.type === 'chat_end' || s.type === 'model_response')
128
+ return false;
129
+ if (s.type === 'chat_start' && s.ts > ts)
130
+ return false;
131
+ }
132
+ }
133
+ return true;
134
+ }
135
+ return isInFlightTask(entry, siblings);
136
+ }
137
+ /**
138
+ * E-ink ASCII glyphs — bracket-padded so total width is constant (4 chars)
139
+ * for column alignment on monospace bitmap fonts.
140
+ */
141
+ export const EINK_ICON_GLYPHS = {
142
+ success: '[OK]',
143
+ error: '[!!]',
144
+ running: '[..]',
145
+ awaiting: '[??]',
146
+ tool: '[T ]',
147
+ model: '[M ]',
148
+ user: '[U ]',
149
+ task: '[==]',
150
+ scheduled: '[S ]',
151
+ memory: '[~ ]',
152
+ };
153
+ /**
154
+ * Whether a timeline entry type carries a content body worth showing in the
155
+ * detail pane. task_start/task_end/task_milestone are hierarchy markers —
156
+ * their `raw` is the summary, no extra detail expected.
157
+ */
158
+ export function entryHasDetailBody(type) {
159
+ return type !== 'task_start' && type !== 'task_end' && type !== 'task_milestone';
160
+ }
161
+ //# sourceMappingURL=timeline-icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-icons.js","sourceRoot":"","sources":["../src/timeline-icons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { TimelineEntry } from './timeline.js';
2
+ /** Canonical agent → display-name map. The ONLY place this mapping lives on
3
+ * the TS side; language mirrors copy it verbatim. Never abbreviate
4
+ * "OpenClaw" (see memory `brand-direction.md`). */
5
+ export declare function agentDisplayLabel(agentType: string | undefined | null): string;
6
+ /** Compact agent → display-name for very tight surfaces (LED matrix, ticker
7
+ * when width is scarce). Keeps the brand distinct without the CLI/App suffix. */
8
+ export declare function agentShortLabel(agentType: string | undefined | null): string;
9
+ export interface TimelineRowAttribution {
10
+ /** Resolved agent display name, or undefined when no agent is attributed. */
11
+ agent?: string;
12
+ /** Project / worktree name. */
13
+ project?: string;
14
+ /** Task label — the APME task summary when present, else the task row's
15
+ * own label; undefined for rows outside a task. */
16
+ task?: string;
17
+ /** The already-summarized row body (`entry.raw`). */
18
+ text: string;
19
+ }
20
+ /** Decompose a TimelineEntry into its attributed pieces. Every surface builds
21
+ * its displayed row from this, so agent/project/task/text stay consistent. */
22
+ export declare function timelineRowAttribution(entry: TimelineEntry): TimelineRowAttribution;
23
+ export interface FormatTimelineRowOptions {
24
+ /** Which attribution parts to prepend before the text, in order. Defaults
25
+ * to agent + project (the common glance form). */
26
+ parts?: Array<'agent' | 'project' | 'task'>;
27
+ /** Include the row body text after the attribution. Default true. */
28
+ includeText?: boolean;
29
+ /** Use the compact agent name (ticker/matrix). Default false. */
30
+ short?: boolean;
31
+ /** Separator between attribution parts. Default " · ". */
32
+ separator?: string;
33
+ }
34
+ /** Canonical joined label. Replaces the divergent "[project] Agent" vs
35
+ * "project · tag" conventions with one separator (" · ") and an explicit,
36
+ * caller-chosen part order. Example: "Claude · AgentDeck · Fix eink ticker". */
37
+ export declare function formatTimelineRowLabel(entry: TimelineEntry, opts?: FormatTimelineRowOptions): string;
38
+ //# sourceMappingURL=timeline-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-label.d.ts","sourceRoot":"","sources":["../src/timeline-label.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;oDAEoD;AACpD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAkB9E;AAED;kFACkF;AAClF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAM5E;AAED,MAAM,WAAW,sBAAsB;IACrC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;wDACoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;CACd;AAeD;+EAC+E;AAC/E,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,sBAAsB,CAWnF;AAED,MAAM,WAAW,wBAAwB;IACvC;uDACmD;IACnD,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,CAAC;IAC5C,qEAAqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;iFAEiF;AACjF,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,aAAa,EACpB,IAAI,GAAE,wBAA6B,GAClC,MAAM,CAYR"}