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...@@ -10,3 +10,4 @@ packages/*/dist
10.env10.env
11.env.local11.env.local
12build/12build/
13.logs/
apps/desktop/src/providerManager.ts+15-1
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1import { EventEmitter } from "node:events";1import { EventEmitter } from "node:events";
2import fs from "node:fs";
3import path from "node:path";
24
3import {5import {
4 type ProviderEvent,6 type ProviderEvent,
...@@ -21,10 +23,22 @@ export interface ProviderManagerEvents {...@@ -21,10 +23,22 @@ export interface ProviderManagerEvents {
2123
22export class ProviderManager extends EventEmitter<ProviderManagerEvents> {24export class ProviderManager extends EventEmitter<ProviderManagerEvents> {
23 private readonly codex = new CodexAppServerManager();25 private readonly codex = new CodexAppServerManager();
26 private readonly logStream: fs.WriteStream;
2427
25 constructor() {28 constructor() {
26 super();29 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 });
28 }42 }
2943
30 async startSession(raw: ProviderSessionStartInput): Promise<ProviderSession> {44 async startSession(raw: ProviderSessionStartInput): Promise<ProviderSession> {
apps/renderer/src/components/ChatView.tsx+33-51
...@@ -36,10 +36,10 @@ function statusLabel(phase: string): string {...@@ -36,10 +36,10 @@ function statusLabel(phase: string): string {
36}36}
3737
38function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {38function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {
39 if (tone === "thinking") return "text-sky-100";39 if (tone === "error") return "text-rose-300/50";
40 if (tone === "tool") return "text-emerald-100";40 if (tone === "tool") return "text-[#8a8a8a]";
41 if (tone === "error") return "text-rose-100";41 if (tone === "thinking") return "text-[#707070]";
42 return "text-[#d8d8d8]";42 return "text-[#606060]";
43}43}
4444
45export default function ChatView() {45export default function ChatView() {
...@@ -341,53 +341,6 @@ export default function ChatView() {...@@ -341,53 +341,6 @@ export default function ChatView() {
341 </div>341 </div>
342 ) : (342 ) : (
343 <div className="mx-auto max-w-3xl space-y-4">343 <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 )}
391 {activeThread.messages.map((msg) => (344 {activeThread.messages.map((msg) => (
392 <div key={msg.id}>345 <div key={msg.id}>
393 {msg.role === "user" ? (346 {msg.role === "user" ? (
...@@ -433,6 +386,35 @@ export default function ChatView() {...@@ -433,6 +386,35 @@ export default function ChatView() {
433 )}386 )}
434 </div>387 </div>
435 ))}388 ))}
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 )}
436 <div ref={messagesEndRef} />418 <div ref={messagesEndRef} />
437 </div>419 </div>
438 )}420 )}
bun.lock+3
...@@ -42,6 +42,7 @@...@@ -42,6 +42,7 @@
42 "@types/react": "^19.0.0",42 "@types/react": "^19.0.0",
43 "@types/react-dom": "^19.0.0",43 "@types/react-dom": "^19.0.0",
44 "@vitejs/plugin-react": "^4.3.4",44 "@vitejs/plugin-react": "^4.3.4",
45 "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112",
45 "tailwindcss": "^4.0.0",46 "tailwindcss": "^4.0.0",
46 "typescript": "^5.7.3",47 "typescript": "^5.7.3",
47 "vite": "^6.0.5",48 "vite": "^6.0.5",
...@@ -368,6 +369,8 @@...@@ -368,6 +369,8 @@
368369
369 "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=="],370 "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
371 "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],374 "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
372375
373 "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],376 "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],