authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-09-04 07:26:51-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 07:26:51-07:00
logec8b2119c377f5c1dbe6235b221ef98eca31a96e
tree19ef1e6a6189b78e49524f79f97daad6945d9387
parent44dc8ae259f5c3349f7ab8045e39bd62408b52a9
signature Signed by PGP key B5690EEEBB952194

perf(server): omit repeated OpenCode progress logs (#9689)


3 files changed, 71 insertions(+), 1 deletions(-)

apps/server/src/provider/Layers/EventNdjsonLogger.test.ts+58
......@@ -341,6 +341,19 @@ describe("EventNdjsonLogger", () => {
341341 },
342342 threadId,
343343 );
344 yield* native.write(
345 {
346 event: {
347 type: "message.part.updated",
348 payload: {
349 properties: {
350 part: { type: "tool", state: { status: "running", output: "progress" } },
351 },
352 },
353 },
354 },
355 threadId,
356 );
344357 yield* native.write({ type: "turn.completed", id: "native-final" }, threadId);
345358 yield* store.close();
346359
......@@ -362,6 +375,51 @@ describe("EventNdjsonLogger", () => {
362375 }),
363376 );
364377
378 it.effect("keeps OpenCode tool input, final output, and errors in native logs", () =>
379 Effect.gen(function* () {
380 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-"));
381 const basePath = NodePath.join(tempDir, "events.log");
382 const threadId = ThreadId.make("thread-tool-lifecycle");
383
384 try {
385 const store = yield* makeEventNdjsonLogStore(basePath, { batchWindowMs: 0 });
386 const native = store.logger("native");
387 const input = { command: "run-build" };
388 const states = [
389 { status: "pending", input },
390 { status: "completed", input, output: "Build complete", time: { start: 1, end: 2 } },
391 { status: "error", input, error: "Build failed", time: { start: 3, end: 4 } },
392 { status: "unknown", input },
393 null,
394 ];
395 const events = states.map((state) => ({
396 event: {
397 type: "message.part.updated",
398 payload: { properties: { part: { type: "tool", state } } },
399 },
400 }));
401 for (const event of events) {
402 yield* native.write(event, threadId);
403 }
404 yield* store.close();
405
406 const payloads = NodeFS.readFileSync(
407 ownedLogPath(basePath, "thread-tool-lifecycle"),
408 "utf8",
409 )
410 .trim()
411 .split("\n")
412 .map((line) => parseLogLine(line).payload);
413 assert.deepEqual(
414 payloads,
415 events.map((event) => encodeUnknownJson(event)),
416 );
417 } finally {
418 NodeFS.rmSync(tempDir, { recursive: true, force: true });
419 }
420 }),
421 );
422
365423 it.effect("contains hostile event accessors inside guarded serialization", () =>
366424 Effect.gen(function* () {
367425 const tempDir = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-provider-log-"));
apps/server/src/provider/Layers/EventNdjsonLogger.ts+9-1
......@@ -229,7 +229,15 @@ function shouldPersist(stream: EventNdjsonStream, event: unknown): boolean {
229229 const part = Reflect.get(properties, "part");
230230 if (typeof part !== "object" || part === null) return true;
231231 const partType = Reflect.get(part, "type");
232 return partType !== "text" && partType !== "reasoning";
232 if (partType === "text" || partType === "reasoning") return false;
233 if (partType === "tool") {
234 // Running snapshots repeat growing output. Pending and terminal states stay in the log.
235 const state = Reflect.get(part, "state");
236 if (typeof state === "object" && state !== null) {
237 return Reflect.get(state, "status") !== "running";
238 }
239 }
240 return true;
233241 }
234242
235243 return true;
docs/internals/providers.md+4
......@@ -230,6 +230,10 @@ sandbox.
230230
231231## OpenCode server ownership and catalog
232232
233Native OpenCode event logs skip text deltas and running tool snapshots. Pending, completed,
234and failed tool states remain, including final output and errors. This filter does not change
235events sent to clients or orchestration storage.
236
233237Each OpenCode provider instance owns one lazy local server for catalog discovery and
234238text-generation helpers through [`OpenCodeServerOwner.ts`][opencode-server-owner]. Concurrent
235239borrowers share startup. The server closes 30 seconds after the last borrower releases it, or