authorgravatar for naveediqbal949@gmail.comNaveed Iqbal <naveediqbal949@gmail.com> 2026-08-15 17:03:43+05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-15 12:03:43+00:00
log474cc5fb017a13dbdb403dd3bf714ddbf867205a
tree09683d5ed2489bbad67534f99a928bc0c3925b11
parente204f5a5d34c753399531e772fbb11420caeea09
signature Signed by PGP key B5690EEEBB952194

fix(server): ignore Claude command lifecycle messages (#6606)


2 files changed, 76 insertions(+), 0 deletions(-)

apps/server/src/provider/Layers/ClaudeAdapter.test.ts+71
......@@ -2287,6 +2287,77 @@ describe("ClaudeAdapterLive", () => {
22872287 );
22882288 });
22892289
2290 it.effect("consumes Claude command lifecycle notifications silently", () => {
2291 const harness = makeHarness();
2292 return Effect.gen(function* () {
2293 const adapter = yield* ClaudeAdapter;
2294 const sessionId = "6e81554e-5cff-4b37-8a39-f3a9051ac234";
2295
2296 yield* adapter.startSession({
2297 threadId: THREAD_ID,
2298 provider: ProviderDriverKind.make("claudeAgent"),
2299 runtimeMode: "full-access",
2300 });
2301
2302 const readyMessage = "command lifecycle test ready";
2303 const readyFiber = yield* Stream.takeUntil(
2304 adapter.streamEvents,
2305 (event) => event.type === "runtime.warning" && event.payload.message === readyMessage,
2306 ).pipe(Stream.runDrain, Effect.forkChild);
2307 harness.query.emit({
2308 type: "system",
2309 subtype: "notification",
2310 key: "command-lifecycle-ready",
2311 text: readyMessage,
2312 priority: "high",
2313 session_id: sessionId,
2314 uuid: "command-lifecycle-ready",
2315 } as unknown as SDKMessage);
2316 yield* Fiber.join(readyFiber);
2317
2318 const processedMessage = "command lifecycle messages processed";
2319 const runtimeEventsFiber = yield* Stream.takeUntil(
2320 adapter.streamEvents,
2321 (event) => event.type === "runtime.warning" && event.payload.message === processedMessage,
2322 ).pipe(Stream.runCollect, Effect.forkChild);
2323 for (const [state, uuid] of [
2324 ["started", "command-started"],
2325 ["completed", "command-completed"],
2326 ]) {
2327 harness.query.emit({
2328 type: "command_lifecycle",
2329 command_uuid: "4cd8e8a3-df7a-425d-b6c9-4053abc0b8fd",
2330 state,
2331 session_id: sessionId,
2332 uuid,
2333 } as unknown as SDKMessage);
2334 }
2335 harness.query.emit({
2336 type: "system",
2337 subtype: "notification",
2338 key: "command-lifecycle-processed",
2339 text: processedMessage,
2340 priority: "high",
2341 session_id: sessionId,
2342 uuid: "command-lifecycle-processed",
2343 } as unknown as SDKMessage);
2344
2345 const runtimeEvents = Array.from(yield* Fiber.join(runtimeEventsFiber));
2346 assert.deepEqual(
2347 runtimeEvents.map((event) => event.type),
2348 ["runtime.warning"],
2349 );
2350 const warning = runtimeEvents[0];
2351 assert.equal(warning?.type, "runtime.warning");
2352 if (warning?.type === "runtime.warning") {
2353 assert.equal(warning.payload.message, processedMessage);
2354 }
2355 }).pipe(
2356 Effect.provideService(Random.Random, makeDeterministicRandomService()),
2357 Effect.provide(harness.layer),
2358 );
2359 });
2360
22902361 it.effect("emits thread token usage updates from Claude task progress", () => {
22912362 const harness = makeHarness();
22922363 return Effect.gen(function* () {
apps/server/src/provider/Layers/ClaudeAdapter.ts+5
......@@ -3490,6 +3490,11 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
34903490 yield* logNativeSdkMessage(context, message);
34913491 yield* ensureThreadId(context, message);
34923492
3493 // Wire-only command bookkeeping has no user-facing T3 lifecycle.
3494 if (sdkMessageType(message) === "command_lifecycle") {
3495 return;
3496 }
3497
34933498 switch (message.type) {
34943499 case "stream_event":
34953500 yield* handleStreamEvent(context, message);