@agentdeck/bridge 1.0.23 → 1.1.0

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 (120) hide show
  1. package/dist/adapters/openclaw.d.ts +74 -4
  2. package/dist/adapters/openclaw.d.ts.map +1 -1
  3. package/dist/adapters/openclaw.js +235 -43
  4. package/dist/adapters/openclaw.js.map +1 -1
  5. package/dist/apme/adapters/openclaw-hook.d.ts +38 -10
  6. package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -1
  7. package/dist/apme/adapters/openclaw-hook.js +181 -37
  8. package/dist/apme/adapters/openclaw-hook.js.map +1 -1
  9. package/dist/apme/collector.d.ts +37 -1
  10. package/dist/apme/collector.d.ts.map +1 -1
  11. package/dist/apme/collector.js +277 -35
  12. package/dist/apme/collector.js.map +1 -1
  13. package/dist/apme/pareto.d.ts +2 -1
  14. package/dist/apme/pareto.d.ts.map +1 -1
  15. package/dist/apme/pareto.js +15 -4
  16. package/dist/apme/pareto.js.map +1 -1
  17. package/dist/apme/recommend.d.ts +24 -1
  18. package/dist/apme/recommend.d.ts.map +1 -1
  19. package/dist/apme/recommend.js +43 -7
  20. package/dist/apme/recommend.js.map +1 -1
  21. package/dist/apme/store.d.ts +6 -0
  22. package/dist/apme/store.d.ts.map +1 -1
  23. package/dist/apme/store.js +244 -72
  24. package/dist/apme/store.js.map +1 -1
  25. package/dist/bridge-core.d.ts +10 -1
  26. package/dist/bridge-core.d.ts.map +1 -1
  27. package/dist/bridge-core.js +20 -5
  28. package/dist/bridge-core.js.map +1 -1
  29. package/dist/card-feed.d.ts +46 -1
  30. package/dist/card-feed.d.ts.map +1 -1
  31. package/dist/card-feed.js +166 -6
  32. package/dist/card-feed.js.map +1 -1
  33. package/dist/cli.d.ts +45 -0
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +388 -27
  36. package/dist/cli.js.map +1 -1
  37. package/dist/daemon-build-identity.d.ts +124 -0
  38. package/dist/daemon-build-identity.d.ts.map +1 -0
  39. package/dist/daemon-build-identity.js +310 -0
  40. package/dist/daemon-build-identity.js.map +1 -0
  41. package/dist/daemon-server.d.ts +34 -6
  42. package/dist/daemon-server.d.ts.map +1 -1
  43. package/dist/daemon-server.js +613 -108
  44. package/dist/daemon-server.js.map +1 -1
  45. package/dist/daemon-takeover.d.ts +11 -0
  46. package/dist/daemon-takeover.d.ts.map +1 -1
  47. package/dist/daemon-takeover.js +62 -17
  48. package/dist/daemon-takeover.js.map +1 -1
  49. package/dist/daemon.js +1 -1
  50. package/dist/daemon.js.map +1 -1
  51. package/dist/esp32-flash.d.ts.map +1 -1
  52. package/dist/esp32-flash.js +19 -3
  53. package/dist/esp32-flash.js.map +1 -1
  54. package/dist/esp32-serial.d.ts +14 -0
  55. package/dist/esp32-serial.d.ts.map +1 -1
  56. package/dist/esp32-serial.js +19 -1
  57. package/dist/esp32-serial.js.map +1 -1
  58. package/dist/glance-frame.d.ts.map +1 -1
  59. package/dist/glance-frame.js +3 -1
  60. package/dist/glance-frame.js.map +1 -1
  61. package/dist/hook-codex-sessions.d.ts +3 -1
  62. package/dist/hook-codex-sessions.d.ts.map +1 -1
  63. package/dist/hook-codex-sessions.js +27 -7
  64. package/dist/hook-codex-sessions.js.map +1 -1
  65. package/dist/legacy-rearm-ledger.d.ts +56 -0
  66. package/dist/legacy-rearm-ledger.d.ts.map +1 -0
  67. package/dist/legacy-rearm-ledger.js +66 -0
  68. package/dist/legacy-rearm-ledger.js.map +1 -0
  69. package/dist/mdns.d.ts +33 -0
  70. package/dist/mdns.d.ts.map +1 -1
  71. package/dist/mdns.js +91 -2
  72. package/dist/mdns.js.map +1 -1
  73. package/dist/openclaw-session.d.ts.map +1 -1
  74. package/dist/openclaw-session.js +13 -0
  75. package/dist/openclaw-session.js.map +1 -1
  76. package/dist/openclaw-timeline-feed.d.ts +84 -0
  77. package/dist/openclaw-timeline-feed.d.ts.map +1 -0
  78. package/dist/openclaw-timeline-feed.js +162 -0
  79. package/dist/openclaw-timeline-feed.js.map +1 -0
  80. package/dist/openclaw-transcript-timeline.d.ts +103 -0
  81. package/dist/openclaw-transcript-timeline.d.ts.map +1 -0
  82. package/dist/openclaw-transcript-timeline.js +328 -0
  83. package/dist/openclaw-transcript-timeline.js.map +1 -0
  84. package/dist/passive-observer.d.ts +21 -1
  85. package/dist/passive-observer.d.ts.map +1 -1
  86. package/dist/passive-observer.js +37 -3
  87. package/dist/passive-observer.js.map +1 -1
  88. package/dist/relayed-usage.d.ts +68 -0
  89. package/dist/relayed-usage.d.ts.map +1 -0
  90. package/dist/relayed-usage.js +80 -0
  91. package/dist/relayed-usage.js.map +1 -0
  92. package/dist/session-aggregator.d.ts +3 -0
  93. package/dist/session-aggregator.d.ts.map +1 -1
  94. package/dist/session-aggregator.js.map +1 -1
  95. package/dist/session-registry.d.ts +1 -0
  96. package/dist/session-registry.d.ts.map +1 -1
  97. package/dist/surface-protocol.d.ts +84 -0
  98. package/dist/surface-protocol.d.ts.map +1 -0
  99. package/dist/surface-protocol.js +235 -0
  100. package/dist/surface-protocol.js.map +1 -0
  101. package/dist/timeline-store.d.ts +1 -1
  102. package/dist/timeline-store.d.ts.map +1 -1
  103. package/dist/timeline-store.js +16 -1
  104. package/dist/timeline-store.js.map +1 -1
  105. package/dist/tui/renderer.d.ts.map +1 -1
  106. package/dist/tui/renderer.js +35 -0
  107. package/dist/tui/renderer.js.map +1 -1
  108. package/dist/weather-config.d.ts +17 -0
  109. package/dist/weather-config.d.ts.map +1 -0
  110. package/dist/weather-config.js +54 -0
  111. package/dist/weather-config.js.map +1 -0
  112. package/dist/weather.d.ts +44 -4
  113. package/dist/weather.d.ts.map +1 -1
  114. package/dist/weather.js +223 -15
  115. package/dist/weather.js.map +1 -1
  116. package/dist/ws-server.d.ts.map +1 -1
  117. package/dist/ws-server.js +55 -5
  118. package/dist/ws-server.js.map +1 -1
  119. package/fm-helper/AgentDeckFMHelper.swift +51 -5
  120. package/package.json +3 -3
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Timeline rows read from OpenClaw's OWN on-disk session store.
3
+ *
4
+ * WHY THIS EXISTS. OpenClaw reaches this daemon through the Gateway WebSocket
5
+ * event stream and nothing else — the adapter's whole RPC surface is `connect`
6
+ * / `sessions.list` / `chat.send` / `chat.abort` / `exec.approval.*`. That
7
+ * stream carries the prompt, the final response, and the approvals it has to
8
+ * block on. It does NOT carry the individual tool calls, so the timeline showed
9
+ * "OpenClaw said something" with no account of what it actually did, and the
10
+ * only tools that ever appeared were the ones that happened to need approval —
11
+ * a biased sample, not a log.
12
+ *
13
+ * OpenClaw writes a full transcript per session (measured 2026-08-23 against
14
+ * OpenClaw 2026.7.1-2). This reads it, exactly the way `kiro-transcript-timeline`
15
+ * reads Kiro's — an agent that pushes nothing still has a producer.
16
+ *
17
+ * **Scope is deliberately the complement of the Gateway stream, not a
18
+ * duplicate of it.** Only `tool_exec` rows are emitted here. Prompts, responses
19
+ * and approval rows already arrive live over the Gateway and re-deriving them
20
+ * would double every turn in the strip.
21
+ *
22
+ * The record shapes below were copied off a real store, never invented — this
23
+ * repo has a specific history of parsers written against imagined fixtures that
24
+ * passed their tests and matched nothing real, so the test file pins the
25
+ * measured shape verbatim:
26
+ *
27
+ * {"type":"session","version":3,"id":"fef73899-…","timestamp":"2026-08-22T23:52:16.443Z",
28
+ * "cwd":"/Users/x/.openclaw/workspace"}
29
+ * {"type":"message","id":"577c04b8","parentId":"40296bc1",
30
+ * "timestamp":"2026-08-22T23:52:23.315Z",
31
+ * "message":{"role":"assistant","content":[
32
+ * {"type":"thinking","thinking":"…"},
33
+ * {"type":"toolCall","id":"38e16c61-…","name":"exec",
34
+ * "arguments":{"command":"ls ~/github/OpenClaw && …"}}],
35
+ * "provider":"openrouter","model":"stealth/ox-alpha","usage":{…}}}
36
+ * {"type":"message","id":"53079fbe","timestamp":"2026-08-22T23:52:23.376Z",
37
+ * "message":{"role":"toolResult","toolCallId":"38e16c61-…","toolName":"exec",
38
+ * "content":[{"type":"text","text":"CAPTURE_GUIDE.md\n…"}],
39
+ * "details":{"status":"completed","exitCode":0,"durationMs":13},"isError":false}}
40
+ *
41
+ * Three properties of that shape drive the code:
42
+ * - A line carries an ISO `timestamp` AND `message.timestamp` in epoch-ms.
43
+ * They agree; the ISO one is used, with the epoch one as the fallback,
44
+ * because a row with no time cannot be ordered against the live stream.
45
+ * - A tool call and its result are SEPARATE lines linked by `toolCallId`. The
46
+ * call names what was attempted and only the result knows whether it worked,
47
+ * so a row is emitted once, at the call, and completed from the result.
48
+ * - `isError` and `details.exitCode` disagree in the interesting direction: a
49
+ * command can exit 0 while the tool reports an error (and vice versa), so
50
+ * both are consulted rather than picking one.
51
+ */
52
+ import { readFileSync, readdirSync, statSync } from 'fs';
53
+ import { homedir } from 'os';
54
+ import { join } from 'path';
55
+ import { debug } from './logger.js';
56
+ /** Cap on transcript bytes read, matching the Claude/Kiro readers' posture. */
57
+ const MAX_TRANSCRIPT_BYTES = 4 * 1024 * 1024;
58
+ /** Agent directories scanned under `~/.openclaw/agents`. */
59
+ const MAX_AGENT_DIRS = 32;
60
+ /** Row text budgets — these land on 144×144 keys and 120-byte firmware buffers. */
61
+ const RAW_MAX = 160;
62
+ const DETAIL_MAX = 400;
63
+ export function openClawStateDir(opts = {}) {
64
+ return opts.stateDir ?? join(homedir(), '.openclaw');
65
+ }
66
+ /**
67
+ * Sessions across every agent, newest-first.
68
+ *
69
+ * Reads each agent's `sessions.json`, whose keys ARE the session keys — the one
70
+ * fact that says whether a given piece of OpenClaw activity is the chat the
71
+ * user is looking at (`agent:main:main`), a scheduled job
72
+ * (`agent:main:cron:<id>`) or a model-eval run (`agent:main:eval-…__r2`). All
73
+ * three arrive at AgentDeck as the single literal "OpenClaw", which is why an
74
+ * approval raised by an eval harness looked like it came from a conversation
75
+ * the user could not find.
76
+ */
77
+ export function listOpenClawSessions(opts = {}) {
78
+ const agentsRoot = join(openClawStateDir(opts), 'agents');
79
+ let agents;
80
+ try {
81
+ agents = readdirSync(agentsRoot, { withFileTypes: true, encoding: 'utf-8' })
82
+ .filter((e) => e.isDirectory() && !e.isSymbolicLink())
83
+ .slice(0, MAX_AGENT_DIRS)
84
+ .map((e) => e.name);
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ const out = [];
90
+ for (const agent of agents) {
91
+ const indexPath = join(agentsRoot, agent, 'sessions', 'sessions.json');
92
+ let parsed;
93
+ try {
94
+ const st = statSync(indexPath);
95
+ if (!st.isFile() || st.size > MAX_TRANSCRIPT_BYTES)
96
+ continue;
97
+ parsed = JSON.parse(readFileSync(indexPath, 'utf-8'));
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
103
+ continue;
104
+ for (const [sessionKey, value] of Object.entries(parsed)) {
105
+ if (!value || typeof value !== 'object')
106
+ continue;
107
+ const rec = value;
108
+ if (typeof rec.sessionId !== 'string' || !rec.sessionId)
109
+ continue;
110
+ out.push({
111
+ sessionKey,
112
+ sessionId: rec.sessionId,
113
+ agent,
114
+ updatedAt: typeof rec.updatedAt === 'number' ? rec.updatedAt : 0,
115
+ });
116
+ }
117
+ }
118
+ return out.sort((a, b) => b.updatedAt - a.updatedAt);
119
+ }
120
+ function clip(s, max) {
121
+ const flat = s.replace(/\s+/g, ' ').trim();
122
+ return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
123
+ }
124
+ /**
125
+ * Join detail parts into LINES, flattening each one but keeping the breaks.
126
+ *
127
+ * `detail` is ordered most-decisive-first precisely so a narrow surface can
128
+ * take its head line, and every consumer does exactly that (`split('\n')[0]`).
129
+ * Running the whole block through `clip` collapses those breaks and there is no
130
+ * head line left to take — the ordering becomes decorative and the surface
131
+ * shows the first 22 characters of a run-on string instead.
132
+ */
133
+ function clipLines(parts, max) {
134
+ const lines = [];
135
+ let budget = max;
136
+ for (const part of parts) {
137
+ if (!part)
138
+ continue;
139
+ for (const raw of part.split('\n')) {
140
+ if (budget <= 1)
141
+ return lines.join('\n');
142
+ const line = clip(raw, budget);
143
+ if (!line)
144
+ continue;
145
+ lines.push(line);
146
+ budget -= line.length + 1;
147
+ }
148
+ }
149
+ return lines.join('\n');
150
+ }
151
+ /** Epoch-ms for a store line. */
152
+ function lineTs(line) {
153
+ if (typeof line.timestamp === 'string') {
154
+ const parsed = Date.parse(line.timestamp);
155
+ if (Number.isFinite(parsed))
156
+ return parsed;
157
+ }
158
+ const inner = line.message?.timestamp;
159
+ if (typeof inner === 'number' && Number.isFinite(inner))
160
+ return inner;
161
+ return null;
162
+ }
163
+ /**
164
+ * The one line that says what a tool call is going to do.
165
+ *
166
+ * `exec` is the overwhelming majority and its `command` is the whole story.
167
+ * Anything else falls back to the first string-valued argument, then to the
168
+ * argument names — a tool this build has never seen must degrade to something
169
+ * true ("browser · url, selector") rather than render as nothing.
170
+ */
171
+ export function summarizeToolArguments(args) {
172
+ if (typeof args === 'string')
173
+ return args;
174
+ if (!args || typeof args !== 'object' || Array.isArray(args))
175
+ return '';
176
+ const rec = args;
177
+ for (const key of ['command', 'cmd', 'script', 'query', 'path', 'file', 'url', 'prompt']) {
178
+ const v = rec[key];
179
+ if (typeof v === 'string' && v.trim())
180
+ return v.trim();
181
+ }
182
+ for (const v of Object.values(rec)) {
183
+ if (typeof v === 'string' && v.trim())
184
+ return v.trim();
185
+ }
186
+ const names = Object.keys(rec);
187
+ return names.length > 0 ? names.join(', ') : '';
188
+ }
189
+ /**
190
+ * `tool_exec` rows for one session's transcript, oldest-first.
191
+ *
192
+ * Emitted at the CALL, then completed in place from the matching result: the
193
+ * call is when the work started and is what orders correctly against the live
194
+ * Gateway rows, while only the result knows the outcome. Building the row at
195
+ * the result instead would stamp every tool with the time it finished and lose
196
+ * any call still running when the read happened.
197
+ */
198
+ export function openClawTimelineForSession(sessionId, opts = {}) {
199
+ const path = join(openClawStateDir(opts), 'agents');
200
+ let transcript = null;
201
+ let agents = [];
202
+ try {
203
+ agents = readdirSync(path, { withFileTypes: true, encoding: 'utf-8' })
204
+ .filter((e) => e.isDirectory() && !e.isSymbolicLink())
205
+ .slice(0, MAX_AGENT_DIRS)
206
+ .map((e) => e.name);
207
+ }
208
+ catch {
209
+ return [];
210
+ }
211
+ for (const agent of agents) {
212
+ const candidate = join(path, agent, 'sessions', `${sessionId}.jsonl`);
213
+ try {
214
+ const st = statSync(candidate);
215
+ if (st.isFile() && st.size <= MAX_TRANSCRIPT_BYTES) {
216
+ transcript = candidate;
217
+ break;
218
+ }
219
+ }
220
+ catch {
221
+ // not this agent's session
222
+ }
223
+ }
224
+ if (!transcript)
225
+ return [];
226
+ let text;
227
+ try {
228
+ text = readFileSync(transcript, 'utf-8');
229
+ }
230
+ catch (err) {
231
+ debug('daemon', `openclaw transcript read failed for ${sessionId}: ${String(err)}`);
232
+ return [];
233
+ }
234
+ const rows = [];
235
+ const pending = new Map();
236
+ let model;
237
+ let provider;
238
+ for (const line of text.split('\n')) {
239
+ if (!line.trim())
240
+ continue;
241
+ let rec;
242
+ try {
243
+ rec = JSON.parse(line);
244
+ }
245
+ catch {
246
+ continue;
247
+ }
248
+ if (rec.type !== 'message')
249
+ continue;
250
+ const msg = rec.message;
251
+ if (!msg || typeof msg !== 'object')
252
+ continue;
253
+ const ts = lineTs(rec);
254
+ if (ts === null)
255
+ continue;
256
+ if (msg.role === 'assistant') {
257
+ // Carried onto the rows this turn produces: which weights actually ran is
258
+ // the fact an eval session exists to vary, and the Gateway stream reports
259
+ // one model for the whole hub.
260
+ if (typeof msg.model === 'string' && msg.model)
261
+ model = msg.model;
262
+ if (typeof msg.provider === 'string' && msg.provider)
263
+ provider = msg.provider;
264
+ const content = Array.isArray(msg.content) ? msg.content : [];
265
+ for (const part of content) {
266
+ if (!part || typeof part !== 'object')
267
+ continue;
268
+ const p = part;
269
+ if (p.type !== 'toolCall')
270
+ continue;
271
+ const toolName = typeof p.name === 'string' && p.name ? p.name : 'tool';
272
+ const summary = summarizeToolArguments(p.arguments);
273
+ const detailParts = [
274
+ model ? `model: ${provider ? `${provider}/${model}` : model}` : '',
275
+ ].filter(Boolean);
276
+ rows.push({
277
+ ts,
278
+ type: 'tool_exec',
279
+ raw: clip(summary ? `${toolName} · ${summary}` : toolName, RAW_MAX),
280
+ ...(detailParts.length > 0 ? { detail: clipLines(detailParts, DETAIL_MAX) } : {}),
281
+ agentType: 'openclaw',
282
+ });
283
+ if (typeof p.id === 'string' && p.id) {
284
+ pending.set(p.id, { index: rows.length - 1, toolName });
285
+ }
286
+ }
287
+ continue;
288
+ }
289
+ if (msg.role === 'toolResult') {
290
+ const callId = typeof msg.toolCallId === 'string' ? msg.toolCallId : '';
291
+ const target = callId ? pending.get(callId) : undefined;
292
+ if (!target)
293
+ continue;
294
+ pending.delete(callId);
295
+ const details = (msg.details ?? {});
296
+ const exitCode = typeof details.exitCode === 'number' ? details.exitCode : undefined;
297
+ const durationMs = typeof details.durationMs === 'number' ? details.durationMs : undefined;
298
+ // `isError` and a non-zero exit are independent signals — a tool can fail
299
+ // to run at all (isError, no exit code) and a command can fail while the
300
+ // tool call itself succeeded. Either one means this did not go well.
301
+ const failed = msg.isError === true || (exitCode !== undefined && exitCode !== 0);
302
+ const first = Array.isArray(msg.content)
303
+ ? msg.content.find((c) => c && typeof c === 'object' && c.type === 'text')
304
+ : undefined;
305
+ const output = typeof first?.text === 'string' ? first.text : '';
306
+ const outcome = [
307
+ failed ? 'failed' : 'ok',
308
+ exitCode !== undefined ? `exit ${exitCode}` : '',
309
+ durationMs !== undefined ? `${durationMs}ms` : '',
310
+ ].filter(Boolean).join(' · ');
311
+ const existing = rows[target.index];
312
+ // Outcome, then the model, then the output. The first two are short and
313
+ // decisive; the output is unbounded, so putting it ahead of the model
314
+ // pushed "which weights ran" past the clip on any tool that printed more
315
+ // than a line — the one fact an eval session exists to vary.
316
+ rows[target.index] = {
317
+ ...existing,
318
+ detail: clipLines([outcome, existing.detail, output], DETAIL_MAX),
319
+ ...(failed ? { status: 'denied' } : {}),
320
+ };
321
+ }
322
+ }
323
+ const since = opts.since;
324
+ const filtered = typeof since === 'number' ? rows.filter((r) => r.ts > since) : rows;
325
+ const limit = opts.limit;
326
+ return typeof limit === 'number' && limit >= 0 ? filtered.slice(-limit) : filtered;
327
+ }
328
+ //# sourceMappingURL=openclaw-transcript-timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openclaw-transcript-timeline.js","sourceRoot":"","sources":["../src/openclaw-transcript-timeline.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,8 @@ export interface CodexRolloutSummary extends TranscriptSummary {
26
26
  hasPendingCalls?: boolean;
27
27
  /** Internal companion rollouts never become top-level dashboard sessions. */
28
28
  isSubagent: boolean;
29
+ /** `codex-tui` for CLI, `Codex Desktop` for the desktop client. */
30
+ originator?: string;
29
31
  }
30
32
  /**
31
33
  * Condense a first user prompt into a one-line session goal: strip injected
@@ -53,6 +55,15 @@ export interface ObservedSession extends EnrichedSession {
53
55
  * prompt is over. Absent when no transcript could be located. */
54
56
  lastActivityAt?: number;
55
57
  }
