@viewportai/daemon 0.6.2 → 0.7.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.
- package/README.md +75 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +59 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/bind-command.d.ts.map +1 -1
- package/dist/cli/bind-command.js +7 -3
- package/dist/cli/bind-command.js.map +1 -1
- package/dist/cli/commands.d.ts +2 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +2 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/context-vault-metadata-command.d.ts +33 -0
- package/dist/cli/context-vault-metadata-command.d.ts.map +1 -1
- package/dist/cli/context-vault-metadata-command.js +15 -5
- package/dist/cli/context-vault-metadata-command.js.map +1 -1
- package/dist/cli/context-vault-use-command.d.ts.map +1 -1
- package/dist/cli/context-vault-use-command.js +20 -0
- package/dist/cli/context-vault-use-command.js.map +1 -1
- package/dist/cli/daemon-settings.js +1 -1
- package/dist/cli/daemon-settings.js.map +1 -1
- package/dist/cli/lifecycle-commands.d.ts.map +1 -1
- package/dist/cli/lifecycle-commands.js +5 -0
- package/dist/cli/lifecycle-commands.js.map +1 -1
- package/dist/cli/lifecycle-status-command.d.ts.map +1 -1
- package/dist/cli/lifecycle-status-command.js +6 -0
- package/dist/cli/lifecycle-status-command.js.map +1 -1
- package/dist/cli/org-binding.d.ts +2 -0
- package/dist/cli/org-binding.d.ts.map +1 -1
- package/dist/cli/org-binding.js +20 -1
- package/dist/cli/org-binding.js.map +1 -1
- package/dist/cli/profile-command.d.ts +3 -0
- package/dist/cli/profile-command.d.ts.map +1 -0
- package/dist/cli/profile-command.js +320 -0
- package/dist/cli/profile-command.js.map +1 -0
- package/dist/cli/profile-runtime-commands.d.ts +6 -0
- package/dist/cli/profile-runtime-commands.d.ts.map +1 -0
- package/dist/cli/profile-runtime-commands.js +127 -0
- package/dist/cli/profile-runtime-commands.js.map +1 -0
- package/dist/cli/setup-command.d.ts +5 -0
- package/dist/cli/setup-command.d.ts.map +1 -1
- package/dist/cli/setup-command.js +52 -0
- package/dist/cli/setup-command.js.map +1 -1
- package/dist/cli/uninstall-command.d.ts +2 -0
- package/dist/cli/uninstall-command.d.ts.map +1 -0
- package/dist/cli/uninstall-command.js +111 -0
- package/dist/cli/uninstall-command.js.map +1 -0
- package/dist/config-resolution/config-writer.d.ts +4 -0
- package/dist/config-resolution/config-writer.d.ts.map +1 -1
- package/dist/config-resolution/config-writer.js +18 -2
- package/dist/config-resolution/config-writer.js.map +1 -1
- package/dist/config-resolution/resolver.js +6 -0
- package/dist/config-resolution/resolver.js.map +1 -1
- package/dist/config-resolution/schema.d.ts +4 -0
- package/dist/config-resolution/schema.d.ts.map +1 -1
- package/dist/config-resolution/schema.js +4 -0
- package/dist/config-resolution/schema.js.map +1 -1
- package/dist/config-resolution/types.d.ts +2 -0
- package/dist/config-resolution/types.d.ts.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +2 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/interfaces.d.ts +38 -0
- package/dist/core/interfaces.d.ts.map +1 -1
- package/dist/core/profiles.d.ts +43 -0
- package/dist/core/profiles.d.ts.map +1 -0
- package/dist/core/profiles.js +181 -0
- package/dist/core/profiles.js.map +1 -0
- package/dist/core/session-context-prompt.d.ts.map +1 -1
- package/dist/core/session-context-prompt.js +22 -4
- package/dist/core/session-context-prompt.js.map +1 -1
- package/dist/discovery/claude.js +10 -0
- package/dist/discovery/claude.js.map +1 -1
- package/dist/discovery/codex-parser.d.ts +10 -0
- package/dist/discovery/codex-parser.d.ts.map +1 -1
- package/dist/discovery/codex-parser.js +138 -6
- package/dist/discovery/codex-parser.js.map +1 -1
- package/dist/discovery/codex.d.ts.map +1 -1
- package/dist/discovery/codex.js +49 -2
- package/dist/discovery/codex.js.map +1 -1
- package/dist/discovery/jsonl-entry-parser.d.ts +38 -0
- package/dist/discovery/jsonl-entry-parser.d.ts.map +1 -1
- package/dist/discovery/jsonl-entry-parser.js +628 -8
- package/dist/discovery/jsonl-entry-parser.js.map +1 -1
- package/dist/discovery/jsonl-reader.d.ts +10 -0
- package/dist/discovery/jsonl-reader.d.ts.map +1 -1
- package/dist/discovery/jsonl-reader.js +50 -2
- package/dist/discovery/jsonl-reader.js.map +1 -1
- package/dist/hooks/types.d.ts +2 -2
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/server/hello-builder.d.ts +22 -0
- package/dist/server/hello-builder.d.ts.map +1 -1
- package/dist/server/hello-builder.js +31 -0
- package/dist/server/hello-builder.js.map +1 -1
- package/dist/server/ws-command-handlers.d.ts.map +1 -1
- package/dist/server/ws-command-handlers.js +1 -0
- package/dist/server/ws-command-handlers.js.map +1 -1
- package/dist/server/ws-daemon-event-bridge.d.ts.map +1 -1
- package/dist/server/ws-daemon-event-bridge.js +10 -0
- package/dist/server/ws-daemon-event-bridge.js.map +1 -1
- package/dist/server/ws-session-command-handlers.d.ts.map +1 -1
- package/dist/server/ws-session-command-handlers.js +47 -4
- package/dist/server/ws-session-command-handlers.js.map +1 -1
- package/docs/configuration.md +91 -4
- package/package.json +1 -1
|
@@ -13,7 +13,12 @@ export function parseJSONLEntry(entry) {
|
|
|
13
13
|
if (typeof entry !== 'object' || entry === null)
|
|
14
14
|
return [];
|
|
15
15
|
const e = entry;
|
|
16
|
-
return parseClaudeEntry(e) ??
|
|
16
|
+
return (parseClaudeEntry(e) ??
|
|
17
|
+
parseCodexEventMsgEntry(e) ??
|
|
18
|
+
parseCodexEntry(e) ??
|
|
19
|
+
parseCodexCompactedEntry(e) ??
|
|
20
|
+
parseProviderMetadataEntry(e) ??
|
|
21
|
+
[]);
|
|
17
22
|
}
|
|
18
23
|
function parseClaudeEntry(e) {
|
|
19
24
|
const type = e.type;
|
|
@@ -29,7 +34,7 @@ function parseClaudeEntry(e) {
|
|
|
29
34
|
// Simple string content → single text block
|
|
30
35
|
if (typeof content === 'string') {
|
|
31
36
|
if (content) {
|
|
32
|
-
blocks.push(
|
|
37
|
+
blocks.push(...mapClaudeTextToRichBlocks(type, content, ts, uuid));
|
|
33
38
|
}
|
|
34
39
|
return blocks;
|
|
35
40
|
}
|
|
@@ -38,7 +43,7 @@ function parseClaudeEntry(e) {
|
|
|
38
43
|
for (const block of content) {
|
|
39
44
|
if (typeof block === 'string') {
|
|
40
45
|
if (block) {
|
|
41
|
-
blocks.push(
|
|
46
|
+
blocks.push(...mapClaudeTextToRichBlocks(type, block, ts, uuid));
|
|
42
47
|
}
|
|
43
48
|
continue;
|
|
44
49
|
}
|
|
@@ -49,7 +54,7 @@ function parseClaudeEntry(e) {
|
|
|
49
54
|
switch (blockType) {
|
|
50
55
|
case 'text':
|
|
51
56
|
if (typeof b.text === 'string' && b.text) {
|
|
52
|
-
blocks.push(
|
|
57
|
+
blocks.push(...mapClaudeTextToRichBlocks(type, b.text, ts, uuid));
|
|
53
58
|
}
|
|
54
59
|
break;
|
|
55
60
|
case 'thinking':
|
|
@@ -58,8 +63,7 @@ function parseClaudeEntry(e) {
|
|
|
58
63
|
}
|
|
59
64
|
break;
|
|
60
65
|
case 'tool_use':
|
|
61
|
-
blocks.push({
|
|
62
|
-
kind: 'tool_use',
|
|
66
|
+
blocks.push(...mapClaudeToolUseToRichBlocks({
|
|
63
67
|
toolName: b.name || 'unknown',
|
|
64
68
|
toolId: b.id || '',
|
|
65
69
|
input: typeof b.input === 'object' && b.input !== null
|
|
@@ -67,19 +71,194 @@ function parseClaudeEntry(e) {
|
|
|
67
71
|
: {},
|
|
68
72
|
ts,
|
|
69
73
|
uuid,
|
|
70
|
-
});
|
|
74
|
+
}));
|
|
71
75
|
break;
|
|
72
76
|
case 'tool_result': {
|
|
73
77
|
const toolId = b.tool_use_id || '';
|
|
74
78
|
const isError = b.is_error === true;
|
|
75
79
|
const output = extractToolResultContent(b.content);
|
|
76
80
|
blocks.push({ kind: 'tool_result', toolId, output, isError, ts, uuid });
|
|
81
|
+
blocks.push(...viewportCliEventsFromOutput(output, ts, `${uuid || toolId}-viewport-cli`));
|
|
77
82
|
break;
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
return blocks;
|
|
82
87
|
}
|
|
88
|
+
function mapClaudeTextToRichBlocks(role, text, ts, uuid) {
|
|
89
|
+
if (role === 'user') {
|
|
90
|
+
const localCommandBlocks = claudeLocalCommandEventsFromText(text, ts, uuid);
|
|
91
|
+
if (localCommandBlocks)
|
|
92
|
+
return localCommandBlocks;
|
|
93
|
+
}
|
|
94
|
+
return [
|
|
95
|
+
{ kind: 'text', role, text, ts, uuid },
|
|
96
|
+
...viewportPlanEventsFromText(text, ts, `${uuid}-viewport-plan`),
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
function claudeLocalCommandEventsFromText(text, ts, uuid) {
|
|
100
|
+
if (!isClaudeLocalCommandText(text))
|
|
101
|
+
return null;
|
|
102
|
+
if (/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/i.test(text))
|
|
103
|
+
return [];
|
|
104
|
+
const commandName = taggedText(text, 'command-name');
|
|
105
|
+
if (commandName) {
|
|
106
|
+
const slashCommand = commandName.startsWith('/') ? commandName : `/${commandName}`;
|
|
107
|
+
const commandMessage = taggedText(text, 'command-message') || commandName;
|
|
108
|
+
const commandArgs = taggedText(text, 'command-args');
|
|
109
|
+
return [
|
|
110
|
+
{
|
|
111
|
+
kind: 'event',
|
|
112
|
+
title: `Claude command: ${slashCommand}`,
|
|
113
|
+
body: [commandMessage, commandArgs ? `args ${commandArgs}` : null]
|
|
114
|
+
.filter(Boolean)
|
|
115
|
+
.join('\n'),
|
|
116
|
+
tone: 'muted',
|
|
117
|
+
ts,
|
|
118
|
+
uuid: `${uuid}:local-command:${commandName}`,
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
const stdout = taggedText(text, 'local-command-stdout');
|
|
123
|
+
if (stdout) {
|
|
124
|
+
return [
|
|
125
|
+
{
|
|
126
|
+
kind: 'event',
|
|
127
|
+
title: 'Claude command output',
|
|
128
|
+
body: stdout,
|
|
129
|
+
tone: 'muted',
|
|
130
|
+
ts,
|
|
131
|
+
uuid: `${uuid}:local-command-stdout`,
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
const stderr = taggedText(text, 'local-command-stderr');
|
|
136
|
+
if (stderr) {
|
|
137
|
+
return [
|
|
138
|
+
{
|
|
139
|
+
kind: 'event',
|
|
140
|
+
title: 'Claude command warning',
|
|
141
|
+
body: stderr,
|
|
142
|
+
tone: 'warning',
|
|
143
|
+
ts,
|
|
144
|
+
uuid: `${uuid}:local-command-stderr`,
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
const taskNotification = taggedText(text, 'task-notification');
|
|
149
|
+
if (taskNotification) {
|
|
150
|
+
return [
|
|
151
|
+
{
|
|
152
|
+
kind: 'event',
|
|
153
|
+
title: 'Claude task notification',
|
|
154
|
+
body: taskNotification,
|
|
155
|
+
tone: 'muted',
|
|
156
|
+
ts,
|
|
157
|
+
uuid: `${uuid}:task-notification`,
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
const teammateMessage = taggedText(text, 'teammate-message');
|
|
162
|
+
if (teammateMessage) {
|
|
163
|
+
return [
|
|
164
|
+
{
|
|
165
|
+
kind: 'event',
|
|
166
|
+
title: 'Teammate message',
|
|
167
|
+
body: teammateMessage,
|
|
168
|
+
tone: 'muted',
|
|
169
|
+
ts,
|
|
170
|
+
uuid: `${uuid}:teammate-message`,
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
function mapClaudeToolUseToRichBlocks(input) {
|
|
177
|
+
if (input.toolName === 'Bash') {
|
|
178
|
+
const command = typeof input.input.command === 'string' ? input.input.command.trim() : '';
|
|
179
|
+
if (command) {
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
182
|
+
kind: 'command',
|
|
183
|
+
command,
|
|
184
|
+
cwd: typeof input.input.cwd === 'string' ? input.input.cwd : undefined,
|
|
185
|
+
status: 'started',
|
|
186
|
+
ts: input.ts,
|
|
187
|
+
uuid: input.toolId || input.uuid,
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (input.toolName === 'Agent') {
|
|
193
|
+
const subagentType = typeof input.input.subagent_type === 'string' ? input.input.subagent_type.trim() : '';
|
|
194
|
+
const description = typeof input.input.description === 'string' ? input.input.description.trim() : '';
|
|
195
|
+
const prompt = typeof input.input.prompt === 'string' ? input.input.prompt.trim() : '';
|
|
196
|
+
return [
|
|
197
|
+
{
|
|
198
|
+
kind: 'event',
|
|
199
|
+
title: `Subagent started${subagentType ? `: ${subagentType}` : ''}`,
|
|
200
|
+
body: [description, prompt ? firstLine(prompt) : null].filter(Boolean).join('\n'),
|
|
201
|
+
tone: 'muted',
|
|
202
|
+
ts: input.ts,
|
|
203
|
+
uuid: input.toolId || input.uuid,
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
if (input.toolName.startsWith('Task')) {
|
|
208
|
+
const subject = typeof input.input.task_subject === 'string'
|
|
209
|
+
? input.input.task_subject.trim()
|
|
210
|
+
: typeof input.input.subject === 'string'
|
|
211
|
+
? input.input.subject.trim()
|
|
212
|
+
: '';
|
|
213
|
+
const description = typeof input.input.task_description === 'string'
|
|
214
|
+
? input.input.task_description.trim()
|
|
215
|
+
: typeof input.input.description === 'string'
|
|
216
|
+
? input.input.description.trim()
|
|
217
|
+
: '';
|
|
218
|
+
return [
|
|
219
|
+
{
|
|
220
|
+
kind: 'event',
|
|
221
|
+
title: subject ? `${input.toolName}: ${subject}` : `Claude task: ${input.toolName}`,
|
|
222
|
+
body: description || safeJsonPreview(input.input),
|
|
223
|
+
tone: input.toolName === 'TaskCompleted' ? 'success' : 'muted',
|
|
224
|
+
ts: input.ts,
|
|
225
|
+
uuid: input.toolId || input.uuid,
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
if (isClaudeFileMutationTool(input.toolName)) {
|
|
230
|
+
const filePath = typeof input.input.file_path === 'string'
|
|
231
|
+
? input.input.file_path
|
|
232
|
+
: typeof input.input.path === 'string'
|
|
233
|
+
? input.input.path
|
|
234
|
+
: undefined;
|
|
235
|
+
return [
|
|
236
|
+
{
|
|
237
|
+
kind: 'file_change',
|
|
238
|
+
path: filePath,
|
|
239
|
+
operation: input.toolName,
|
|
240
|
+
ts: input.ts,
|
|
241
|
+
uuid: input.toolId || input.uuid,
|
|
242
|
+
},
|
|
243
|
+
];
|
|
244
|
+
}
|
|
245
|
+
return [
|
|
246
|
+
{
|
|
247
|
+
kind: 'tool_use',
|
|
248
|
+
toolName: input.toolName,
|
|
249
|
+
toolId: input.toolId,
|
|
250
|
+
input: input.input,
|
|
251
|
+
ts: input.ts,
|
|
252
|
+
uuid: input.uuid,
|
|
253
|
+
},
|
|
254
|
+
];
|
|
255
|
+
}
|
|
256
|
+
function isClaudeFileMutationTool(toolName) {
|
|
257
|
+
return (toolName === 'Edit' ||
|
|
258
|
+
toolName === 'Write' ||
|
|
259
|
+
toolName === 'MultiEdit' ||
|
|
260
|
+
toolName === 'NotebookEdit');
|
|
261
|
+
}
|
|
83
262
|
function parseCodexEntry(e) {
|
|
84
263
|
const envelopeType = e.type;
|
|
85
264
|
if (envelopeType !== 'response_item')
|
|
@@ -102,6 +281,7 @@ function parseCodexEntry(e) {
|
|
|
102
281
|
const text = content.trim();
|
|
103
282
|
if (text) {
|
|
104
283
|
messageBlocks.push({ kind: 'text', role, text, ts, uuid: baseUuid });
|
|
284
|
+
messageBlocks.push(...viewportPlanEventsFromText(text, ts, `${baseUuid}-viewport-plan`));
|
|
105
285
|
}
|
|
106
286
|
return messageBlocks;
|
|
107
287
|
}
|
|
@@ -120,6 +300,7 @@ function parseCodexEntry(e) {
|
|
|
120
300
|
ts,
|
|
121
301
|
uuid: `${baseUuid}-${i}`,
|
|
122
302
|
});
|
|
303
|
+
messageBlocks.push(...viewportPlanEventsFromText(text, ts, `${baseUuid}-${i}-viewport-plan`));
|
|
123
304
|
}
|
|
124
305
|
return messageBlocks;
|
|
125
306
|
}
|
|
@@ -152,6 +333,7 @@ function parseCodexEntry(e) {
|
|
|
152
333
|
ts,
|
|
153
334
|
uuid: codexUuid(payload, ts, 'tool-result'),
|
|
154
335
|
},
|
|
336
|
+
...viewportCliEventsFromOutput(output, ts, `${codexUuid(payload, ts, 'tool-result')}-viewport-cli`),
|
|
155
337
|
];
|
|
156
338
|
}
|
|
157
339
|
if (itemType === 'reasoning') {
|
|
@@ -167,7 +349,137 @@ function parseCodexEntry(e) {
|
|
|
167
349
|
},
|
|
168
350
|
];
|
|
169
351
|
}
|
|
170
|
-
return [
|
|
352
|
+
return [
|
|
353
|
+
{
|
|
354
|
+
kind: 'event',
|
|
355
|
+
title: `Provider item: ${itemType || 'unknown'}`,
|
|
356
|
+
body: safeJsonPreview(payload),
|
|
357
|
+
tone: 'muted',
|
|
358
|
+
ts,
|
|
359
|
+
uuid: codexUuid(payload, ts, 'item'),
|
|
360
|
+
},
|
|
361
|
+
];
|
|
362
|
+
}
|
|
363
|
+
function parseCodexEventMsgEntry(e) {
|
|
364
|
+
if (e.type !== 'event_msg')
|
|
365
|
+
return null;
|
|
366
|
+
const payload = typeof e.payload === 'object' && e.payload !== null
|
|
367
|
+
? e.payload
|
|
368
|
+
: null;
|
|
369
|
+
if (!payload)
|
|
370
|
+
return [];
|
|
371
|
+
const ts = timestampFromEntry(e, payload);
|
|
372
|
+
const itemType = payload.type;
|
|
373
|
+
const uuid = codexUuid(payload, ts, itemType || 'event');
|
|
374
|
+
if (itemType === 'user_message') {
|
|
375
|
+
const text = extractText(payload.message ?? payload.text);
|
|
376
|
+
return text ? [{ kind: 'text', role: 'user', text, ts, uuid }] : [];
|
|
377
|
+
}
|
|
378
|
+
if (itemType === 'agent_message' || itemType === 'assistant_message') {
|
|
379
|
+
const text = extractText(payload.message ?? payload.text);
|
|
380
|
+
return text
|
|
381
|
+
? [
|
|
382
|
+
{ kind: 'text', role: 'assistant', text, ts, uuid },
|
|
383
|
+
...viewportPlanEventsFromText(text, ts, `${uuid}-viewport-plan`),
|
|
384
|
+
]
|
|
385
|
+
: [];
|
|
386
|
+
}
|
|
387
|
+
if (itemType === 'agent_message_delta' || itemType === 'agent_message_content_delta') {
|
|
388
|
+
const text = extractText(payload.delta ?? payload.text ?? payload.content);
|
|
389
|
+
return text ? [{ kind: 'text', role: 'assistant', text, ts, uuid }] : [];
|
|
390
|
+
}
|
|
391
|
+
if (itemType === 'exec_command_begin') {
|
|
392
|
+
return [
|
|
393
|
+
{
|
|
394
|
+
kind: 'command',
|
|
395
|
+
command: commandToString(payload.command),
|
|
396
|
+
cwd: typeof payload.cwd === 'string' ? payload.cwd : undefined,
|
|
397
|
+
status: 'started',
|
|
398
|
+
ts,
|
|
399
|
+
uuid,
|
|
400
|
+
},
|
|
401
|
+
];
|
|
402
|
+
}
|
|
403
|
+
if (itemType === 'exec_command_end') {
|
|
404
|
+
return [
|
|
405
|
+
{
|
|
406
|
+
kind: 'command',
|
|
407
|
+
command: commandToString(payload.command),
|
|
408
|
+
cwd: typeof payload.cwd === 'string' ? payload.cwd : undefined,
|
|
409
|
+
status: 'completed',
|
|
410
|
+
exitCode: numberOrNull(payload.exit_code),
|
|
411
|
+
output: extractText(payload.aggregated_output ?? payload.formatted_output ?? payload.stdout),
|
|
412
|
+
durationMs: durationToMs(payload.duration),
|
|
413
|
+
ts,
|
|
414
|
+
uuid,
|
|
415
|
+
},
|
|
416
|
+
];
|
|
417
|
+
}
|
|
418
|
+
if (itemType === 'exec_approval_request') {
|
|
419
|
+
const command = commandToString(payload.command);
|
|
420
|
+
const reason = typeof payload.reason === 'string' ? payload.reason : '';
|
|
421
|
+
return [
|
|
422
|
+
{
|
|
423
|
+
kind: 'approval',
|
|
424
|
+
title: 'Command approval needed',
|
|
425
|
+
body: [reason, command].filter(Boolean).join('\n'),
|
|
426
|
+
input: payload,
|
|
427
|
+
ts,
|
|
428
|
+
uuid,
|
|
429
|
+
},
|
|
430
|
+
];
|
|
431
|
+
}
|
|
432
|
+
if (itemType === 'turn_diff') {
|
|
433
|
+
return [
|
|
434
|
+
{
|
|
435
|
+
kind: 'file_change',
|
|
436
|
+
diff: typeof payload.unified_diff === 'string' ? payload.unified_diff : undefined,
|
|
437
|
+
operation: 'diff',
|
|
438
|
+
ts,
|
|
439
|
+
uuid,
|
|
440
|
+
},
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
if (itemType === 'turn_aborted' || itemType === 'turn_failed') {
|
|
444
|
+
return [
|
|
445
|
+
{
|
|
446
|
+
kind: 'event',
|
|
447
|
+
title: itemType === 'turn_aborted' ? 'Turn aborted' : 'Turn failed',
|
|
448
|
+
body: extractText(payload.error ?? payload.message) || itemType,
|
|
449
|
+
tone: 'danger',
|
|
450
|
+
ts,
|
|
451
|
+
uuid,
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
}
|
|
455
|
+
if (itemType === 'thread_name_updated') {
|
|
456
|
+
const title = typeof payload.thread_name === 'string' ? payload.thread_name : '';
|
|
457
|
+
return title
|
|
458
|
+
? [{ kind: 'event', title: 'Session renamed', body: title, tone: 'muted', ts, uuid }]
|
|
459
|
+
: [];
|
|
460
|
+
}
|
|
461
|
+
if (itemType === 'token_count') {
|
|
462
|
+
return [
|
|
463
|
+
{
|
|
464
|
+
kind: 'usage',
|
|
465
|
+
inputTokens: numberOrUndefined(payload.input_tokens ?? payload.inputTokens),
|
|
466
|
+
outputTokens: numberOrUndefined(payload.output_tokens ?? payload.outputTokens),
|
|
467
|
+
totalTokens: numberOrUndefined(payload.total_tokens ?? payload.totalTokens),
|
|
468
|
+
ts,
|
|
469
|
+
uuid,
|
|
470
|
+
},
|
|
471
|
+
];
|
|
472
|
+
}
|
|
473
|
+
return [
|
|
474
|
+
{
|
|
475
|
+
kind: 'event',
|
|
476
|
+
title: `Provider event: ${itemType || 'unknown'}`,
|
|
477
|
+
body: safeJsonPreview(payload),
|
|
478
|
+
tone: 'muted',
|
|
479
|
+
ts,
|
|
480
|
+
uuid,
|
|
481
|
+
},
|
|
482
|
+
];
|
|
171
483
|
}
|
|
172
484
|
function parseCodexCompactedEntry(e) {
|
|
173
485
|
if (e.type !== 'compacted')
|
|
@@ -203,6 +515,44 @@ function parseCodexCompactedEntry(e) {
|
|
|
203
515
|
}
|
|
204
516
|
return blocks;
|
|
205
517
|
}
|
|
518
|
+
function parseProviderMetadataEntry(e) {
|
|
519
|
+
const type = typeof e.type === 'string' ? e.type : '';
|
|
520
|
+
if (!type)
|
|
521
|
+
return null;
|
|
522
|
+
const ts = timestampFromEntry(e);
|
|
523
|
+
const uuid = typeof e.uuid === 'string' && e.uuid ? e.uuid : `provider-${type}-${ts}`;
|
|
524
|
+
if (type === 'custom-title') {
|
|
525
|
+
const title = stringField(e, 'customTitle') || stringField(e, 'custom_title') || stringField(e, 'title');
|
|
526
|
+
return title
|
|
527
|
+
? [{ kind: 'event', title: 'Session renamed', body: title, tone: 'muted', ts, uuid }]
|
|
528
|
+
: [];
|
|
529
|
+
}
|
|
530
|
+
if (type === 'summary' || type === 'compact' || type === 'compaction') {
|
|
531
|
+
const summary = extractText(e.summary ?? e.message ?? e.content);
|
|
532
|
+
return [
|
|
533
|
+
{
|
|
534
|
+
kind: 'event',
|
|
535
|
+
title: 'Conversation compacted',
|
|
536
|
+
body: summary || safeJsonPreview(e),
|
|
537
|
+
tone: 'muted',
|
|
538
|
+
ts,
|
|
539
|
+
uuid,
|
|
540
|
+
},
|
|
541
|
+
];
|
|
542
|
+
}
|
|
543
|
+
if (type === 'system' && isInjectedEnvironmentEntry(e))
|
|
544
|
+
return [];
|
|
545
|
+
return [
|
|
546
|
+
{
|
|
547
|
+
kind: 'event',
|
|
548
|
+
title: `Provider event: ${type}`,
|
|
549
|
+
body: safeJsonPreview(e),
|
|
550
|
+
tone: 'muted',
|
|
551
|
+
ts,
|
|
552
|
+
uuid,
|
|
553
|
+
},
|
|
554
|
+
];
|
|
555
|
+
}
|
|
206
556
|
// ---------------------------------------------------------------------------
|
|
207
557
|
// Helpers
|
|
208
558
|
// ---------------------------------------------------------------------------
|
|
@@ -230,6 +580,176 @@ function extractToolResultContent(content) {
|
|
|
230
580
|
}
|
|
231
581
|
return '';
|
|
232
582
|
}
|
|
583
|
+
function viewportCliEventsFromOutput(output, ts, uuidPrefix) {
|
|
584
|
+
const payload = parseViewportCliJson(output);
|
|
585
|
+
if (!payload)
|
|
586
|
+
return [];
|
|
587
|
+
const schemaVersion = typeof payload.schema_version === 'string' ? payload.schema_version : '';
|
|
588
|
+
const ok = payload.ok !== false;
|
|
589
|
+
if (schemaVersion === 'viewport.cli.context_propose/v1') {
|
|
590
|
+
const candidateId = stringField(payload, 'candidate_id');
|
|
591
|
+
const providerId = stringField(payload, 'provider_id');
|
|
592
|
+
const status = stringField(payload, 'status') || (ok ? 'pending_review' : 'not_queued');
|
|
593
|
+
const digest = stringField(payload, 'payload_digest');
|
|
594
|
+
return [
|
|
595
|
+
{
|
|
596
|
+
kind: 'event',
|
|
597
|
+
title: ok ? 'Context candidate proposed' : 'Context proposal did not queue',
|
|
598
|
+
body: [
|
|
599
|
+
candidateId ? `candidate ${candidateId}` : null,
|
|
600
|
+
providerId ? `provider ${providerId}` : null,
|
|
601
|
+
status ? `status ${status}` : null,
|
|
602
|
+
digest ? digest : null,
|
|
603
|
+
]
|
|
604
|
+
.filter(Boolean)
|
|
605
|
+
.join('\n'),
|
|
606
|
+
tone: ok ? 'warning' : 'muted',
|
|
607
|
+
ts,
|
|
608
|
+
uuid: `${uuidPrefix}:context-propose:${candidateId || status || schemaVersion}`,
|
|
609
|
+
},
|
|
610
|
+
];
|
|
611
|
+
}
|
|
612
|
+
if (schemaVersion === 'viewport.cli.context_search/v1') {
|
|
613
|
+
const count = numberOrUndefined(payload.result_count ?? payload.count);
|
|
614
|
+
return [
|
|
615
|
+
{
|
|
616
|
+
kind: 'event',
|
|
617
|
+
title: 'Context searched',
|
|
618
|
+
body: typeof count === 'number'
|
|
619
|
+
? `${count.toLocaleString()} result${count === 1 ? '' : 's'}`
|
|
620
|
+
: 'Context search completed',
|
|
621
|
+
tone: 'muted',
|
|
622
|
+
ts,
|
|
623
|
+
uuid: `${uuidPrefix}:context-search`,
|
|
624
|
+
},
|
|
625
|
+
];
|
|
626
|
+
}
|
|
627
|
+
if (schemaVersion === 'viewport.cli.context_get/v1') {
|
|
628
|
+
return [
|
|
629
|
+
{
|
|
630
|
+
kind: 'event',
|
|
631
|
+
title: 'Context loaded',
|
|
632
|
+
body: stringField(payload, 'provider_id') || 'Context item loaded',
|
|
633
|
+
tone: 'muted',
|
|
634
|
+
ts,
|
|
635
|
+
uuid: `${uuidPrefix}:context-get`,
|
|
636
|
+
},
|
|
637
|
+
];
|
|
638
|
+
}
|
|
639
|
+
if (schemaVersion === 'viewport.cli.context_use/v1') {
|
|
640
|
+
const providerId = stringField(payload, 'provider_id');
|
|
641
|
+
return [
|
|
642
|
+
{
|
|
643
|
+
kind: 'event',
|
|
644
|
+
title: 'Context attached to repo',
|
|
645
|
+
body: providerId ? `provider ${providerId}` : 'Context provider attached',
|
|
646
|
+
tone: ok ? 'success' : 'warning',
|
|
647
|
+
ts,
|
|
648
|
+
uuid: `${uuidPrefix}:context-use:${providerId || schemaVersion}`,
|
|
649
|
+
},
|
|
650
|
+
];
|
|
651
|
+
}
|
|
652
|
+
if (schemaVersion === 'viewport.cli.context_create/v1') {
|
|
653
|
+
const vaultId = stringField(payload, 'vault_id') || stringField(payload, 'id');
|
|
654
|
+
return [
|
|
655
|
+
{
|
|
656
|
+
kind: 'event',
|
|
657
|
+
title: 'Context vault created',
|
|
658
|
+
body: vaultId ? `vault ${vaultId}` : 'Context vault created',
|
|
659
|
+
tone: ok ? 'success' : 'warning',
|
|
660
|
+
ts,
|
|
661
|
+
uuid: `${uuidPrefix}:context-create:${vaultId || schemaVersion}`,
|
|
662
|
+
},
|
|
663
|
+
];
|
|
664
|
+
}
|
|
665
|
+
if (schemaVersion === 'viewport.cli.session_manifest/v1') {
|
|
666
|
+
return [
|
|
667
|
+
{
|
|
668
|
+
kind: 'event',
|
|
669
|
+
title: 'Viewport repo config read',
|
|
670
|
+
body: stringField(payload, 'manifest_digest') || 'Session manifest resolved',
|
|
671
|
+
tone: 'muted',
|
|
672
|
+
ts,
|
|
673
|
+
uuid: `${uuidPrefix}:session-manifest`,
|
|
674
|
+
},
|
|
675
|
+
];
|
|
676
|
+
}
|
|
677
|
+
return [];
|
|
678
|
+
}
|
|
679
|
+
function viewportPlanEventsFromText(text, ts, uuidPrefix) {
|
|
680
|
+
const payload = parseViewportPlanPayload(text);
|
|
681
|
+
if (!payload)
|
|
682
|
+
return [];
|
|
683
|
+
const schema = stringField(payload, 'schema');
|
|
684
|
+
if (schema !== 'viewport.plan_proposal/v1')
|
|
685
|
+
return [];
|
|
686
|
+
const title = stringField(payload, 'title') || 'Plan proposal';
|
|
687
|
+
const summary = stringField(payload, 'summary');
|
|
688
|
+
const source = stringField(payload, 'source');
|
|
689
|
+
const body = [summary, source ? `source ${source}` : null].filter(Boolean).join('\n');
|
|
690
|
+
return [
|
|
691
|
+
{
|
|
692
|
+
kind: 'event',
|
|
693
|
+
title: `Plan draft emitted: ${title}`,
|
|
694
|
+
body: body || 'A Viewport plan proposal block was emitted for trusted-edge capture.',
|
|
695
|
+
tone: 'warning',
|
|
696
|
+
ts,
|
|
697
|
+
uuid: `${uuidPrefix}:${title}`,
|
|
698
|
+
},
|
|
699
|
+
];
|
|
700
|
+
}
|
|
701
|
+
function parseViewportPlanPayload(text) {
|
|
702
|
+
const fence = /```viewport-plan\s*\n([\s\S]*?)```/i.exec(text);
|
|
703
|
+
const comment = /<!--\s*viewport-plan\s*\n([\s\S]*?)-->/i.exec(text);
|
|
704
|
+
const raw = fence?.[1] ?? comment?.[1];
|
|
705
|
+
if (!raw)
|
|
706
|
+
return null;
|
|
707
|
+
const trimmed = raw.trim();
|
|
708
|
+
if (!trimmed.includes('viewport.plan_proposal/v1'))
|
|
709
|
+
return null;
|
|
710
|
+
try {
|
|
711
|
+
const parsed = JSON.parse(trimmed);
|
|
712
|
+
if (typeof parsed === 'object' && parsed !== null)
|
|
713
|
+
return parsed;
|
|
714
|
+
}
|
|
715
|
+
catch {
|
|
716
|
+
// YAML-frontmatter style plan blocks are allowed elsewhere. The session
|
|
717
|
+
// reader keeps them as normal transcript text until a structured parser is
|
|
718
|
+
// needed for timeline links.
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
}
|
|
722
|
+
function isInjectedEnvironmentEntry(entry) {
|
|
723
|
+
const content = extractText(entry.content ?? entry.message);
|
|
724
|
+
return content.includes('<environment_context>') || content.includes('<system-reminder>');
|
|
725
|
+
}
|
|
726
|
+
function parseViewportCliJson(output) {
|
|
727
|
+
const trimmed = output.trim();
|
|
728
|
+
if (!trimmed.includes('schema_version'))
|
|
729
|
+
return null;
|
|
730
|
+
const candidates = [trimmed];
|
|
731
|
+
const firstBrace = trimmed.indexOf('{');
|
|
732
|
+
const lastBrace = trimmed.lastIndexOf('}');
|
|
733
|
+
if (firstBrace >= 0 && lastBrace > firstBrace) {
|
|
734
|
+
candidates.push(trimmed.slice(firstBrace, lastBrace + 1));
|
|
735
|
+
}
|
|
736
|
+
for (const candidate of candidates) {
|
|
737
|
+
try {
|
|
738
|
+
const parsed = JSON.parse(candidate);
|
|
739
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
740
|
+
return parsed;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch {
|
|
744
|
+
// Keep trying the next candidate.
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
function stringField(record, field) {
|
|
750
|
+
const value = record[field];
|
|
751
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
752
|
+
}
|
|
233
753
|
function timestampFromEntry(entry, payload) {
|
|
234
754
|
const fromPayload = payload?.timestamp;
|
|
235
755
|
if (typeof fromPayload === 'string' && fromPayload)
|
|
@@ -329,4 +849,104 @@ function extractCodexReasoning(payload) {
|
|
|
329
849
|
const fallback = extractCodexContentText(payload);
|
|
330
850
|
return fallback;
|
|
331
851
|
}
|
|
852
|
+
function extractText(value) {
|
|
853
|
+
if (typeof value === 'string')
|
|
854
|
+
return value.trim();
|
|
855
|
+
if (Array.isArray(value)) {
|
|
856
|
+
return value
|
|
857
|
+
.map((item) => extractText(item))
|
|
858
|
+
.filter(Boolean)
|
|
859
|
+
.join('\n')
|
|
860
|
+
.trim();
|
|
861
|
+
}
|
|
862
|
+
if (!value || typeof value !== 'object')
|
|
863
|
+
return '';
|
|
864
|
+
const rec = value;
|
|
865
|
+
const candidates = [
|
|
866
|
+
rec.text,
|
|
867
|
+
rec.output_text,
|
|
868
|
+
rec.input_text,
|
|
869
|
+
rec.value,
|
|
870
|
+
rec.content,
|
|
871
|
+
rec.message,
|
|
872
|
+
];
|
|
873
|
+
for (const candidate of candidates) {
|
|
874
|
+
const text = extractText(candidate);
|
|
875
|
+
if (text)
|
|
876
|
+
return text;
|
|
877
|
+
}
|
|
878
|
+
return '';
|
|
879
|
+
}
|
|
880
|
+
function isClaudeLocalCommandText(text) {
|
|
881
|
+
return /<(?:local-command-caveat|local-command-std(?:out|err)|command-name|command-message|command-args|task-notification|teammate-message)>/i.test(text);
|
|
882
|
+
}
|
|
883
|
+
function firstLine(text) {
|
|
884
|
+
return (text
|
|
885
|
+
.split(/\r?\n/)
|
|
886
|
+
.find((line) => line.trim())
|
|
887
|
+
?.trim() ?? '');
|
|
888
|
+
}
|
|
889
|
+
function taggedText(text, tag) {
|
|
890
|
+
const escaped = tag.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
891
|
+
const match = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, 'i').exec(text);
|
|
892
|
+
return match
|
|
893
|
+
? stripAnsi(match[1] ?? '')
|
|
894
|
+
.replace(/\s+/g, ' ')
|
|
895
|
+
.trim()
|
|
896
|
+
: '';
|
|
897
|
+
}
|
|
898
|
+
function stripAnsi(text) {
|
|
899
|
+
// ANSI CSI escape sequences emitted by Claude local command status lines.
|
|
900
|
+
return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '');
|
|
901
|
+
}
|
|
902
|
+
function commandToString(value) {
|
|
903
|
+
if (Array.isArray(value)) {
|
|
904
|
+
return value
|
|
905
|
+
.map((part) => String(part))
|
|
906
|
+
.join(' ')
|
|
907
|
+
.trim();
|
|
908
|
+
}
|
|
909
|
+
if (typeof value === 'string')
|
|
910
|
+
return value.trim();
|
|
911
|
+
return '';
|
|
912
|
+
}
|
|
913
|
+
function numberOrNull(value) {
|
|
914
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
915
|
+
return value;
|
|
916
|
+
if (typeof value === 'string') {
|
|
917
|
+
const parsed = Number(value);
|
|
918
|
+
if (Number.isFinite(parsed))
|
|
919
|
+
return parsed;
|
|
920
|
+
}
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
function numberOrUndefined(value) {
|
|
924
|
+
const n = numberOrNull(value);
|
|
925
|
+
return n === null ? undefined : n;
|
|
926
|
+
}
|
|
927
|
+
function durationToMs(value) {
|
|
928
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
929
|
+
return value;
|
|
930
|
+
if (typeof value === 'string') {
|
|
931
|
+
const parsed = Number(value);
|
|
932
|
+
if (Number.isFinite(parsed))
|
|
933
|
+
return parsed;
|
|
934
|
+
}
|
|
935
|
+
if (!value || typeof value !== 'object')
|
|
936
|
+
return null;
|
|
937
|
+
const rec = value;
|
|
938
|
+
return (numberOrNull(rec.ms) ??
|
|
939
|
+
numberOrNull(rec.millis) ??
|
|
940
|
+
numberOrNull(rec.milliseconds) ??
|
|
941
|
+
(typeof rec.secs === 'number' ? rec.secs * 1000 : null));
|
|
942
|
+
}
|
|
943
|
+
function safeJsonPreview(value) {
|
|
944
|
+
try {
|
|
945
|
+
const json = JSON.stringify(value);
|
|
946
|
+
return json.length > 2_000 ? `${json.slice(0, 2_000)}…` : json;
|
|
947
|
+
}
|
|
948
|
+
catch {
|
|
949
|
+
return String(value);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
332
952
|
//# sourceMappingURL=jsonl-entry-parser.js.map
|