authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-02-08 14:11:19-08:00
committergravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-02-08 14:11:19-08:00
log2f4051ed308d8c10558e9be7a4b58f6fd7a936eb
treef20a730467f7ae8419f5df05c93eb89ade950cca
parentf0ac59306842c0392b2e11ac35ebf24763131de4
signature Commit is signed but in an unrecognized format.

better logging


4 files changed, 52 insertions(+), 52 deletions(-)

.gitignore+1
......@@ -10,3 +10,4 @@ packages/*/dist
1010.env
1111.env.local
1212build/
13.logs/
apps/desktop/src/providerManager.ts+15-1
......@@ -1,4 +1,6 @@
11import { EventEmitter } from "node:events";
2import fs from "node:fs";
3import path from "node:path";
24
35import {
46 type ProviderEvent,
......@@ -21,10 +23,22 @@ export interface ProviderManagerEvents {
2123
2224export class ProviderManager extends EventEmitter<ProviderManagerEvents> {
2325 private readonly codex = new CodexAppServerManager();
26 private readonly logStream: fs.WriteStream;
2427
2528 constructor() {
2629 super();
27 this.codex.on("event", (event) => this.emit("event", event));
30
31 const logsDir = path.resolve(process.cwd(), ".logs");
32 fs.mkdirSync(logsDir, { recursive: true });
33 this.logStream = fs.createWriteStream(
34 path.join(logsDir, "events.txt"),
35 { flags: "a" },
36 );
37
38 this.codex.on("event", (event) => {
39 this.logStream.write(`${JSON.stringify(event)}\n`);
40 this.emit("event", event);
41 });
2842 }
2943
3044 async startSession(raw: ProviderSessionStartInput): Promise<ProviderSession> {
apps/renderer/src/components/ChatView.tsx+33-51
......@@ -36,10 +36,10 @@ function statusLabel(phase: string): string {
3636}
3737
3838function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {
39 if (tone === "thinking") return "text-sky-100";
40 if (tone === "tool") return "text-emerald-100";
41 if (tone === "error") return "text-rose-100";
42 return "text-[#d8d8d8]";
39 if (tone === "error") return "text-rose-300/50";
40 if (tone === "tool") return "text-[#8a8a8a]";
41 if (tone === "thinking") return "text-[#707070]";
42 return "text-[#606060]";
4343}
4444
4545export default function ChatView() {
......@@ -341,53 +341,6 @@ export default function ChatView() {
341341 </div>
342342 ) : (
343343 <div className="mx-auto max-w-3xl space-y-4">
344 {isWorking && (
345 <div className="rounded-2xl border border-sky-400/20 bg-sky-500/[0.06] px-3 py-2.5">
346 <div className="flex items-center justify-between gap-2">
347 <div className="inline-flex items-center gap-2 text-xs text-sky-100">
348 <span className="relative inline-flex h-2.5 w-2.5">
349 <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-300/70" />
350 <span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-sky-200" />
351 </span>
352 <span>Model is working</span>
353 </div>
354 <span className="text-[10px] text-sky-100/70">
355 {recentWorkLogEntries.length} event
356 {recentWorkLogEntries.length === 1 ? "" : "s"}
357 </span>
358 </div>
359 {recentWorkLogEntries.length === 0 ? (
360 <p className="pt-2 text-[11px] text-sky-100/70">
361 Waiting for tool/preamble updates...
362 </p>
363 ) : (
364 <div className="mt-2 max-h-48 space-y-1.5 overflow-y-auto pr-1">
365 {recentWorkLogEntries.map((entry) => (
366 <div
367 key={entry.id}
368 className="rounded-lg border border-white/[0.08] bg-black/20 px-2 py-1.5"
369 >
370 <div className="flex items-start justify-between gap-2">
371 <p
372 className={`text-[11px] ${workToneClass(entry.tone)}`}
373 >
374 {entry.label}
375 </p>
376 <span className="shrink-0 text-[10px] text-[#a0a0a0]/60">
377 {formatTimestamp(entry.createdAt)}
378 </span>
379 </div>
380 {entry.detail && (
381 <p className="pt-0.5 font-mono text-[11px] text-[#d0d0d0]/75">
382 {entry.detail}
383 </p>
384 )}
385 </div>
386 ))}
387 </div>
388 )}
389 </div>
390 )}
391344 {activeThread.messages.map((msg) => (
392345 <div key={msg.id}>
393346 {msg.role === "user" ? (
......@@ -433,6 +386,35 @@ export default function ChatView() {
433386 )}
434387 </div>
435388 ))}
389 {/* Inline activity feed */}
390 {isWorking && (
391 <div className="border-l-2 border-white/[0.05] pl-4 py-1">
392 {recentWorkLogEntries.map((entry) => (
393 <p
394 key={entry.id}
395 className={`py-[2px] text-[12px] leading-relaxed ${workToneClass(entry.tone)}`}
396 >
397 {entry.detail ? (
398 <>
399 {entry.label}
400 <span className="ml-1.5 font-mono text-[11px] opacity-60">
401 {entry.detail}
402 </span>
403 </>
404 ) : (
405 entry.label
406 )}
407 </p>
408 ))}
409 <div className="flex items-center pt-1">
410 <span className="inline-flex items-center gap-[3px]">
411 <span className="h-1 w-1 rounded-full bg-white/20 animate-pulse" />
412 <span className="h-1 w-1 rounded-full bg-white/20 animate-pulse [animation-delay:200ms]" />
413 <span className="h-1 w-1 rounded-full bg-white/20 animate-pulse [animation-delay:400ms]" />
414 </span>
415 </div>
416 </div>
417 )}
436418 <div ref={messagesEndRef} />
437419 </div>
438420 )}
bun.lock+3
......@@ -42,6 +42,7 @@
4242 "@types/react": "^19.0.0",
4343 "@types/react-dom": "^19.0.0",
4444 "@vitejs/plugin-react": "^4.3.4",
45 "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112",
4546 "tailwindcss": "^4.0.0",
4647 "typescript": "^5.7.3",
4748 "vite": "^6.0.5",
......@@ -368,6 +369,8 @@
368369
369370 "axios": ["axios@1.13.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="],
370371
372 "babel-plugin-react-compiler": ["babel-plugin-react-compiler@19.0.0-beta-ebf51a3-20250411", "", { "dependencies": { "@babel/types": "^7.26.0" } }, "sha512-q84bNR9JG1crykAlJUt5Ud0/5BUyMFuQww/mrwIQDFBaxsikqBDj3f/FNDsVd2iR26A1HvXKWPEIfgJDv8/V2g=="],
373
371374 "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
372375
373376 "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],