@agentdeck/bridge 1.0.20 → 1.0.22

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 (211) hide show
  1. package/dist/adapters/openclaw.d.ts +55 -1
  2. package/dist/adapters/openclaw.d.ts.map +1 -1
  3. package/dist/adapters/openclaw.js +278 -45
  4. package/dist/adapters/openclaw.js.map +1 -1
  5. package/dist/adb-reverse.d.ts +26 -3
  6. package/dist/adb-reverse.d.ts.map +1 -1
  7. package/dist/adb-reverse.js +26 -5
  8. package/dist/adb-reverse.js.map +1 -1
  9. package/dist/apme/adapters/openclaw-hook.d.ts +45 -1
  10. package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -1
  11. package/dist/apme/adapters/openclaw-hook.js +84 -4
  12. package/dist/apme/adapters/openclaw-hook.js.map +1 -1
  13. package/dist/apme/claude-transcript-reader.d.ts +54 -0
  14. package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
  15. package/dist/apme/claude-transcript-reader.js +77 -0
  16. package/dist/apme/claude-transcript-reader.js.map +1 -1
  17. package/dist/apme/collector.d.ts +63 -5
  18. package/dist/apme/collector.d.ts.map +1 -1
  19. package/dist/apme/collector.js +147 -10
  20. package/dist/apme/collector.js.map +1 -1
  21. package/dist/apme/store.d.ts +23 -1
  22. package/dist/apme/store.d.ts.map +1 -1
  23. package/dist/apme/store.js +81 -2
  24. package/dist/apme/store.js.map +1 -1
  25. package/dist/apme/types.d.ts +1 -1
  26. package/dist/apme/types.d.ts.map +1 -1
  27. package/dist/apme/types.js.map +1 -1
  28. package/dist/auth.d.ts +37 -0
  29. package/dist/auth.d.ts.map +1 -1
  30. package/dist/auth.js +23 -0
  31. package/dist/auth.js.map +1 -1
  32. package/dist/ble-sync-spawn.d.ts +27 -0
  33. package/dist/ble-sync-spawn.d.ts.map +1 -1
  34. package/dist/ble-sync-spawn.js +32 -0
  35. package/dist/ble-sync-spawn.js.map +1 -1
  36. package/dist/bridge-core.d.ts +21 -4
  37. package/dist/bridge-core.d.ts.map +1 -1
  38. package/dist/bridge-core.js +52 -23
  39. package/dist/bridge-core.js.map +1 -1
  40. package/dist/claude-interrupt-marker.d.ts +31 -0
  41. package/dist/claude-interrupt-marker.d.ts.map +1 -0
  42. package/dist/claude-interrupt-marker.js +58 -0
  43. package/dist/claude-interrupt-marker.js.map +1 -0
  44. package/dist/claude-turn-watchdog.d.ts +23 -0
  45. package/dist/claude-turn-watchdog.d.ts.map +1 -1
  46. package/dist/claude-turn-watchdog.js +30 -4
  47. package/dist/claude-turn-watchdog.js.map +1 -1
  48. package/dist/cli.d.ts +6 -0
  49. package/dist/cli.d.ts.map +1 -1
  50. package/dist/cli.js +411 -115
  51. package/dist/cli.js.map +1 -1
  52. package/dist/codex-rate-limits-live.d.ts +58 -9
  53. package/dist/codex-rate-limits-live.d.ts.map +1 -1
  54. package/dist/codex-rate-limits-live.js +64 -15
  55. package/dist/codex-rate-limits-live.js.map +1 -1
  56. package/dist/codex-rate-limits.d.ts +2 -2
  57. package/dist/codex-rate-limits.d.ts.map +1 -1
  58. package/dist/codex-rate-limits.js +34 -16
  59. package/dist/codex-rate-limits.js.map +1 -1
  60. package/dist/daemon-port.d.ts +90 -0
  61. package/dist/daemon-port.d.ts.map +1 -0
  62. package/dist/daemon-port.js +119 -0
  63. package/dist/daemon-port.js.map +1 -0
  64. package/dist/daemon-server.d.ts +60 -3
  65. package/dist/daemon-server.d.ts.map +1 -1
  66. package/dist/daemon-server.js +362 -109
  67. package/dist/daemon-server.js.map +1 -1
  68. package/dist/daemon-settings.d.ts +46 -0
  69. package/dist/daemon-settings.d.ts.map +1 -0
  70. package/dist/daemon-settings.js +85 -0
  71. package/dist/daemon-settings.js.map +1 -0
  72. package/dist/daemon-takeover.d.ts +75 -0
  73. package/dist/daemon-takeover.d.ts.map +1 -0
  74. package/dist/daemon-takeover.js +137 -0
  75. package/dist/daemon-takeover.js.map +1 -0
  76. package/dist/daemon.js +1 -1
  77. package/dist/hook-server.d.ts.map +1 -1
  78. package/dist/hook-server.js +17 -0
  79. package/dist/hook-server.js.map +1 -1
  80. package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
  81. package/dist/idotmatrix/idotmatrix-daemon-sync.js +13 -1
  82. package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +34 -12
  86. package/dist/index.js.map +1 -1
  87. package/dist/kiro-diagnostics.d.ts +90 -0
  88. package/dist/kiro-diagnostics.d.ts.map +1 -0
  89. package/dist/kiro-diagnostics.js +278 -0
  90. package/dist/kiro-diagnostics.js.map +1 -0
  91. package/dist/kiro-session.d.ts +113 -0
  92. package/dist/kiro-session.d.ts.map +1 -0
  93. package/dist/kiro-session.js +688 -0
  94. package/dist/kiro-session.js.map +1 -0
  95. package/dist/kiro-timeline-feed.d.ts +45 -0
  96. package/dist/kiro-timeline-feed.d.ts.map +1 -0
  97. package/dist/kiro-timeline-feed.js +116 -0
  98. package/dist/kiro-timeline-feed.js.map +1 -0
  99. package/dist/kiro-transcript-timeline.d.ts +57 -0
  100. package/dist/kiro-transcript-timeline.d.ts.map +1 -0
  101. package/dist/kiro-transcript-timeline.js +183 -0
  102. package/dist/kiro-transcript-timeline.js.map +1 -0
  103. package/dist/linux-service.d.ts +2 -1
  104. package/dist/linux-service.d.ts.map +1 -1
  105. package/dist/linux-service.js +8 -3
  106. package/dist/linux-service.js.map +1 -1
  107. package/dist/mdns-discover.d.ts +27 -0
  108. package/dist/mdns-discover.d.ts.map +1 -1
  109. package/dist/mdns-discover.js +60 -7
  110. package/dist/mdns-discover.js.map +1 -1
  111. package/dist/mdns.d.ts.map +1 -1
  112. package/dist/mdns.js +16 -3
  113. package/dist/mdns.js.map +1 -1
  114. package/dist/modules/adb-module.d.ts.map +1 -1
  115. package/dist/modules/adb-module.js +5 -2
  116. package/dist/modules/adb-module.js.map +1 -1
  117. package/dist/modules/index.d.ts +2 -1
  118. package/dist/modules/index.d.ts.map +1 -1
  119. package/dist/modules/index.js +1 -0
  120. package/dist/modules/index.js.map +1 -1
  121. package/dist/modules/types.d.ts +34 -9
  122. package/dist/modules/types.d.ts.map +1 -1
  123. package/dist/modules/types.js +34 -1
  124. package/dist/modules/types.js.map +1 -1
  125. package/dist/network-posture.d.ts +67 -0
  126. package/dist/network-posture.d.ts.map +1 -0
  127. package/dist/network-posture.js +100 -0
  128. package/dist/network-posture.js.map +1 -0
  129. package/dist/observed-steering.d.ts +14 -0
  130. package/dist/observed-steering.d.ts.map +1 -1
  131. package/dist/observed-steering.js +18 -0
  132. package/dist/observed-steering.js.map +1 -1
  133. package/dist/observed-turn-watchdogs.d.ts +84 -0
  134. package/dist/observed-turn-watchdogs.d.ts.map +1 -0
  135. package/dist/observed-turn-watchdogs.js +134 -0
  136. package/dist/observed-turn-watchdogs.js.map +1 -0
  137. package/dist/openclaw-session.d.ts +10 -0
  138. package/dist/openclaw-session.d.ts.map +1 -1
  139. package/dist/openclaw-session.js +12 -0
  140. package/dist/openclaw-session.js.map +1 -1
  141. package/dist/passive-observer.d.ts +19 -0
  142. package/dist/passive-observer.d.ts.map +1 -1
  143. package/dist/passive-observer.js +208 -24
  144. package/dist/passive-observer.js.map +1 -1
  145. package/dist/pixoo/micro-glyphs.d.ts +1 -1
  146. package/dist/pixoo/micro-glyphs.d.ts.map +1 -1
  147. package/dist/pixoo/micro-glyphs.js +2 -0
  148. package/dist/pixoo/micro-glyphs.js.map +1 -1
  149. package/dist/pixoo/official-dot-glyphs.generated.d.ts +1 -1
  150. package/dist/pixoo/official-dot-glyphs.generated.d.ts.map +1 -1
  151. package/dist/pixoo/official-dot-glyphs.generated.js +47 -0
  152. package/dist/pixoo/official-dot-glyphs.generated.js.map +1 -1
  153. package/dist/pixoo/pixoo-discover.d.ts +8 -1
  154. package/dist/pixoo/pixoo-discover.d.ts.map +1 -1
  155. package/dist/pixoo/pixoo-discover.js +15 -8
  156. package/dist/pixoo/pixoo-discover.js.map +1 -1
  157. package/dist/pixoo/pixoo-renderer.d.ts +4 -2
  158. package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
  159. package/dist/pixoo/pixoo-renderer.js +105 -36
  160. package/dist/pixoo/pixoo-renderer.js.map +1 -1
  161. package/dist/pixoo/pixoo-settings.d.ts +19 -2
  162. package/dist/pixoo/pixoo-settings.d.ts.map +1 -1
  163. package/dist/pixoo/pixoo-settings.js +22 -4
  164. package/dist/pixoo/pixoo-settings.js.map +1 -1
  165. package/dist/pixoo/pixoo-sprites.d.ts.map +1 -1
  166. package/dist/pixoo/pixoo-sprites.js +2 -0
  167. package/dist/pixoo/pixoo-sprites.js.map +1 -1
  168. package/dist/python-ble-runtime.d.ts +2 -0
  169. package/dist/python-ble-runtime.d.ts.map +1 -1
  170. package/dist/python-ble-runtime.js +101 -7
  171. package/dist/python-ble-runtime.js.map +1 -1
  172. package/dist/session-aggregator.d.ts +9 -1
  173. package/dist/session-aggregator.d.ts.map +1 -1
  174. package/dist/session-aggregator.js +1 -1
  175. package/dist/session-aggregator.js.map +1 -1
  176. package/dist/session-registry.d.ts +71 -3
  177. package/dist/session-registry.d.ts.map +1 -1
  178. package/dist/session-registry.js +126 -19
  179. package/dist/session-registry.js.map +1 -1
  180. package/dist/subagent-timeline.d.ts +38 -7
  181. package/dist/subagent-timeline.d.ts.map +1 -1
  182. package/dist/subagent-timeline.js +154 -23
  183. package/dist/subagent-timeline.js.map +1 -1
  184. package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
  185. package/dist/timebox/timebox-daemon-sync.js +11 -1
  186. package/dist/timebox/timebox-daemon-sync.js.map +1 -1
  187. package/dist/timeline-store.d.ts.map +1 -1
  188. package/dist/timeline-store.js +26 -1
  189. package/dist/timeline-store.js.map +1 -1
  190. package/dist/tui/renderer.d.ts.map +1 -1
  191. package/dist/tui/renderer.js +13 -1
  192. package/dist/tui/renderer.js.map +1 -1
  193. package/dist/tui/terrarium.d.ts.map +1 -1
  194. package/dist/tui/terrarium.js +10 -5
  195. package/dist/tui/terrarium.js.map +1 -1
  196. package/dist/usage-api.d.ts +28 -1
  197. package/dist/usage-api.d.ts.map +1 -1
  198. package/dist/usage-api.js +74 -33
  199. package/dist/usage-api.js.map +1 -1
  200. package/dist/usage-event.d.ts.map +1 -1
  201. package/dist/usage-event.js +2 -17
  202. package/dist/usage-event.js.map +1 -1
  203. package/dist/utils/redact-secrets.d.ts +3 -0
  204. package/dist/utils/redact-secrets.d.ts.map +1 -0
  205. package/dist/utils/redact-secrets.js +29 -0
  206. package/dist/utils/redact-secrets.js.map +1 -0
  207. package/dist/windows-service.d.ts +2 -1
  208. package/dist/windows-service.d.ts.map +1 -1
  209. package/dist/windows-service.js +6 -3
  210. package/dist/windows-service.js.map +1 -1
  211. package/package.json +3 -3
