History

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

Naveed Iqbal committed 2 months ago

Commit 474cc5f1 parent e204f5a2 files · +76 −0Browse files

Unified · Split

2 files
  1. apps/server/src/provider/Layers/ClaudeAdapter.test.ts +71 −0
  2. apps/server/src/provider/Layers/ClaudeAdapter.ts +5 −0
apps/server/src/provider/Layers/ClaudeAdapter.test.ts+71−0
@@ -2287,6 +2287,77 @@ describe("ClaudeAdapterLive", () => { · 2286 unmodified lines
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−0
@@ -3490,6 +3490,11 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( · 3489 unmodified lines
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);