@yusukeshib/pi-babysit 0.6.16 → 0.6.17
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 +4 -1
- package/index.ts +1 -1
- package/log-viewer.ts +38 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,7 +100,10 @@ terminal, leaving room for the editor/status/footer. Agent logs separate
|
|
|
100
100
|
assistant text, tool calls, and results; process logs show terminal output.
|
|
101
101
|
When babysit provides `output.timestamps.jsonl`, process lines begin with a muted
|
|
102
102
|
local `[YYYY-MM-DD HH:mm:ss]` capture time, with wrapped text aligned beneath the
|
|
103
|
-
message.
|
|
103
|
+
message. Agent lines show the capture time of their block's first available event
|
|
104
|
+
in the same left column, with the assistant/tool/result/error label beside the
|
|
105
|
+
first content line; streamed updates and duplicate results keep that time. This
|
|
106
|
+
is the time babysit received the first byte of the event or line, not an
|
|
104
107
|
application event time. Narrow views place the date above the text. Old logs and
|
|
105
108
|
lines without capture metadata remain readable without invented timestamps.
|
|
106
109
|
Only the selected log is loaded, bounded to the latest 1 MiB / 2,000 display
|
package/index.ts
CHANGED
|
@@ -3675,7 +3675,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
3675
3675
|
getTheme: () => ctx.ui.theme,
|
|
3676
3676
|
readLog: (id) => {
|
|
3677
3677
|
const isAgent = kindOf(id) === "subagent";
|
|
3678
|
-
return { ...readViewerLog(logPath(id),
|
|
3678
|
+
return { ...readViewerLog(logPath(id), true), isAgent };
|
|
3679
3679
|
},
|
|
3680
3680
|
});
|
|
3681
3681
|
return sessionWidget;
|
package/log-viewer.ts
CHANGED
|
@@ -31,7 +31,7 @@ function sanitize(text: string, sgr = false): string {
|
|
|
31
31
|
(s) => (sgr && /^\x1b\[[\d;]*m$/.test(s) ? s : ""),
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
|
-
type Block = { kind: "assistant" | "tool" | "result" | "error"; text: string };
|
|
34
|
+
type Block = { kind: "assistant" | "tool" | "result" | "error"; text: string; timestamp?: number };
|
|
35
35
|
function contentText(content: unknown): string {
|
|
36
36
|
if (typeof content === "string") return content;
|
|
37
37
|
return Array.isArray(content)
|
|
@@ -41,15 +41,17 @@ function contentText(content: unknown): string {
|
|
|
41
41
|
.join("")
|
|
42
42
|
: "";
|
|
43
43
|
}
|
|
44
|
-
function agentBlocks(raw: string): Block[] {
|
|
44
|
+
function agentBlocks(raw: string, timestamps?: (number | undefined)[]): Block[] {
|
|
45
45
|
const blocks: Block[] = [];
|
|
46
46
|
let current: Block | undefined;
|
|
47
47
|
const toolResults = new Map<string, Block>();
|
|
48
|
-
const assistant = () =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const assistant = (timestamp: number | undefined) => {
|
|
49
|
+
current ??= (blocks.push({ kind: "assistant", text: "", timestamp }), blocks[blocks.length - 1]);
|
|
50
|
+
current.timestamp ??= timestamp;
|
|
51
|
+
return current;
|
|
52
|
+
};
|
|
53
|
+
for (const [index, line] of raw.split(/\r\n|\r|\n/).entries()) {
|
|
54
|
+
const timestamp = timestamps?.[index];
|
|
53
55
|
let e: any;
|
|
54
56
|
try {
|
|
55
57
|
e = JSON.parse(line);
|
|
@@ -62,28 +64,30 @@ function agentBlocks(raw: string): Block[] {
|
|
|
62
64
|
if (["message_start", "message_update", "message_end"].includes(e.type)) {
|
|
63
65
|
if (e.message?.role === "assistant") {
|
|
64
66
|
const text = contentText(e.message.content);
|
|
65
|
-
const b = assistant();
|
|
67
|
+
const b = assistant(timestamp);
|
|
66
68
|
if (text) b.text = text; // cumulative/final snapshots replace streamed deltas
|
|
67
69
|
if (e.message.errorMessage)
|
|
68
|
-
blocks.push({ kind: "error", text: String(e.message.errorMessage) });
|
|
70
|
+
blocks.push({ kind: "error", text: String(e.message.errorMessage), timestamp });
|
|
69
71
|
if (e.type === "message_end") current = undefined;
|
|
70
72
|
} else if (
|
|
71
73
|
!e.message &&
|
|
72
74
|
e.assistantMessageEvent?.type === "text_delta" &&
|
|
73
75
|
typeof e.assistantMessageEvent.delta === "string"
|
|
74
76
|
) {
|
|
75
|
-
assistant().text += e.assistantMessageEvent.delta;
|
|
77
|
+
assistant(timestamp).text += e.assistantMessageEvent.delta;
|
|
76
78
|
} else if (e.type === "message_end" && e.message?.role === "toolResult") {
|
|
77
79
|
const id = e.message.toolCallId;
|
|
78
80
|
const text = contentText(e.message.content);
|
|
79
81
|
if (id && toolResults.has(id)) {
|
|
80
82
|
const b = toolResults.get(id)!;
|
|
81
83
|
b.text = text;
|
|
84
|
+
b.timestamp ??= timestamp;
|
|
82
85
|
b.kind = e.message.isError ? "error" : b.kind;
|
|
83
86
|
} else {
|
|
84
87
|
const b: Block = {
|
|
85
88
|
kind: e.message.isError ? "error" : "result",
|
|
86
89
|
text,
|
|
90
|
+
timestamp,
|
|
87
91
|
};
|
|
88
92
|
blocks.push(b);
|
|
89
93
|
if (id) toolResults.set(id, b);
|
|
@@ -93,10 +97,12 @@ function agentBlocks(raw: string): Block[] {
|
|
|
93
97
|
blocks.push({
|
|
94
98
|
kind: "tool",
|
|
95
99
|
text: `${e.toolName ?? "tool"} ${JSON.stringify(e.args ?? {})}`,
|
|
100
|
+
timestamp,
|
|
96
101
|
});
|
|
97
102
|
} else if (e.type === "tool_execution_end") {
|
|
98
103
|
const b: Block = {
|
|
99
104
|
kind: e.isError ? "error" : "result",
|
|
105
|
+
timestamp,
|
|
100
106
|
text:
|
|
101
107
|
contentText(e.result?.content) ||
|
|
102
108
|
String(e.error?.message ?? e.error ?? ""),
|
|
@@ -108,6 +114,7 @@ function agentBlocks(raw: string): Block[] {
|
|
|
108
114
|
} else if (e.type === "error")
|
|
109
115
|
blocks.push({
|
|
110
116
|
kind: "error",
|
|
117
|
+
timestamp,
|
|
111
118
|
text: String(
|
|
112
119
|
e.message ?? e.error?.message ?? e.error ?? "Unknown error",
|
|
113
120
|
),
|
|
@@ -141,7 +148,7 @@ export function createLogViewer(options: LogViewerOptions) {
|
|
|
141
148
|
const theme = options.getTheme();
|
|
142
149
|
if (cache && cacheWidth === width && cacheTheme === theme) return cache;
|
|
143
150
|
const blocks = isAgent
|
|
144
|
-
? agentBlocks(raw)
|
|
151
|
+
? agentBlocks(raw, lineTimestamps)
|
|
145
152
|
: [{ kind: "result" as const, text: sanitize(raw, true) }];
|
|
146
153
|
const lines: string[] = description
|
|
147
154
|
? [
|
|
@@ -167,7 +174,25 @@ export function createLogViewer(options: LogViewerOptions) {
|
|
|
167
174
|
}
|
|
168
175
|
continue;
|
|
169
176
|
}
|
|
170
|
-
if (isAgent)
|
|
177
|
+
if (isAgent && b.timestamp !== undefined) {
|
|
178
|
+
const date = theme.fg("muted", dateLabel(b.timestamp));
|
|
179
|
+
const kind = theme.fg(b.kind === "error" ? "error" : b.kind === "assistant" ? "accent" : "toolTitle", `[${b.kind}]`);
|
|
180
|
+
const indent = visibleWidth(date) + 1;
|
|
181
|
+
const kindIndent = visibleWidth(kind) + 1;
|
|
182
|
+
if (width <= indent + kindIndent + 4) {
|
|
183
|
+
lines.push(...new Text(date, 0, 0).render(width));
|
|
184
|
+
lines.push(...new Text(`${kind} ${b.text}`, 0, 0).render(width));
|
|
185
|
+
} else {
|
|
186
|
+
const contentWidth = width - indent - kindIndent;
|
|
187
|
+
const rendered = b.kind === "assistant"
|
|
188
|
+
? new Markdown(b.text, 0, 0, getMarkdownTheme()).render(contentWidth)
|
|
189
|
+
: new Text(b.text, 0, 0).render(contentWidth);
|
|
190
|
+
lines.push(...rendered.map((line, row) =>
|
|
191
|
+
`${date} ${row === 0 ? kind + " " : " ".repeat(kindIndent)}${line}`));
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (isAgent) {
|
|
171
196
|
lines.push(
|
|
172
197
|
theme.fg(
|
|
173
198
|
b.kind === "error"
|
|
@@ -178,6 +203,7 @@ export function createLogViewer(options: LogViewerOptions) {
|
|
|
178
203
|
`[${b.kind}]`,
|
|
179
204
|
),
|
|
180
205
|
);
|
|
206
|
+
}
|
|
181
207
|
lines.push(
|
|
182
208
|
...(b.kind === "assistant"
|
|
183
209
|
? new Markdown(b.text, 0, 0, getMarkdownTheme()).render(width)
|