@@ -0,0 +1,688 @@
1
+ import { open, readdir, readFile, stat } from 'node:fs/promises';
2
+ import { createRequire } from 'node:module';
3
+ import { homedir } from 'node:os';
4
+ import { basename, join } from 'node:path';
5
+ import { stripUnsafeText } from '@agentdeck/shared';
6
+ import { redactSecrets } from './utils/redact-secrets.js';
7
+ const MAX_SESSION_FILES = 200;
8
+ const MAX_TRANSCRIPT_SAMPLE_BYTES = 1024 * 1024;
9
+ const KIRO_PENDING_TURN_MAX_AGE_MS = 10 * 60 * 1000;
10
+ const require = createRequire(import.meta.url);
11
+ /** Reuses parsed snapshots across the passive observer's five-second polls. */
12
+ export class KiroSessionCache {
13
+ entries = new Map();
14
+ get(path, signature) {
15
+ const entry = this.entries.get(path);
16
+ return entry &&
17
+ entry.transcriptMtimeMs === signature.transcriptMtimeMs &&
18
+ entry.transcriptSize === signature.transcriptSize &&
19
+ entry.metadataMtimeMs === signature.metadataMtimeMs &&
20
+ entry.metadataSize === signature.metadataSize
21
+ ? entry.snapshot
22
+ : undefined;
23
+ }
24
+ set(path, entry) {
25
+ this.entries.set(path, entry);
26
+ }
27
+ retain(paths) {
28
+ for (const path of this.entries.keys()) {
29
+ if (!paths.has(path))
30
+ this.entries.delete(path);
31
+ }
32
+ }
33
+ get size() {
34
+ return this.entries.size;
35
+ }
36
+ }
37
+ /** Kiro's relocatable config root. `KIRO_HOME` names the root itself. */
38
+ export function kiroConfigRoot() {
39
+ const configured = process.env.KIRO_HOME?.trim();
40
+ return configured || join(homedir(), '.kiro');
41
+ }
42
+ export function kiroSessionsDir() {
43
+ return join(kiroConfigRoot(), 'sessions', 'cli');
44
+ }
45
+ export function kiroV3SessionsRoot() {
46
+ return join(kiroConfigRoot(), 'sessions');
47
+ }
48
+ /** Measured Kiro CLI 2.18.1 v2 desktop store. */
49
+ export function kiroDataDbPath() {
50
+ if (process.platform === 'darwin') {
51
+ return join(homedir(), 'Library', 'Application Support', 'kiro-cli', 'data.sqlite3');
52
+ }
53
+ if (process.platform === 'win32') {
54
+ const localAppData = process.env.LOCALAPPDATA?.trim()
55
+ || join(homedir(), 'AppData', 'Local');
56
+ return join(localAppData, 'kiro-cli', 'data.sqlite3');
57
+ }
58
+ const dataHome = process.env.XDG_DATA_HOME?.trim() || join(homedir(), '.local', 'share');
59
+ return join(dataHome, 'kiro-cli', 'data.sqlite3');
60
+ }
61
+ export function kiroPromptHistoryPath() {
62
+ return join(kiroConfigRoot(), '.cli_bash_history');
63
+ }
64
+ /**
65
+ * Read the stable subset of Kiro's sibling `<session>.json` metadata. Kiro 2.x
66
+ * stores the model under `session_state.rts_model_state.model_info.model_id`;
67
+ * the flatter alternatives keep this reader useful for the incompatible 3.x
68
+ * session format without pretending the undocumented remainder is stable.
69
+ */
70
+ export function parseKiroSessionMetadata(raw, fallbackSessionId) {
71
+ let root;
72
+ try {
73
+ const parsed = JSON.parse(raw);
74
+ if (!isRecord(parsed))
75
+ return { sessionId: fallbackSessionId };
76
+ root = parsed;
77
+ }
78
+ catch {
79
+ return { sessionId: fallbackSessionId };
80
+ }
81
+ const sessionState = objectAt(root, 'session_state');
82
+ const rtsState = sessionState ? objectAt(sessionState, 'rts_model_state') : null;
83
+ const modelInfo = rtsState ? objectAt(rtsState, 'model_info') : null;
84
+ const sessionId = firstString(root, ['session_id', 'sessionId', 'id']) ?? fallbackSessionId;
85
+ const cwd = firstString(root, ['cwd', 'working_directory', 'workspace_root']);
86
+ const title = firstString(root, ['title', 'name', 'summary']);
87
+ const workspacePaths = arrayAt(root, 'workspacePaths') ?? arrayAt(root, 'rootPaths') ?? [];
88
+ const workspacePath = workspacePaths.find((value) => typeof value === 'string' && Boolean(value));
89
+ const modelName = (modelInfo ? firstString(modelInfo, ['model_id', 'modelId', 'name']) : undefined) ??
90
+ firstString(root, ['model', 'model_id', 'modelId']);
91
+ return {
92
+ ...(sessionId ? { sessionId: sessionId.slice(0, 256) } : {}),
93
+ ...((cwd ?? workspacePath) ? { cwd: (cwd ?? workspacePath).slice(0, 4096) } : {}),
94
+ ...(title ? { title: title.trim().slice(0, 160) } : {}),
95
+ ...(modelName ? { modelName: modelName.slice(0, 160) } : {}),
96
+ ...(timestampMs(firstString(root, ['created_at', 'createdAt', 'started_at'])) !== undefined
97
+ ? { createdAt: timestampMs(firstString(root, ['created_at', 'createdAt', 'started_at'])) }
98
+ : {}),
99
+ ...(timestampMs(firstString(root, ['updated_at', 'updatedAt', 'last_activity_at', 'lastModifiedAt'])) !== undefined
100
+ ? { updatedAt: timestampMs(firstString(root, ['updated_at', 'updatedAt', 'last_activity_at', 'lastModifiedAt'])) }
101
+ : {}),
102
+ };
103
+ }
104
+ /**
105
+ * Summarize measured Kiro 2.x records, v3 `payload.type` envelopes, and ACP
106
+ * notifications. Unknown records are ignored: a Kiro
107
+ * upgrade may reduce detail, but must never make daemon observation fail.
108
+ */
109
+ export function parseKiroTranscript(raw) {
110
+ let turnOpen = false;
111
+ let goal;
112
+ let currentTask;
113
+ let response;
114
+ const notePrompt = (text) => {
115
+ const cleaned = cleanKiroGoal(text);
116
+ if (!goal && cleaned)
117
+ goal = cleaned;
118
+ turnOpen = true;
119
+ currentTask = undefined;
120
+ };
121
+ const noteAssistant = (text) => {
122
+ const cleaned = text.replace(/\s+/g, ' ').trim();
123
+ if (cleaned)
124
+ response = cleaned.slice(-1000);
125
+ };
126
+ for (const value of parseJsonl(raw)) {
127
+ const envelope = objectAt(value, 'payload') ?? value;
128
+ const kind = firstString(envelope, ['kind', 'type']);
129
+ const data = objectAt(envelope, 'data') ?? envelope;
130
+ if (kind === 'user') {
131
+ notePrompt(contentText(data));
132
+ continue;
133
+ }
134
+ if (kind === 'turn_start') {
135
+ turnOpen = true;
136
+ continue;
137
+ }
138
+ if (kind === 'assistant') {
139
+ // Kiro writes one `assistant` record per operation, and the LAST one in a
140
+ // v3 turn is routinely `operationType: "Reasoning"` — a chain-of-thought
141
+ // block whose content Kiro has already redacted to a literal "...". Since
142
+ // the newest assistant record wins, taking it unconditionally captured
143
+ // that placeholder as the reply for every v3 session (measured against 6
144
+ // real transcripts: all six came out with response === "...", discarding
145
+ // the `operationType: "Say"` record that held the actual answer).
146
+ //
147
+ // Excluded by operation name, not by matching the "..." text: that string
148
+ // is a display artifact free to change, while the operation is the
149
+ // structural fact. Reasoning content must stay out of the timeline and
150
+ // APME on its own merits anyway.
151
+ if (firstString(envelope, ['operationType']) !== 'Reasoning') {
152
+ noteAssistant(contentText(data));
153
+ }
154
+ turnOpen = true;
155
+ continue;
156
+ }
157
+ if (kind === 'Prompt') {
158
+ notePrompt(contentText(data));
159
+ continue;
160
+ }
161
+ if (kind === 'AssistantMessage') {
162
+ const blocks = arrayAt(data, 'content') ?? [];
163
+ let hasToolUse = false;
164
+ for (const block of blocks) {
165
+ if (!isRecord(block))
166
+ continue;
167
+ const blockKind = firstString(block, ['kind', 'type']);
168
+ if (blockKind === 'text')
169
+ noteAssistant(valueText(block.data) ?? firstString(block, ['text']) ?? '');
170
+ if (blockKind === 'toolUse' || blockKind === 'tool_use') {
171
+ hasToolUse = true;
172
+ currentTask = kiroToolTask(block);
173
+ }
174
+ }
175
+ // A final text-only AssistantMessage closes a 2.x turn. Tool-use
176
+ // messages remain open until a later assistant message completes it.
177
+ turnOpen = hasToolUse;
178
+ continue;
179
+ }
180
+ if (kind === 'ToolResults' || kind === 'ToolResult') {
181
+ turnOpen = true;
182
+ continue;
183
+ }
184
+ if (kind === 'TurnEnd' || kind === 'turn_end') {
185
+ turnOpen = false;
186
+ currentTask = undefined;
187
+ continue;
188
+ }
189
+ const method = firstString(value, ['method']);
190
+ const params = objectAt(value, 'params');
191
+ if (method === 'session/prompt' && params) {
192
+ notePrompt(contentText(params));
193
+ continue;
194
+ }
195
+ if (method !== 'session/notification' || !params)
196
+ continue;
197
+ const update = objectAt(params, 'update') ?? params;
198
+ const updateType = normalizeUpdateType(firstString(update, ['sessionUpdate', 'session_update', 'type', 'kind']) ?? '');
199
+ if (updateType === 'agentmessagechunk') {
200
+ noteAssistant(contentText(update));
201
+ turnOpen = true;
202
+ }
203
+ else if (updateType === 'toolcall') {
204
+ turnOpen = true;
205
+ currentTask = kiroToolTask(update);
206
+ }
207
+ else if (updateType === 'toolcallupdate') {
208
+ turnOpen = true;
209
+ currentTask = kiroToolTask(update) ?? currentTask;
210
+ }
211
+ else if (updateType === 'turnend') {
212
+ turnOpen = false;
213
+ currentTask = undefined;
214
+ }
215
+ }
216
+ return {
217
+ state: turnOpen ? 'processing' : 'idle',
218
+ ...(goal ? { goal } : {}),
219
+ ...(currentTask ? { currentTask } : {}),
220
+ ...(response ? { response } : {}),
221
+ };
222
+ }
223
+ /**
224
+ * Return schema markers only. Safe for diagnostics: values, prompt text,
225
+ * tool input, response text, and message ids never leave this function.
226
+ */
227
+ /** Non-blank lines in a transcript sample — the evidence that something WAS
228
+ * written, independent of whether this build could interpret it. */
229
+ export function kiroTranscriptLineCount(raw) {
230
+ let count = 0;
231
+ for (const line of raw.split('\n')) {
232
+ if (line.trim())
233
+ count++;
234
+ }
235
+ return count;
236
+ }
237
+ export function kiroTranscriptRecordKinds(raw) {
238
+ const kinds = new Set();
239
+ for (const value of parseJsonl(raw)) {
240
+ const envelope = objectAt(value, 'payload') ?? value;
241
+ const kind = firstString(envelope, ['kind', 'type']);
242
+ if (kind)
243
+ kinds.add(safeKiroRecordKind(kind));
244
+ const method = firstString(value, ['method']);
245
+ if (method)
246
+ kinds.add(safeKiroMethod(method));
247
+ if (method === 'session/notification') {
248
+ const params = objectAt(value, 'params');
249
+ const update = params ? (objectAt(params, 'update') ?? params) : null;
250
+ const updateType = update ? firstString(update, ['sessionUpdate', 'session_update', 'type', 'kind']) : undefined;
251
+ if (updateType)
252
+ kinds.add(`session/notification:${safeKiroUpdateType(updateType)}`);
253
+ }
254
+ }
255
+ return [...kinds].sort();
256
+ }
257
+ function safeKiroRecordKind(value) {
258
+ return [
259
+ 'Prompt', 'AssistantMessage', 'ToolResults', 'ToolResult', 'TurnEnd',
260
+ 'user', 'assistant', 'turn_start', 'turn_end', 'session_start',
261
+ 'session_event', 'session_metadata', 'usage_summary', 'ContextualHookInvoked',
262
+ ].includes(value)
263
+ ? value
264
+ : 'other';
265
+ }
266
+ function safeKiroMethod(value) {
267
+ return ['session/prompt', 'session/notification'].includes(value) ? value : 'other';
268
+ }
269
+ function safeKiroUpdateType(value) {
270
+ const normalized = normalizeUpdateType(value);
271
+ const known = {
272
+ agentmessagechunk: 'AgentMessageChunk',
273
+ toolcall: 'ToolCall',
274
+ toolcallupdate: 'ToolCallUpdate',
275
+ turnend: 'TurnEnd',
276
+ };
277
+ return known[normalized] ?? 'other';
278
+ }
279
+ function cleanKiroGoal(raw) {
280
+ const value = stripUnsafeText(raw)
281
+ .replace(/<[^>]+>/g, ' ')
282
+ .replace(/\s+/g, ' ')
283
+ .trim();
284
+ return /^\/[a-z][\w-]*\s*$/i.test(value) ? '' : value.slice(0, 120);
285
+ }
286
+ /** Read the newest bounded set of Kiro CLI session pairs. */
287
+ export async function readKiroSessionSnapshots(sessionsDir = kiroSessionsDir(), maxSessions = MAX_SESSION_FILES, cache) {
288
+ let names;
289
+ try {
290
+ const entries = await readdir(sessionsDir, { withFileTypes: true });
291
+ names = entries
292
+ .filter((entry) => entry.isFile() && !entry.isSymbolicLink() && entry.name.endsWith('.jsonl'))
293
+ .map((entry) => entry.name)
294
+ .slice(0, 2000);
295
+ }
296
+ catch {
297
+ return [];
298
+ }
299
+ const files = [];
300
+ for (const name of names) {
301
+ try {
302
+ const info = await stat(join(sessionsDir, name));
303
+ if (info.isFile())
304
+ files.push({ name, mtimeMs: info.mtimeMs, size: info.size });
305
+ }
306
+ catch {
307
+ /* file disappeared during the scan */
308
+ }
309
+ }
310
+ files.sort((a, b) => b.mtimeMs - a.mtimeMs);
311
+ const snapshots = [];
312
+ const retainedPaths = new Set();
313
+ for (const file of files.slice(0, Math.max(0, maxSessions))) {
314
+ const sessionId = basename(file.name, '.jsonl');
315
+ if (!/^[A-Za-z0-9_-]{1,256}$/.test(sessionId))
316
+ continue;
317
+ const transcriptPath = join(sessionsDir, file.name);
318
+ const metadataPath = join(sessionsDir, `${sessionId}.json`);
319
+ const metadataInfo = await stat(metadataPath).catch(() => undefined);
320
+ const signature = {
321
+ transcriptMtimeMs: file.mtimeMs,
322
+ transcriptSize: file.size,
323
+ metadataMtimeMs: metadataInfo?.mtimeMs ?? 0,
324
+ metadataSize: metadataInfo?.size ?? 0,
325
+ };
326
+ retainedPaths.add(transcriptPath);
327
+ const cached = cache?.get(transcriptPath, signature);
328
+ if (cached) {
329
+ snapshots.push(cached);
330
+ continue;
331
+ }
332
+ const [metadataRaw, transcriptRaw] = await Promise.all([
333
+ readFile(metadataPath, 'utf8').catch(() => ''),
334
+ readHeadAndTail(transcriptPath, 64 * 1024, MAX_TRANSCRIPT_SAMPLE_BYTES).catch(() => ''),
335
+ ]);
336
+ const metadata = parseKiroSessionMetadata(metadataRaw, sessionId);
337
+ const summary = parseKiroTranscript(transcriptRaw);
338
+ const snapshot = {
339
+ ...metadata,
340
+ ...summary,
341
+ sessionId,
342
+ transcriptPath,
343
+ lastActivityAt: metadata.updatedAt ?? file.mtimeMs,
344
+ goal: summary.goal ?? metadata.title,
345
+ recordKinds: kiroTranscriptRecordKinds(transcriptRaw),
346
+ transcriptLineCount: kiroTranscriptLineCount(transcriptRaw),
347
+ };
348
+ snapshots.push(snapshot);
349
+ cache?.set(transcriptPath, { ...signature, snapshot });
350
+ }
351
+ cache?.retain(retainedPaths);
352
+ return snapshots;
353
+ }
354
+ /** Read Kiro CLI 3.0's measured `<workspace>/<session>/messages.jsonl` store. */
355
+ export async function readKiroV3SessionSnapshots(sessionsRoot = kiroV3SessionsRoot(), maxSessions = MAX_SESSION_FILES, cache) {
356
+ const candidates = [];
357
+ try {
358
+ const workspaces = await readdir(sessionsRoot, { withFileTypes: true });
359
+ for (const workspace of workspaces.slice(0, 2000)) {
360
+ if (!workspace.isDirectory() || workspace.isSymbolicLink() || workspace.name === 'cli')
361
+ continue;
362
+ const workspacePath = join(sessionsRoot, workspace.name);
363
+ const sessions = await readdir(workspacePath, { withFileTypes: true }).catch(() => []);
364
+ for (const session of sessions.slice(0, 2000)) {
365
+ if (!session.isDirectory() || session.isSymbolicLink() || !/^[A-Za-z0-9_-]{1,256}$/.test(session.name))
366
+ continue;
367
+ const transcriptPath = join(workspacePath, session.name, 'messages.jsonl');
368
+ const metadataPath = join(workspacePath, session.name, 'session.json');
369
+ const [transcriptInfo, metadataInfo] = await Promise.all([
370
+ stat(transcriptPath).catch(() => undefined),
371
+ stat(metadataPath).catch(() => undefined),
372
+ ]);
373
+ if (!transcriptInfo?.isFile() || !metadataInfo?.isFile())
374
+ continue;
375
+ candidates.push({
376
+ sessionId: session.name,
377
+ transcriptPath,
378
+ metadataPath,
379
+ transcriptMtimeMs: transcriptInfo.mtimeMs,
380
+ transcriptSize: transcriptInfo.size,
381
+ metadataMtimeMs: metadataInfo.mtimeMs,
382
+ metadataSize: metadataInfo.size,
383
+ });
384
+ }
385
+ }
386
+ }
387
+ catch {
388
+ return [];
389
+ }
390
+ candidates.sort((a, b) => Math.max(b.transcriptMtimeMs, b.metadataMtimeMs)
391
+ - Math.max(a.transcriptMtimeMs, a.metadataMtimeMs));
392
+ const snapshots = [];
393
+ const retainedPaths = new Set();
394
+ for (const candidate of candidates.slice(0, Math.max(0, maxSessions))) {
395
+ const signature = {
396
+ transcriptMtimeMs: candidate.transcriptMtimeMs,
397
+ transcriptSize: candidate.transcriptSize,
398
+ metadataMtimeMs: candidate.metadataMtimeMs,
399
+ metadataSize: candidate.metadataSize,
400
+ };
401
+ retainedPaths.add(candidate.transcriptPath);
402
+ const cached = cache?.get(candidate.transcriptPath, signature);
403
+ if (cached) {
404
+ snapshots.push(cached);
405
+ continue;
406
+ }
407
+ const [metadataRaw, transcriptRaw] = await Promise.all([
408
+ readFile(candidate.metadataPath, 'utf8').catch(() => ''),
409
+ readHeadAndTail(candidate.transcriptPath, 64 * 1024, MAX_TRANSCRIPT_SAMPLE_BYTES).catch(() => ''),
410
+ ]);
411
+ const metadata = parseKiroSessionMetadata(metadataRaw, candidate.sessionId);
412
+ const summary = parseKiroTranscript(transcriptRaw);
413
+ const snapshot = {
414
+ ...metadata,
415
+ ...summary,
416
+ sessionId: metadata.sessionId ?? candidate.sessionId,
417
+ transcriptPath: candidate.transcriptPath,
418
+ lastActivityAt: metadata.updatedAt
419
+ ?? Math.max(candidate.transcriptMtimeMs, candidate.metadataMtimeMs),
420
+ goal: summary.goal ?? metadata.title,
421
+ recordKinds: kiroTranscriptRecordKinds(transcriptRaw),
422
+ transcriptLineCount: kiroTranscriptLineCount(transcriptRaw),
423
+ };
424
+ snapshots.push(snapshot);
425
+ cache?.set(candidate.transcriptPath, { ...signature, snapshot });
426
+ }
427
+ cache?.retain(retainedPaths);
428
+ return snapshots;
429
+ }
430
+ /**
431
+ * Read every currently-known native Kiro store without changing how Kiro is
432
+ * launched. Kiro CLI 2.18.1 on macOS persists `conversations_v2` in its app
433
+ * SQLite database; v3 writes nested `session.json` + `messages.jsonl` pairs.
434
+ * Legacy flat JSONL remains a fallback. Sources are merged by session id.
435
+ */
436
+ export async function readKiroNativeSessionSnapshots(maxSessions = MAX_SESSION_FILES, cache) {
437
+ const jsonlDir = kiroSessionsDir();
438
+ const v3Root = kiroV3SessionsRoot();
439
+ const dbPath = kiroDataDbPath();
440
+ const [jsonlInfo, v3Info, dbInfo, promptHistoryInfo] = await Promise.all([
441
+ stat(jsonlDir).catch(() => undefined),
442
+ stat(v3Root).catch(() => undefined),
443
+ stat(dbPath).catch(() => undefined),
444
+ stat(kiroPromptHistoryPath()).catch(() => undefined),
445
+ ]);
446
+ const jsonlDirectoryPresent = Boolean(jsonlInfo?.isDirectory());
447
+ const v3DirectoryPresent = Boolean(v3Info?.isDirectory());
448
+ const sqliteDatabasePresent = Boolean(dbInfo?.isFile());
449
+ const jsonl = jsonlDirectoryPresent
450
+ ? await readKiroSessionSnapshots(jsonlDir, maxSessions, v3DirectoryPresent ? undefined : cache)
451
+ : [];
452
+ const v3 = v3DirectoryPresent
453
+ ? await readKiroV3SessionSnapshots(v3Root, maxSessions, cache)
454
+ : [];
455
+ const sqlite = sqliteDatabasePresent
456
+ ? readKiroSqliteSessionSnapshots(dbPath, maxSessions)
457
+ : { snapshots: [], readable: false };
458
+ const byId = new Map();
459
+ const promptHistoryMtimeMs = promptHistoryInfo?.isFile()
460
+ ? promptHistoryInfo.mtimeMs
461
+ : undefined;
462
+ const sqliteSnapshots = applyKiroPendingTurnMarker(sqlite.snapshots, promptHistoryMtimeMs);
463
+ for (const snapshot of [...jsonl, ...sqliteSnapshots, ...v3]) {
464
+ const previous = byId.get(snapshot.sessionId);
465
+ if (!previous || snapshot.lastActivityAt >= previous.lastActivityAt) {
466
+ byId.set(snapshot.sessionId, snapshot);
467
+ }
468
+ }
469
+ let snapshots = [...byId.values()]
470
+ .sort((a, b) => b.lastActivityAt - a.lastActivityAt)
471
+ .slice(0, Math.max(0, maxSessions));
472
+ return {
473
+ snapshots,
474
+ jsonlDirectoryPresent,
475
+ v3DirectoryPresent,
476
+ sqliteDatabasePresent,
477
+ sqliteReadable: sqlite.readable,
478
+ ...(promptHistoryMtimeMs !== undefined ? { promptHistoryMtimeMs } : {}),
479
+ };
480
+ }
481
+ /**
482
+ * Kiro appends the submitted prompt to `.cli_bash_history` immediately, but
483
+ * only commits `conversations_v2` after the response completes. A newer prompt
484
+ * history mtime therefore brackets the current turn without reading its text.
485
+ * The marker is global, so only the newest persisted conversation is promoted;
486
+ * simultaneous Kiro chats remain an explicitly ambiguous best effort.
487
+ */
488
+ export function applyKiroPendingTurnMarker(snapshots, promptHistoryMtimeMs, now = Date.now()) {
489
+ if (snapshots.length === 0
490
+ || promptHistoryMtimeMs === undefined
491
+ || promptHistoryMtimeMs <= snapshots[0].lastActivityAt
492
+ || now - promptHistoryMtimeMs > KIRO_PENDING_TURN_MAX_AGE_MS) {
493
+ return snapshots;
494
+ }
495
+ return snapshots.map((snapshot, index) => index === 0
496
+ ? { ...snapshot, state: 'processing', lastActivityAt: promptHistoryMtimeMs }
497
+ : snapshot);
498
+ }
499
+ function readKiroSqliteSessionSnapshots(dbPath, maxSessions) {
500
+ let db;
501
+ try {
502
+ const Ctor = require('better-sqlite3');
503
+ db = new Ctor(dbPath, { readonly: true, fileMustExist: true });
504
+ db.pragma('query_only = ON');
505
+ const rows = db.prepare(`
506
+ SELECT
507
+ key AS cwd,
508
+ conversation_id AS sessionId,
509
+ created_at AS createdAt,
510
+ updated_at AS updatedAt,
511
+ json_extract(value, '$.model_info.model_id') AS modelName,
512
+ json_extract(value, '$.history[0].user.content.Prompt.prompt') AS goal,
513
+ json_extract(value, '$.history[#-1].assistant.Response.content') AS response,
514
+ json_array_length(json_extract(value, '$.history')) AS historyCount,
515
+ CASE WHEN json_type(value, '$.history[#-1].assistant') = 'object' THEN 1 ELSE 0 END AS hasAssistant
516
+ FROM conversations_v2
517
+ WHERE json_valid(value)
518
+ ORDER BY updated_at DESC
519
+ LIMIT ?
520
+ `).all(Math.max(0, maxSessions));
521
+ return { snapshots: parseKiroSqliteRows(rows, dbPath), readable: true };
522
+ }
523
+ catch {
524
+ return { snapshots: [], readable: false };
525
+ }
526
+ finally {
527
+ try {
528
+ db?.close();
529
+ }
530
+ catch { /* best effort read-only handle */ }
531
+ }
532
+ }
533
+ /** Convert only the allowlisted conversation columns selected above. */
534
+ export function parseKiroSqliteRows(rows, dbPath) {
535
+ const snapshots = [];
536
+ for (const row of rows) {
537
+ if (typeof row.sessionId !== 'string'
538
+ || !/^[A-Za-z0-9_-]{1,256}$/.test(row.sessionId)
539
+ || typeof row.cwd !== 'string'
540
+ || typeof row.createdAt !== 'number'
541
+ || typeof row.updatedAt !== 'number')
542
+ continue;
543
+ const goal = typeof row.goal === 'string' ? cleanKiroGoal(row.goal) : '';
544
+ const response = typeof row.response === 'string'
545
+ ? stripUnsafeText(row.response).replace(/\s+/g, ' ').trim().slice(-1000)
546
+ : '';
547
+ snapshots.push({
548
+ sessionId: row.sessionId,
549
+ transcriptPath: dbPath,
550
+ cwd: row.cwd.slice(0, 4096),
551
+ createdAt: row.createdAt,
552
+ updatedAt: row.updatedAt,
553
+ lastActivityAt: row.updatedAt,
554
+ ...(typeof row.modelName === 'string' ? { modelName: row.modelName.slice(0, 160) } : {}),
555
+ state: 'idle',
556
+ ...(goal ? { goal } : {}),
557
+ ...(response ? { response } : {}),
558
+ // Row-backed, not line-backed: there is no transcript text to count, and
559
+ // 'ConversationV2' is always present, so the empty-kinds branch that uses
560
+ // this is unreachable here.
561
+ transcriptLineCount: 0,
562
+ recordKinds: [
563
+ 'ConversationV2',
564
+ ...(row.historyCount > 0 ? ['Prompt'] : []),
565
+ ...(row.hasAssistant ? ['AssistantMessage'] : []),
566
+ ],
567
+ });
568
+ }
569
+ return snapshots;
570
+ }
571
+ async function readHeadAndTail(path, headBytes, tailBytes) {
572
+ const file = await open(path, 'r');
573
+ try {
574
+ const { size } = await file.stat();
575
+ if (size <= headBytes + tailBytes) {
576
+ const buffer = Buffer.alloc(size);
577
+ await file.read(buffer, 0, size, 0);
578
+ return buffer.toString('utf8');
579
+ }
580
+ const head = Buffer.alloc(headBytes);
581
+ const tail = Buffer.alloc(tailBytes);
582
+ await file.read(head, 0, headBytes, 0);
583
+ await file.read(tail, 0, tailBytes, size - tailBytes);
584
+ const tailText = tail.toString('utf8');
585
+ const firstNewline = tailText.indexOf('\n');
586
+ return `${head.toString('utf8')}\n${firstNewline >= 0 ? tailText.slice(firstNewline + 1) : ''}`;
587
+ }
588
+ finally {
589
+ await file.close();
590
+ }
591
+ }
592
+ function kiroToolTask(record) {
593
+ const raw = isRecord(record.data) ? record.data : record;
594
+ const name = firstString(raw, ['name', 'tool_name', 'toolName', 'title']) ?? 'tool';
595
+ const input = objectAt(raw, 'input') ?? objectAt(raw, 'tool_input') ?? objectAt(raw, 'rawInput');
596
+ if (!input)
597
+ return name;
598
+ // Kiro's `read` puts its subject one level down, in `input.operations[]`
599
+ // (measured: `{name:"read", input:{operations:[{mode:"Directory",
600
+ // path:"/…", depth:1}]}}`), while `execute`-style tools carry `command`
601
+ // directly on `input`. Scanning only the top level yielded a bare "read"
602
+ // where Kiro's own UI shows `Read /Users/…/AgentDeck` — a task label with
603
+ // the subject stripped out, which is the part that makes it a label.
604
+ const operations = Array.isArray(input.operations) ? input.operations : [];
605
+ const scopes = [input, ...operations.filter(isRecord)];
606
+ for (const scope of scopes) {
607
+ for (const key of ['path', 'file_path', 'command', 'cmd', 'query', 'url']) {
608
+ const value = valueText(scope[key]);
609
+ if (value)
610
+ return `${name} ${redactSecrets(value).replace(/\s+/g, ' ').slice(0, 80)}`;
611
+ }
612
+ }
613
+ return name;
614
+ }
615
+ function contentText(record) {
616
+ const direct = firstString(record, ['text', 'message', 'prompt']);
617
+ if (direct)
618
+ return direct;
619
+ const content = record.content;
620
+ if (typeof content === 'string')
621
+ return content;
622
+ if (!Array.isArray(content))
623
+ return '';
624
+ return content
625
+ .map((block) => {
626
+ if (typeof block === 'string')
627
+ return block;
628
+ if (!isRecord(block))
629
+ return '';
630
+ const kind = firstString(block, ['kind', 'type']);
631
+ return kind === 'text' ? (valueText(block.data) ?? firstString(block, ['text']) ?? '') : '';
632
+ })
633
+ .filter(Boolean)
634
+ .join(' ');
635
+ }
636
+ function parseJsonl(raw) {
637
+ const values = [];
638
+ for (const line of raw.split('\n')) {
639
+ const trimmed = line.trim();
640
+ if (!trimmed || trimmed.length > 10 * 1024 * 1024)
641
+ continue;
642
+ try {
643
+ const parsed = JSON.parse(trimmed);
644
+ if (isRecord(parsed))
645
+ values.push(parsed);
646
+ }
647
+ catch {
648
+ /* partial tail line */
649
+ }
650
+ }
651
+ return values;
652
+ }
653
+ function normalizeUpdateType(value) {
654
+ return value.replace(/[^a-z]/gi, '').toLowerCase();
655
+ }
656
+ function timestampMs(value) {
657
+ if (!value)
658
+ return undefined;
659
+ const parsed = Date.parse(value);
660
+ return Number.isFinite(parsed) ? parsed : undefined;
661
+ }
662
+ function firstString(record, keys) {
663
+ for (const key of keys) {
664
+ const value = record[key];
665
+ if (typeof value === 'string' && value)
666
+ return value;
667
+ }
668
+ return undefined;
669
+ }
670
+ function valueText(value) {
671
+ if (typeof value === 'string')
672
+ return value;
673
+ if (typeof value === 'number' || typeof value === 'boolean')
674
+ return String(value);
675
+ return undefined;
676
+ }
677
+ function objectAt(record, key) {
678
+ const value = record[key];
679
+ return isRecord(value) ? value : null;
680
+ }
681
+ function arrayAt(record, key) {
682
+ const value = record[key];
683
+ return Array.isArray(value) ? value : null;
684
+ }
685
+ function isRecord(value) {
686
+ return !!value && typeof value === 'object' && !Array.isArray(value);
687
+ }
688
+ //# sourceMappingURL=kiro-session.js.map