@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,95 @@
1
+ // timeline-label.ts — the SINGLE source of truth for turning a TimelineEntry
2
+ // into its attributed, human-readable row text ("agent · project · task ·
3
+ // text").
4
+ //
5
+ // Before this file, shared/src exposed `agentBrandColor` but NO agent
6
+ // display-name function, so every surface (Swift ×5, Kotlin ×2, ESP32 ×4,
7
+ // plugin) hand-rolled the same `claude-code → "Claude"` map and its own
8
+ // separator convention, and they drifted. The mirrors (Swift
9
+ // `SessionFormatters.displayAgentLabel`, Kotlin `EinkFormatUtils`, C++
10
+ // `agent_label.h`) must match `agentDisplayLabel` below exactly.
11
+ //
12
+ // Design: `timelineRowAttribution` returns the STRUCTURED pieces so a
13
+ // space-constrained surface (ESP32 ticker, iPhone) can pick which fields to
14
+ // show, while roomy surfaces render all four. `formatTimelineRowLabel` joins
15
+ // them with the one canonical separator.
16
+ /** Canonical agent → display-name map. The ONLY place this mapping lives on
17
+ * the TS side; language mirrors copy it verbatim. Never abbreviate
18
+ * "OpenClaw" (see memory `brand-direction.md`). */
19
+ export function agentDisplayLabel(agentType) {
20
+ switch (agentType) {
21
+ case 'claude-code': return 'Claude';
22
+ case 'openclaw': return 'OpenClaw';
23
+ case 'codex-cli': return 'Codex CLI';
24
+ case 'codex-app': return 'Codex App';
25
+ case 'opencode': return 'OpenCode';
26
+ case 'antigravity': return 'Antigravity';
27
+ case 'monitor': return 'Monitor';
28
+ case 'daemon': return 'Daemon';
29
+ default:
30
+ if (!agentType)
31
+ return 'Agent';
32
+ // Unknown but non-empty: titlecase the slug so a new agent still reads
33
+ // sensibly instead of collapsing to a generic "Agent".
34
+ return agentType
35
+ .replace(/-/g, ' ')
36
+ .replace(/\b\w/g, (c) => c.toUpperCase());
37
+ }
38
+ }
39
+ /** Compact agent → display-name for very tight surfaces (LED matrix, ticker
40
+ * when width is scarce). Keeps the brand distinct without the CLI/App suffix. */
41
+ export function agentShortLabel(agentType) {
42
+ switch (agentType) {
43
+ case 'codex-cli':
44
+ case 'codex-app': return 'Codex';
45
+ default: return agentDisplayLabel(agentType);
46
+ }
47
+ }
48
+ /** The task pieces vary: a `task_start`/`task_end` row's own `raw` is the task
49
+ * label ("Task 1"); a turn row inside a task carries `taskId` but no inline
50
+ * label, so callers that want a task chip use `taskSummary` when the eval has
51
+ * produced one. Returns the best available task descriptor. */
52
+ function taskDescriptor(entry) {
53
+ const summary = entry.taskSummary?.trim();
54
+ if (summary)
55
+ return summary;
56
+ if (entry.type === 'task_start' || entry.type === 'task_end') {
57
+ return entry.raw?.trim() || undefined;
58
+ }
59
+ return undefined;
60
+ }
61
+ /** Decompose a TimelineEntry into its attributed pieces. Every surface builds
62
+ * its displayed row from this, so agent/project/task/text stay consistent. */
63
+ export function timelineRowAttribution(entry) {
64
+ const agent = entry.agentType ? agentDisplayLabel(entry.agentType) : undefined;
65
+ const project = entry.projectName?.trim() || undefined;
66
+ const task = taskDescriptor(entry);
67
+ const text = (entry.raw ?? '').trim();
68
+ return {
69
+ ...(agent ? { agent } : {}),
70
+ ...(project ? { project } : {}),
71
+ ...(task ? { task } : {}),
72
+ text,
73
+ };
74
+ }
75
+ /** Canonical joined label. Replaces the divergent "[project] Agent" vs
76
+ * "project · tag" conventions with one separator (" · ") and an explicit,
77
+ * caller-chosen part order. Example: "Claude · AgentDeck · Fix eink ticker". */
78
+ export function formatTimelineRowLabel(entry, opts = {}) {
79
+ const { parts = ['agent', 'project'], includeText = true, short = false, separator = ' · ' } = opts;
80
+ const attr = timelineRowAttribution(entry);
81
+ const agentText = short ? agentShortLabel(entry.agentType) : attr.agent;
82
+ const pieces = [];
83
+ for (const p of parts) {
84
+ if (p === 'agent' && agentText)
85
+ pieces.push(agentText);
86
+ else if (p === 'project' && attr.project)
87
+ pieces.push(attr.project);
88
+ else if (p === 'task' && attr.task)
89
+ pieces.push(attr.task);
90
+ }
91
+ if (includeText && attr.text)
92
+ pieces.push(attr.text);
93
+ return pieces.join(separator);
94
+ }
95
+ //# sourceMappingURL=timeline-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-label.js","sourceRoot":"","sources":["../src/timeline-label.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Lightweight markdown parser for timeline detail panes.
3
+ *
4
+ * Output is a flat array of typed lines that each platform renders natively
5
+ * (SwiftUI / Compose). Pure data — no rendering. Mirrors the Apple parser at
6
+ * apple/AgentDeck/UI/Monitor/TimelineStripView.swift `TimelineMarkdownLine`
7
+ * and the Android port at
8
+ * android/.../ui/timeline/TimelineMarkdown.kt — keep the three in lockstep.
9
+ *
10
+ * Block grammar (line-oriented):
11
+ * - ``` toggles a code fence; lines inside become `code` (verbatim, no inline parsing)
12
+ * - empty / whitespace-only line → `blank`
13
+ * - 1-6 leading hashes + space → `heading` (level 1-6; CommonMark ATX)
14
+ * - `- ` or `* ` → `bullet`
15
+ * - `<digits>.` or `<digits>)` followed by space → `numbered`
16
+ * - `> ` → `quote`
17
+ * - `|...|` (with optional `|---|` separator) → `table` block
18
+ * - anything else → `text`
19
+ *
20
+ * Inline grammar (per `parseInlineSpans`, applied at render time to non-code
21
+ * line content + table cells; first-match-wins, no recursion):
22
+ * - `` `code` `` → `code` span
23
+ * - `**bold**` → `bold` span
24
+ * - `*italic*` → `italic` span (only when not adjacent to another `*`)
25
+ * - `[text](href)` → `link` span
26
+ * - everything else → `plain` span
27
+ */
28
+ export type MarkdownLine = {
29
+ kind: 'blank';
30
+ } | {
31
+ kind: 'heading';
32
+ level: 1 | 2 | 3 | 4 | 5 | 6;
33
+ content: string;
34
+ } | {
35
+ kind: 'bullet';
36
+ content: string;
37
+ } | {
38
+ kind: 'numbered';
39
+ marker: string;
40
+ content: string;
41
+ } | {
42
+ kind: 'quote';
43
+ content: string;
44
+ } | {
45
+ kind: 'code';
46
+ content: string;
47
+ } | {
48
+ kind: 'text';
49
+ content: string;
50
+ } | {
51
+ kind: 'table';
52
+ rows: string[][];
53
+ hasHeader: boolean;
54
+ };
55
+ export type InlineSpan = {
56
+ kind: 'plain';
57
+ text: string;
58
+ } | {
59
+ kind: 'bold';
60
+ text: string;
61
+ } | {
62
+ kind: 'italic';
63
+ text: string;
64
+ } | {
65
+ kind: 'code';
66
+ text: string;
67
+ } | {
68
+ kind: 'link';
69
+ text: string;
70
+ href: string;
71
+ };
72
+ export declare function parseTimelineMarkdown(text: string): MarkdownLine[];
73
+ /**
74
+ * Tokenize a single line of inline content into spans. First-match-wins
75
+ * left-to-right walker; never recurses (so e.g. `**bold *with italic***` is
76
+ * just bold + plain — keeps the implementation simple and the platform ports
77
+ * trivial). Always returns at least one span; empty input → empty array.
78
+ */
79
+ export declare function parseInlineSpans(text: string): InlineSpan[];
80
+ //# sourceMappingURL=timeline-markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-markdown.d.ts","sourceRoot":"","sources":["../src/timeline-markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC;AAE5D,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAgFlE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CA8E3D"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Lightweight markdown parser for timeline detail panes.
3
+ *
4
+ * Output is a flat array of typed lines that each platform renders natively
5
+ * (SwiftUI / Compose). Pure data — no rendering. Mirrors the Apple parser at
6
+ * apple/AgentDeck/UI/Monitor/TimelineStripView.swift `TimelineMarkdownLine`
7
+ * and the Android port at
8
+ * android/.../ui/timeline/TimelineMarkdown.kt — keep the three in lockstep.
9
+ *
10
+ * Block grammar (line-oriented):
11
+ * - ``` toggles a code fence; lines inside become `code` (verbatim, no inline parsing)
12
+ * - empty / whitespace-only line → `blank`
13
+ * - 1-6 leading hashes + space → `heading` (level 1-6; CommonMark ATX)
14
+ * - `- ` or `* ` → `bullet`
15
+ * - `<digits>.` or `<digits>)` followed by space → `numbered`
16
+ * - `> ` → `quote`
17
+ * - `|...|` (with optional `|---|` separator) → `table` block
18
+ * - anything else → `text`
19
+ *
20
+ * Inline grammar (per `parseInlineSpans`, applied at render time to non-code
21
+ * line content + table cells; first-match-wins, no recursion):
22
+ * - `` `code` `` → `code` span
23
+ * - `**bold**` → `bold` span
24
+ * - `*italic*` → `italic` span (only when not adjacent to another `*`)
25
+ * - `[text](href)` → `link` span
26
+ * - everything else → `plain` span
27
+ */
28
+ export function parseTimelineMarkdown(text) {
29
+ if (!text)
30
+ return [];
31
+ const out = [];
32
+ let inCodeFence = false;
33
+ const lines = text.split(/\r?\n/);
34
+ let i = 0;
35
+ while (i < lines.length) {
36
+ const rawLine = lines[i];
37
+ const trimmed = rawLine.trim();
38
+ if (trimmed.startsWith('```')) {
39
+ inCodeFence = !inCodeFence;
40
+ i += 1;
41
+ continue;
42
+ }
43
+ if (inCodeFence) {
44
+ out.push({ kind: 'code', content: rawLine });
45
+ i += 1;
46
+ continue;
47
+ }
48
+ if (trimmed.length === 0) {
49
+ out.push({ kind: 'blank' });
50
+ i += 1;
51
+ continue;
52
+ }
53
+ // Table block: current line looks like `|...|`. Optional separator on
54
+ // next line marks the previous row as header. Continue collecting rows
55
+ // while subsequent lines are also table-rows.
56
+ if (isTableRow(trimmed)) {
57
+ const rows = [splitCells(trimmed)];
58
+ let hasHeader = false;
59
+ let j = i + 1;
60
+ if (j < lines.length && isTableSeparator(lines[j].trim())) {
61
+ hasHeader = true;
62
+ j += 1;
63
+ }
64
+ while (j < lines.length) {
65
+ const nextTrimmed = lines[j].trim();
66
+ if (!isTableRow(nextTrimmed) || isTableSeparator(nextTrimmed))
67
+ break;
68
+ rows.push(splitCells(nextTrimmed));
69
+ j += 1;
70
+ }
71
+ out.push({ kind: 'table', rows, hasHeader });
72
+ i = j;
73
+ continue;
74
+ }
75
+ const heading = parseHeading(trimmed);
76
+ if (heading) {
77
+ out.push({ kind: 'heading', level: heading.level, content: heading.content });
78
+ i += 1;
79
+ continue;
80
+ }
81
+ if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
82
+ out.push({ kind: 'bullet', content: trimmed.slice(2) });
83
+ i += 1;
84
+ continue;
85
+ }
86
+ const numbered = parseNumbered(trimmed);
87
+ if (numbered) {
88
+ out.push({ kind: 'numbered', marker: numbered.marker, content: numbered.content });
89
+ i += 1;
90
+ continue;
91
+ }
92
+ if (trimmed.startsWith('> ')) {
93
+ out.push({ kind: 'quote', content: trimmed.slice(2) });
94
+ i += 1;
95
+ continue;
96
+ }
97
+ out.push({ kind: 'text', content: rawLine });
98
+ i += 1;
99
+ }
100
+ return out.length === 0 ? [{ kind: 'text', content: text }] : out;
101
+ }
102
+ /**
103
+ * Tokenize a single line of inline content into spans. First-match-wins
104
+ * left-to-right walker; never recurses (so e.g. `**bold *with italic***` is
105
+ * just bold + plain — keeps the implementation simple and the platform ports
106
+ * trivial). Always returns at least one span; empty input → empty array.
107
+ */
108
+ export function parseInlineSpans(text) {
109
+ if (!text)
110
+ return [];
111
+ const out = [];
112
+ let pending = ''; // buffered plain text waiting to flush
113
+ const flushPlain = () => {
114
+ if (pending.length > 0) {
115
+ out.push({ kind: 'plain', text: pending });
116
+ pending = '';
117
+ }
118
+ };
119
+ let i = 0;
120
+ while (i < text.length) {
121
+ const ch = text[i];
122
+ // `code`
123
+ if (ch === '`') {
124
+ const close = text.indexOf('`', i + 1);
125
+ if (close > i) {
126
+ flushPlain();
127
+ out.push({ kind: 'code', text: text.slice(i + 1, close) });
128
+ i = close + 1;
129
+ continue;
130
+ }
131
+ }
132
+ // **bold**
133
+ if (ch === '*' && text[i + 1] === '*') {
134
+ const close = text.indexOf('**', i + 2);
135
+ if (close > i + 1) {
136
+ flushPlain();
137
+ out.push({ kind: 'bold', text: text.slice(i + 2, close) });
138
+ i = close + 2;
139
+ continue;
140
+ }
141
+ }
142
+ // *italic* — single `*`, not surrounded by another `*`
143
+ if (ch === '*' &&
144
+ text[i + 1] !== '*' &&
145
+ (i === 0 || text[i - 1] !== '*')) {
146
+ // Find a closing single `*` that isn't doubled.
147
+ let j = i + 1;
148
+ while (j < text.length) {
149
+ if (text[j] === '*' && text[j + 1] !== '*' && text[j - 1] !== '*')
150
+ break;
151
+ j += 1;
152
+ }
153
+ if (j < text.length && j > i + 1) {
154
+ flushPlain();
155
+ out.push({ kind: 'italic', text: text.slice(i + 1, j) });
156
+ i = j + 1;
157
+ continue;
158
+ }
159
+ }
160
+ // [text](href)
161
+ if (ch === '[') {
162
+ const closeBracket = text.indexOf(']', i + 1);
163
+ if (closeBracket > i && text[closeBracket + 1] === '(') {
164
+ const closeParen = text.indexOf(')', closeBracket + 2);
165
+ if (closeParen > closeBracket + 1) {
166
+ const linkText = text.slice(i + 1, closeBracket);
167
+ const href = text.slice(closeBracket + 2, closeParen);
168
+ flushPlain();
169
+ out.push({ kind: 'link', text: linkText, href });
170
+ i = closeParen + 1;
171
+ continue;
172
+ }
173
+ }
174
+ }
175
+ pending += ch;
176
+ i += 1;
177
+ }
178
+ flushPlain();
179
+ return out.length === 0 ? [{ kind: 'plain', text }] : out;
180
+ }
181
+ function parseHeading(trimmed) {
182
+ let level = 0;
183
+ for (const ch of trimmed) {
184
+ if (ch === '#')
185
+ level += 1;
186
+ else
187
+ break;
188
+ }
189
+ if (level < 1 || level > 6)
190
+ return null;
191
+ if (trimmed.charAt(level) !== ' ')
192
+ return null;
193
+ return { level: level, content: trimmed.slice(level + 1) };
194
+ }
195
+ function parseNumbered(trimmed) {
196
+ const m = trimmed.match(/^(\d+)([.)])\s+(.*)$/);
197
+ if (!m)
198
+ return null;
199
+ return { marker: `${m[1]}${m[2]}`, content: m[3] };
200
+ }
201
+ // ---- Table helpers ----
202
+ const TABLE_ROW_RE = /^\s*\|.+\|\s*$/;
203
+ const TABLE_SEPARATOR_RE = /^\s*\|[-:\s|]+\|\s*$/;
204
+ function isTableRow(trimmed) {
205
+ return TABLE_ROW_RE.test(trimmed) && !isTableSeparator(trimmed);
206
+ }
207
+ function isTableSeparator(trimmed) {
208
+ if (!TABLE_SEPARATOR_RE.test(trimmed))
209
+ return false;
210
+ // Must contain at least one dash to be a real separator (not just `| | |`).
211
+ return /-/.test(trimmed);
212
+ }
213
+ function splitCells(rowLine) {
214
+ return rowLine
215
+ .trim()
216
+ .replace(/^\|/, '')
217
+ .replace(/\|$/, '')
218
+ .split('|')
219
+ .map((c) => c.trim());
220
+ }
221
+ //# sourceMappingURL=timeline-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-markdown.js","sourceRoot":"","sources":["../src/timeline-markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared timeline summarizer utilities — extractTopicHint and cleanLLMOutput.
3
+ * Used by both bridge/src/timeline-summarizer.ts and plugin/src/timeline-summarizer.ts.
4
+ */
5
+ export declare const SUMMARY_SYSTEM_PROMPT = "You are a timeline summarizer. Given an AI assistant's response text, produce a single-line Korean summary (max 80 characters) of what was accomplished. Focus on the result, not the process. No quotes, no markdown, no punctuation at the end. Output ONLY the summary line, nothing else. Examples:\n- \uBD07\uB9C8\uB2F9 \uC778\uBC15\uC2A4\uC5D0 AI \uC5D0\uC774\uC804\uD2B8 \uC6B4\uC601 \uD301 3\uAC74 \uC218\uC9D1\n- \uC8FC\uAC04 \uBAA8\uB378 \uC0AC\uC6A9\uB7C9 \uB9AC\uD3EC\uD2B8: glm-5 28\uD68C \uCD5C\uB2E4\n- GitHub \uC774\uC288 #10428 \uAC80\uD1A0 \uC644\uB8CC, PR #16831 \uB300\uAE30 \uC911\n- HBF \uB274\uC2A4 9\uC6D4~2\uC6D4\uBD84 \uD55C\uAE00 \uC815\uB9AC \uC644\uB8CC";
6
+ /**
7
+ * Extract a topic hint from the first few tokens of a response.
8
+ * Used for immediate chat_start enrichment before LLM summarization.
9
+ *
10
+ * Korean handling: politeness-only responses ("네, 확인했습니다.") used to be
11
+ * stripped to empty and return null, leaving the timeline showing bare
12
+ * "Completed". Now the strip falls back to the pre-strip candidate so the
13
+ * row shows something meaningful.
14
+ */
15
+ export declare function extractTopicHint(text: string): string | null;
16
+ export type TopicHintKind = 'topic' | 'fallback' | null;
17
+ /**
18
+ * Same as `extractTopicHint` but tells the caller whether the result was a
19
+ * substantive topic (kind='topic') or just a politeness-fallback /
20
+ * first-sentence fallback (kind='fallback'). Lets emitters set
21
+ * `summaryKind: 'heuristic'` only when there's real content, vs `'none'`
22
+ * when we're scraping the bottom of the barrel.
23
+ */
24
+ export declare function extractTopicHintWithKind(text: string): {
25
+ hint: string | null;
26
+ kind: TopicHintKind;
27
+ };
28
+ /**
29
+ * Last-resort fallback when the response yielded no usable hint and the
30
+ * caller has the prompt text — produce a snippet of the prompt instead of
31
+ * literal "Completed". Returns the first sentence (up to `maxLen` chars,
32
+ * ending at `.` `!` `?` or newline) or null when prompt is too short.
33
+ */
34
+ export declare function promptSnippetFallback(prompt: string | null | undefined, maxLen?: number): string | null;
35
+ /** Clean LLM output — strip thinking, quotes, markdown artifacts */
36
+ export declare function cleanLLMOutput(content: string): string | null;
37
+ //# sourceMappingURL=timeline-summarizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-summarizer.d.ts","sourceRoot":"","sources":["../src/timeline-summarizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,qBAAqB,wqBAIT,CAAC;AAE1B;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE5D;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAiD9C;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,SAAK,GAAG,MAAM,GAAG,IAAI,CAWnG;AAED,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4B7D"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Shared timeline summarizer utilities — extractTopicHint and cleanLLMOutput.
3
+ * Used by both bridge/src/timeline-summarizer.ts and plugin/src/timeline-summarizer.ts.
4
+ */
5
+ import { cleanRawText } from './timeline.js';
6
+ export const SUMMARY_SYSTEM_PROMPT = `You are a timeline summarizer. Given an AI assistant's response text, produce a single-line Korean summary (max 80 characters) of what was accomplished. Focus on the result, not the process. No quotes, no markdown, no punctuation at the end. Output ONLY the summary line, nothing else. Examples:
7
+ - 봇마당 인박스에 AI 에이전트 운영 팁 3건 수집
8
+ - 주간 모델 사용량 리포트: glm-5 28회 최다
9
+ - GitHub 이슈 #10428 검토 완료, PR #16831 대기 중
10
+ - HBF 뉴스 9월~2월분 한글 정리 완료`;
11
+ /**
12
+ * Extract a topic hint from the first few tokens of a response.
13
+ * Used for immediate chat_start enrichment before LLM summarization.
14
+ *
15
+ * Korean handling: politeness-only responses ("네, 확인했습니다.") used to be
16
+ * stripped to empty and return null, leaving the timeline showing bare
17
+ * "Completed". Now the strip falls back to the pre-strip candidate so the
18
+ * row shows something meaningful.
19
+ */
20
+ export function extractTopicHint(text) {
21
+ return extractTopicHintWithKind(text).hint;
22
+ }
23
+ /**
24
+ * Same as `extractTopicHint` but tells the caller whether the result was a
25
+ * substantive topic (kind='topic') or just a politeness-fallback /
26
+ * first-sentence fallback (kind='fallback'). Lets emitters set
27
+ * `summaryKind: 'heuristic'` only when there's real content, vs `'none'`
28
+ * when we're scraping the bottom of the barrel.
29
+ */
30
+ export function extractTopicHintWithKind(text) {
31
+ if (!text || text.length < 5)
32
+ return { hint: null, kind: null };
33
+ const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
34
+ // Find first non-markdown, non-code-fence, non-empty line
35
+ let candidate = null;
36
+ let inCodeFence = false;
37
+ for (const line of lines) {
38
+ if (/^```/.test(line)) {
39
+ inCodeFence = !inCodeFence;
40
+ continue;
41
+ }
42
+ if (inCodeFence)
43
+ continue;
44
+ if (/^#{1,6}\s*$/.test(line))
45
+ continue;
46
+ const stripped = cleanRawText(line)
47
+ .replace(/^[-*]\s+/, '')
48
+ .replace(/^>\s+/, '')
49
+ .trim();
50
+ if (stripped.length >= 3) {
51
+ candidate = stripped;
52
+ break;
53
+ }
54
+ }
55
+ if (!candidate)
56
+ return { hint: null, kind: null };
57
+ const snippet = candidate.length > 80 ? candidate.slice(0, 77) + '...' : candidate;
58
+ // Try stripping leading Korean polite closers — "네, …" / "확인했습니다."
59
+ let cleaned = snippet
60
+ .replace(/^네[,.]?\s*/i, '')
61
+ .replace(/^(완료했습니다\.\s*|알겠습니다\.\s*|확인했습니다\.\s*)/i, '')
62
+ .trim();
63
+ if (cleaned.length >= 3) {
64
+ return { hint: cleaned, kind: 'topic' };
65
+ }
66
+ // Strip left only the EXTRA content beside the polite closer. If polite
67
+ // closer was the entire candidate, fall back to the candidate itself —
68
+ // "네, 확인했습니다." beats showing just "Completed".
69
+ if (snippet.length >= 3) {
70
+ return { hint: snippet, kind: 'fallback' };
71
+ }
72
+ return { hint: null, kind: null };
73
+ }
74
+ /**
75
+ * Last-resort fallback when the response yielded no usable hint and the
76
+ * caller has the prompt text — produce a snippet of the prompt instead of
77
+ * literal "Completed". Returns the first sentence (up to `maxLen` chars,
78
+ * ending at `.` `!` `?` or newline) or null when prompt is too short.
79
+ */
80
+ export function promptSnippetFallback(prompt, maxLen = 60) {
81
+ if (!prompt)
82
+ return null;
83
+ const trimmed = prompt.trim();
84
+ if (trimmed.length < 3)
85
+ return null;
86
+ // Cut at the first sentence terminator, otherwise at a hard line break.
87
+ const stop = trimmed.search(/[.!?。、]|\n/);
88
+ let snippet = stop > 0 ? trimmed.slice(0, stop) : trimmed;
89
+ snippet = snippet.trim();
90
+ if (snippet.length === 0)
91
+ snippet = trimmed;
92
+ if (snippet.length > maxLen)
93
+ snippet = snippet.slice(0, maxLen - 1).trim() + '…';
94
+ return snippet;
95
+ }
96
+ /** Clean LLM output — strip thinking, quotes, markdown artifacts */
97
+ export function cleanLLMOutput(content) {
98
+ let cleaned = content
99
+ // Strip <think>...</think> blocks (Qwen thinking mode)
100
+ .replace(/<think>[\s\S]*?<\/think>/g, '')
101
+ // Strip unclosed <think> at end (truncated output)
102
+ .replace(/<think>[\s\S]*$/g, '')
103
+ .trim();
104
+ // If output has multiple lines, take the last non-empty line
105
+ // (thinking text tends to come first, summary last)
106
+ const lines = cleaned.split('\n').map(l => l.trim()).filter(Boolean);
107
+ if (lines.length > 1) {
108
+ // Find the last line that looks like Korean summary (contains Korean chars)
109
+ const koreanLine = lines.reverse().find(l => /[\uAC00-\uD7AF]/.test(l));
110
+ cleaned = koreanLine || lines[lines.length - 1];
111
+ }
112
+ // Strip surrounding quotes, markdown list markers
113
+ cleaned = cleaned
114
+ .replace(/^[-*]\s*/, '')
115
+ .replace(/^["'`\u201C\u201D\u2018\u2019]+|["'`\u201C\u201D\u2018\u2019]+$/g, '')
116
+ .replace(/[.\u3002]$/, '') // remove trailing period
117
+ .trim();
118
+ if (!cleaned || cleaned.length < 3)
119
+ return null;
120
+ // Truncate to 80 chars if needed
121
+ if (cleaned.length > 80)
122
+ cleaned = cleaned.slice(0, 77) + '...';
123
+ return cleaned;
124
+ }
125
+ //# sourceMappingURL=timeline-summarizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline-summarizer.js","sourceRoot":"","sources":["../src/timeline-summarizer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;yBAIZ,CAAC;AAE1B;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAID;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAY;IAEZ,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAEhE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEpE,0DAA0D;IAC1D,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,WAAW,GAAG,CAAC,WAAW,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,IAAI,WAAW;YAAE,SAAS;QAC1B,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAEvC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC;aAChC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;aACvB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;aACpB,IAAI,EAAE,CAAC;QAEV,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACzB,SAAS,GAAG,QAAQ,CAAC;YACrB,MAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAElD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnF,mEAAmE;IACnE,IAAI,OAAO,GAAG,OAAO;SAClB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,wCAAwC,EAAE,EAAE,CAAC;SACrD,IAAI,EAAE,CAAC;IAEV,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC1C,CAAC;IAED,wEAAwE;IACxE,uEAAuE;IACvE,+CAA+C;IAC/C,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC7C,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAiC,EAAE,MAAM,GAAG,EAAE;IAClF,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,wEAAwE;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1C,IAAI,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1D,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,OAAO,CAAC;IAC5C,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM;QAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC;IACjF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,IAAI,OAAO,GAAG,OAAO;QACnB,uDAAuD;SACtD,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC;QACzC,mDAAmD;SAClD,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,IAAI,EAAE,CAAC;IAEV,6DAA6D;IAC7D,oDAAoD;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,4EAA4E;QAC5E,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,OAAO,GAAG,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,kDAAkD;IAClD,OAAO,GAAG,OAAO;SACd,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,kEAAkE,EAAE,EAAE,CAAC;SAC/E,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,yBAAyB;SACnD,IAAI,EAAE,CAAC;IAEV,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,iCAAiC;IACjC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC;IAChE,OAAO,OAAO,CAAC;AACjB,CAAC"}