58
+ /**
59
+ * A desktop app-server keeps rollout descriptors open for chats retained by
60
+ * the Codex App, including conversations that have not done work for days.
61
+ * An idle descriptor is therefore only a recent observation, not process
62
+ * liveness. Mirror the Swift daemon's `codexIdleObservationStaleTTL`: after
63
+ * this window the chat remains resumable in Codex, but leaves AgentDeck's
64
+ * active-session roster until its rollout changes again.
65
+ */
66
+ export declare const CODEX_APP_IDLE_ROLLOUT_TTL_MS = 90000;
56
67
  export declare function observedStateAfterSilence(state: ObservedState, lastActivityAt: number | undefined, now?: number): ObservedState;
57
68
  interface CodexRolloutFileInfo {
58
69
  mtimeMs: number;
@@ -103,6 +114,15 @@ export declare class PassiveSessionObserver {
103
114
  export declare function parseProcessTable(output: string): ProcInfo[];
104
115
  export declare function parseClaudeTranscript(raw: string): TranscriptSummary;
105
116
  export declare function parseCodexRollout(raw: string): CodexRolloutSummary;
117
+ export interface LocatedCodexRolloutSummary {
118
+ path: string;
119
+ mtimeMs: number;
120
+ summary: CodexRolloutSummary;
121
+ }
122
+ /** Locate and summarize a rollout by the stable id carried by lifecycle hooks.
123
+ * This path is independent of pid/fd enumeration, so it works on Windows and
124
+ * can distinguish desktop from TUI via session_meta.originator. */
125
+ export declare function codexRolloutSummaryForSession(sessionId: string, sessionsRoot?: string): LocatedCodexRolloutSummary | null;
106
126
  export declare function collectProcessInfo(): Promise<ProcInfo[]>;
107
127
  /**
108
128
  * Parse `Win32_Process | ConvertTo-Json` output into the shared ProcInfo shape.
@@ -121,7 +141,7 @@ export declare function appNameFromCommand(command: string): string | undefined;
121
141
  * tty, so the owning app is how the injector finds their UI. */
122
142
  export declare function resolveHostApp(pid: number, byPid: Map<number, ProcInfo>, maxDepth?: number): string | undefined;
123
143
  /** Testable async projection from open rollout paths to top-level sessions. */
124
- export declare function collectCodexSessionsFromRollouts(processes: ProcInfo[], rolloutsByPid: Map<number, string[]>, rolloutCache: CodexRolloutCache): Promise<ObservedSession[]>;
144
+ export declare function collectCodexSessionsFromRollouts(processes: ProcInfo[], rolloutsByPid: Map<number, string[]>, rolloutCache: CodexRolloutCache, now?: number): Promise<ObservedSession[]>;
125
145
  /**
126
146
  * A Codex process that can own a user session.
127
147
  *
@@ -1 +1 @@
1
- {"version":3,"file":"passive-observer.d.ts","sourceRoot":"","sources":["../src/passive-observer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"passive-observer.d.ts","sourceRoot":"","sources":["../src/passive-observer.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@ import { stripUnsafeText, rawSessionId } from '@agentdeck/shared';
13
13
  import { isClaudeInterruptMessage } from './claude-interrupt-marker.js';
14
14
  import { parseAntigravityTranscript, antigravityDefaultModel, antigravityConversationId, antigravityTranscriptPath, } from './apme/antigravity-transcript.js';
15
15
  import { KiroSessionCache, readKiroNativeSessionSnapshots, } from './kiro-session.js';
16
+ import { locateCodexRollout } from './codex-rollout-response.js';
16
17
  /** Pull display text out of a Claude user message (string or text blocks). */
17
18
  function claudeUserText(message) {
18
19
  if (!message)
@@ -50,6 +51,15 @@ const MAX_TAIL_BYTES = 512 * 1024;
50
51
  const MAX_SAMPLE_BYTES = 1024 * 1024;
51
52
  /** Transcript/rollout silence after which an end-event-less turn is presumed dead. */
52
53
  const STALE_TURN_MS = 10 * 60 * 1000;
54
+ /**
55
+ * A desktop app-server keeps rollout descriptors open for chats retained by
56
+ * the Codex App, including conversations that have not done work for days.
57
+ * An idle descriptor is therefore only a recent observation, not process
58
+ * liveness. Mirror the Swift daemon's `codexIdleObservationStaleTTL`: after
59
+ * this window the chat remains resumable in Codex, but leaves AgentDeck's
60
+ * active-session roster until its rollout changes again.
61
+ */
62
+ export const CODEX_APP_IDLE_ROLLOUT_TTL_MS = 90_000;
53
63
  export function observedStateAfterSilence(state, lastActivityAt, now = Date.now()) {
54
64
  return state === 'processing'
55
65
  && lastActivityAt !== undefined
@@ -295,6 +305,7 @@ export function parseCodexRollout(raw) {
295
305
  let lastContextTokens = 0;
296
306
  let contextWindow = 0;
297
307
  let isSubagent = false;
308
+ let originator;
298
309
  // A turn is in flight from task_started/user_message until task_complete/
299
310
  // turn_aborted. Mid-turn events (agent_message, function_call) must NOT end
300
311
  // it: most of a working turn is the thinking gap between a tool result and
@@ -313,6 +324,7 @@ export function parseCodexRollout(raw) {
313
324
  continue;
314
325
  sessionId = stringAt(payload, 'id') ?? sessionId;
315
326
  cwd = stringAt(payload, 'cwd') ?? cwd;
327
+ originator = stringAt(payload, 'originator') ?? originator;
316
328
  startedAt = timestampMs(stringAt(payload, 'timestamp')) ?? startedAt;
317
329
  const source = payload.source;
318
330
  isSubagent = isSubagent || (isRecord(source) && 'subagent' in source);
@@ -404,6 +416,7 @@ export function parseCodexRollout(raw) {
404
416
  return {
405
417
  hasPendingCalls: pendingCalls.size > 0,
406
418
  isSubagent,
419
+ originator,
407
420
  sessionId,
408
421
  cwd,
409
422
  startedAt,
@@ -418,6 +431,24 @@ export function parseCodexRollout(raw) {
418
431
  : undefined,
419
432
  };
420
433
  }
434
+ /** Locate and summarize a rollout by the stable id carried by lifecycle hooks.
435
+ * This path is independent of pid/fd enumeration, so it works on Windows and
436
+ * can distinguish desktop from TUI via session_meta.originator. */
437
+ export function codexRolloutSummaryForSession(sessionId, sessionsRoot) {
438
+ const path = locateCodexRollout(sessionId, sessionsRoot);
439
+ if (!path)
440
+ return null;
441
+ try {
442
+ const stat = statSync(path);
443
+ const raw = readFileHeadAndTail(path, 256 * 1024, MAX_SAMPLE_BYTES);
444
+ if (!raw)
445
+ return null;
446
+ return { path, mtimeMs: stat.mtimeMs, summary: parseCodexRollout(raw) };
447
+ }
448
+ catch {
449
+ return null;
450
+ }
451
+ }
421
452
  export async function collectProcessInfo() {
422
453
  if (process.platform === 'win32')
423
454
  return collectProcessInfoWin32();
@@ -578,7 +609,7 @@ async function collectCodexSessions(processes, rolloutCache) {
578
609
  return collectCodexSessionsFromRollouts(processes, rolloutsByPid, rolloutCache);
579
610
  }
580
611
  /** Testable async projection from open rollout paths to top-level sessions. */
581
- export async function collectCodexSessionsFromRollouts(processes, rolloutsByPid, rolloutCache) {
612
+ export async function collectCodexSessionsFromRollouts(processes, rolloutsByPid, rolloutCache, now = Date.now()) {
582
613
  const byPid = new Map(processes.map((p) => [p.pid, p]));
583
614
  const codex = processes.filter((p) => isCodexSessionProcessCommand(p.command));
584
615
  const activeRollouts = new Set();
@@ -598,15 +629,18 @@ export async function collectCodexSessionsFromRollouts(processes, rolloutsByPid,
598
629
  continue;
599
630
  const parsed = snapshot.summary;
600
631
  let state = parsed.state;
601
- if (state === 'processing' && !parsed.hasPendingCalls && Date.now() - snapshot.mtimeMs > STALE_TURN_MS) {
632
+ if (state === 'processing' && !parsed.hasPendingCalls && now - snapshot.mtimeMs > STALE_TURN_MS) {
602
633
  state = 'idle';
603
634
  }
635
+ const desktop = proc.command.includes('app-server');
636
+ if (desktop && state === 'idle' && now - snapshot.mtimeMs > CODEX_APP_IDLE_ROLLOUT_TTL_MS) {
637
+ continue;
638
+ }
604
639
  const sessionId = parsed.sessionId ?? codexRolloutId(rollout);
605
640
  if (seen.has(sessionId))
606
641
  continue;
607
642
  seen.add(sessionId);
608
643
  const cwd = parsed.cwd;
609
- const desktop = proc.command.includes('app-server');
610
644
  sessions.push({
611
645
  id: desktop ? `observed:codex-app:${sessionId}` : `observed:codex:${sessionId}`,
612
646
  port: 0,