@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.
- package/LICENSE +21 -0
- package/dist/adapter.d.ts +18 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +62 -0
- package/dist/adapter.js.map +1 -1
- package/dist/command-builders.d.ts +35 -0
- package/dist/command-builders.d.ts.map +1 -0
- package/dist/command-builders.js +75 -0
- package/dist/command-builders.js.map +1 -0
- package/dist/connection-status.d.ts +42 -0
- package/dist/connection-status.d.ts.map +1 -0
- package/dist/connection-status.js +52 -0
- package/dist/connection-status.js.map +1 -0
- package/dist/d200h-layout.d.ts +131 -0
- package/dist/d200h-layout.d.ts.map +1 -0
- package/dist/d200h-layout.js +627 -0
- package/dist/d200h-layout.js.map +1 -0
- package/dist/design-tokens.d.ts +259 -0
- package/dist/design-tokens.d.ts.map +1 -0
- package/dist/design-tokens.js +156 -0
- package/dist/design-tokens.js.map +1 -0
- package/dist/eval-schema.d.ts +235 -0
- package/dist/eval-schema.d.ts.map +1 -0
- package/dist/eval-schema.js +23 -0
- package/dist/eval-schema.js.map +1 -0
- package/dist/format-utils.d.ts +52 -0
- package/dist/format-utils.d.ts.map +1 -0
- package/dist/format-utils.js +165 -0
- package/dist/format-utils.js.map +1 -0
- package/dist/gateway-protocol.d.ts +391 -0
- package/dist/gateway-protocol.d.ts.map +1 -0
- package/dist/gateway-protocol.js +19 -0
- package/dist/gateway-protocol.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/llm-settings.d.ts +52 -0
- package/dist/llm-settings.d.ts.map +1 -0
- package/dist/llm-settings.js +118 -0
- package/dist/llm-settings.js.map +1 -0
- package/dist/net-utils.d.ts +2 -1
- package/dist/net-utils.d.ts.map +1 -1
- package/dist/net-utils.js +100 -3
- package/dist/net-utils.js.map +1 -1
- package/dist/pricing.d.ts +45 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +93 -0
- package/dist/pricing.js.map +1 -0
- package/dist/protocol.d.ts +496 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +30 -1
- package/dist/protocol.js.map +1 -1
- package/dist/sample.d.ts +152 -0
- package/dist/sample.d.ts.map +1 -0
- package/dist/sample.js +22 -0
- package/dist/sample.js.map +1 -0
- package/dist/session-utils.d.ts +142 -0
- package/dist/session-utils.d.ts.map +1 -0
- package/dist/session-utils.js +304 -0
- package/dist/session-utils.js.map +1 -0
- package/dist/state-colors.d.ts +16 -0
- package/dist/state-colors.d.ts.map +1 -0
- package/dist/state-colors.js +48 -0
- package/dist/state-colors.js.map +1 -0
- package/dist/states.d.ts +2 -0
- package/dist/states.d.ts.map +1 -1
- package/dist/states.js +6 -1
- package/dist/states.js.map +1 -1
- package/dist/svg-renderers/agent-logos.d.ts +45 -0
- package/dist/svg-renderers/agent-logos.d.ts.map +1 -0
- package/dist/svg-renderers/agent-logos.js +238 -0
- package/dist/svg-renderers/agent-logos.js.map +1 -0
- package/dist/svg-renderers/index.d.ts +4 -0
- package/dist/svg-renderers/index.d.ts.map +1 -0
- package/dist/svg-renderers/index.js +4 -0
- package/dist/svg-renderers/index.js.map +1 -0
- package/dist/svg-renderers/session-slot-renderer.d.ts +78 -0
- package/dist/svg-renderers/session-slot-renderer.d.ts.map +1 -0
- package/dist/svg-renderers/session-slot-renderer.js +506 -0
- package/dist/svg-renderers/session-slot-renderer.js.map +1 -0
- package/dist/svg-renderers/text-utils.d.ts +29 -0
- package/dist/svg-renderers/text-utils.d.ts.map +1 -0
- package/dist/svg-renderers/text-utils.js +122 -0
- package/dist/svg-renderers/text-utils.js.map +1 -0
- package/dist/telemetry-envelope.d.ts +108 -0
- package/dist/telemetry-envelope.d.ts.map +1 -0
- package/dist/telemetry-envelope.js +34 -0
- package/dist/telemetry-envelope.js.map +1 -0
- package/dist/timeline-icons.d.ts +70 -0
- package/dist/timeline-icons.d.ts.map +1 -0
- package/dist/timeline-icons.js +161 -0
- package/dist/timeline-icons.js.map +1 -0
- package/dist/timeline-label.d.ts +38 -0
- package/dist/timeline-label.d.ts.map +1 -0
- package/dist/timeline-label.js +95 -0
- package/dist/timeline-label.js.map +1 -0
- package/dist/timeline-markdown.d.ts +80 -0
- package/dist/timeline-markdown.d.ts.map +1 -0
- package/dist/timeline-markdown.js +221 -0
- package/dist/timeline-markdown.js.map +1 -0
- package/dist/timeline-summarizer.d.ts +37 -0
- package/dist/timeline-summarizer.d.ts.map +1 -0
- package/dist/timeline-summarizer.js +125 -0
- package/dist/timeline-summarizer.js.map +1 -0
- package/dist/timeline.d.ts +135 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +633 -0
- package/dist/timeline.js.map +1 -0
- package/dist/voice-paths.d.ts +4 -0
- package/dist/voice-paths.d.ts.map +1 -1
- package/dist/voice-paths.js +5 -0
- package/dist/voice-paths.js.map +1 -1
- 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, '&')
|
|
117
|
+
.replace(/</g, '<')
|
|
118
|
+
.replace(/>/g, '>')
|
|
119
|
+
.replace(/"/g, '"')
|
|
120
|
+
.replace(/'/g, ''');
|
|
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"}
|