Browse Source

chore: fix pre-existing lint warnings

- Remove unnecessary non-null assertions on this.input (index.ts)
- Use literal key instead of computed string (internal-initiator.ts)
- Use import type for type-only import (session.ts)
- Remove unused beforeEach import (secondary-model.test.ts)
- Remove unused client destructuring in test file (index.test.ts)

Fixes #950
Michael Henke 1 tháng trước cách đây
mục cha
commit
e3286b9523

+ 171 - 79
src/hooks/foreground-fallback/index.test.ts

@@ -223,8 +223,10 @@ describe('isFailoverError', () => {
 
 describe('ForegroundFallbackManager (disabled)', () => {
   test('does nothing when enabled=false', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), false, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), false, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'session.error',
@@ -243,13 +245,14 @@ describe('ForegroundFallbackManager (disabled)', () => {
 // ---------------------------------------------------------------------------
 
 describe('ForegroundFallbackManager session.error', () => {
-  let client: ReturnType<typeof createMockClient>['client'];
   let mocks: ReturnType<typeof createMockClient>['mocks'];
   let mgr: ForegroundFallbackManager;
 
   beforeEach(() => {
-    ({ client, mocks } = createMockClient());
-    mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    ({ mocks } = createMockClient());
+    mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
   });
 
   test('triggers fallback on rate-limit session.error', async () => {
@@ -347,9 +350,7 @@ describe('ForegroundFallbackManager session.error', () => {
       },
     });
 
-    const call = mocks.promptAsync.mock.calls[0] as [
-      { parts: unknown[] },
-    ];
+    const call = mocks.promptAsync.mock.calls[0] as [{ parts: unknown[] }];
     expect(call[0].parts.some(isInternalInitiatorPart)).toBe(true);
   });
 
@@ -357,7 +358,7 @@ describe('ForegroundFallbackManager session.error', () => {
     // OpenCode may return partial/streaming messages whose `info` is undefined;
     // the fallback must ignore those rather than crash, and still re-submit the
     // real last user message.
-    ({ client, mocks } = createMockClient({
+    ({ mocks } = createMockClient({
       messagesData: [
         {},
         { info: { role: 'assistant' }, parts: [] },
@@ -368,7 +369,9 @@ describe('ForegroundFallbackManager session.error', () => {
         },
       ],
     }));
-    mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -410,7 +413,9 @@ describe('ForegroundFallbackManager session.error', () => {
   });
 
   test('does nothing when no chain configured for session', async () => {
-    const emptyMgr = new ForegroundFallbackManager({}, true, { directory: '/test' } as any);
+    const emptyMgr = new ForegroundFallbackManager({}, true, {
+      directory: '/test',
+    } as any);
     await emptyMgr.handleEvent({
       type: 'session.error',
       properties: {
@@ -424,8 +429,10 @@ describe('ForegroundFallbackManager session.error', () => {
   });
 
   test('does not abort when promptAsync is unavailable', async () => {
-    const { client, mocks } = createMockClient({ includePromptAsync: false });
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient({ includePromptAsync: false });
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'session.error',
@@ -440,7 +447,7 @@ describe('ForegroundFallbackManager session.error', () => {
   });
 
   test('falls back to abort+retry when promptAsync fails on busy session', async () => {
-    const { client, mocks } = createMockClient({
+    const { mocks } = createMockClient({
       promptAsyncImpl: async () => {
         throw new Error('session busy');
       },
@@ -448,7 +455,9 @@ describe('ForegroundFallbackManager session.error', () => {
         // abort succeeds on first call
       },
     });
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'session.error',
@@ -470,8 +479,10 @@ describe('ForegroundFallbackManager session.error', () => {
 
 describe('ForegroundFallbackManager message.updated', () => {
   test('tracks model from message.updated and falls back on error', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -496,8 +507,10 @@ describe('ForegroundFallbackManager message.updated', () => {
   });
 
   test('uses agent name from message.updated to select correct chain', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     // explorer message with its model
     await mgr.handleEvent({
@@ -533,7 +546,7 @@ describe('ForegroundFallbackManager message.updated', () => {
 describe('ForegroundFallbackManager session.status', () => {
   test('aborts session before fallback re-prompt on first failover retry', async () => {
     const calls: string[] = [];
-    const { client, mocks } = createMockClient({
+    const { mocks } = createMockClient({
       abortImpl: async () => {
         calls.push('abort');
       },
@@ -542,7 +555,12 @@ describe('ForegroundFallbackManager session.status', () => {
         return {};
       },
     });
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -573,7 +591,7 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('keeps registered child agent identity sticky for retry fallback chain', async () => {
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       makeChains({
         oracle: ['anthropic/claude-sonnet-4-5', 'openai/o3'],
@@ -612,7 +630,7 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('includes the sticky child agent in fallback promptAsync body', async () => {
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       makeChains({
         oracle: ['anthropic/claude-sonnet-4-5', 'openai/o3'],
@@ -654,8 +672,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('triggers fallback on retry status with rate limit message', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 1);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      1,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -680,8 +703,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('triggers fallback on retry status with insufficient balance message', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 1);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      1,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -706,8 +734,10 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('ignores session.status with non-rate-limit retry message', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'session.status',
@@ -721,8 +751,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('does not abort or switch after retries without a failover reason', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -750,8 +785,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('triggers immediate fallback on first failover retry', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -779,8 +819,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('switches to fallback model on first failover retry', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -808,8 +853,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('triggers fallback when rate-limit text is in props.error instead of status.message', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 1);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      1,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -835,8 +885,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('triggers fallback when props.error is a plain string', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 1);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      1,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -862,8 +917,13 @@ describe('ForegroundFallbackManager session.status', () => {
   });
 
   test('non-rate-limit retry does not trigger fallback but rate-limit does', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -907,7 +967,7 @@ describe('ForegroundFallbackManager session.status', () => {
     // loop (already in-flight when the abort happened) should NOT trigger a
     // second fallback — it carries the old model's error, not model B's.
     const calls: string[] = [];
-    const { client, mocks } = createMockClient({
+    const { mocks } = createMockClient({
       abortImpl: async () => {
         calls.push('abort');
       },
@@ -916,7 +976,12 @@ describe('ForegroundFallbackManager session.status', () => {
         return {};
       },
     });
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     // Seed session with model A (anthropic/claude-opus-4-5)
     await mgr.handleEvent({
@@ -978,7 +1043,7 @@ describe('ForegroundFallbackManager session.status', () => {
     // The previous fix used lastTriggerModel which still held model A, causing
     // model B's genuine retry to be mistaken for a stale retry from model A.
     const calls: string[] = [];
-    const { client, mocks } = createMockClient({
+    const { mocks } = createMockClient({
       abortImpl: async () => {
         calls.push('abort');
       },
@@ -987,7 +1052,12 @@ describe('ForegroundFallbackManager session.status', () => {
         return {};
       },
     });
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 1); // maxRetries=1 for immediate fallback
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      1,
+    ); // maxRetries=1 for immediate fallback
 
     // Seed session with model A
     await mgr.handleEvent({
@@ -1060,7 +1130,7 @@ describe('ForegroundFallbackManager chain exhaustion', () => {
   test('does not call promptAsync when the only chain model is already the current model', async () => {
     // Scenario: chain = ['openai/gpt-b'], current model IS 'openai/gpt-b'.
     // tryFallback adds 'openai/gpt-b' to tried → chain.find() returns undefined → exhausted.
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       { orchestrator: ['openai/gpt-b'] },
       true,
@@ -1096,13 +1166,11 @@ describe('ForegroundFallbackManager chain exhaustion', () => {
     // Use agent name tracking so we can target the right chain, then seed tried
     // by having the manager go through both models via sequential events
     // (each on a distinct session so dedup does not interfere).
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const chain = ['openai/model-x', 'openai/model-y'];
-    const mgr = new ForegroundFallbackManager(
-      { orchestrator: chain },
-      true,
-      { directory: '/test' } as any,
-    );
+    const mgr = new ForegroundFallbackManager({ orchestrator: chain }, true, {
+      directory: '/test',
+    } as any);
 
     // Session A: current model is model-x, which IS in the chain → picks model-y ✓
     await mgr.handleEvent({
@@ -1122,7 +1190,7 @@ describe('ForegroundFallbackManager chain exhaustion', () => {
     // Session B (fresh session, different ID): only model-y is in chain and it IS
     // the current model → tried gets model-y → chain.find() = undefined → exhausted
     // → abort called to stop the freeze
-    const { client: client2, mocks: mocks2 } = createMockClient();
+    const { mocks: mocks2 } = createMockClient();
     const mgr2 = new ForegroundFallbackManager(
       { orchestrator: ['openai/model-y'] }, // single-entry chain already in use
       true,
@@ -1151,8 +1219,10 @@ describe('ForegroundFallbackManager chain exhaustion', () => {
 
 describe('ForegroundFallbackManager deduplication', () => {
   test('ignores a second trigger within dedup window for same session', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     const event = {
       type: 'session.error',
@@ -1169,8 +1239,10 @@ describe('ForegroundFallbackManager deduplication', () => {
   });
 
   test('different sessions are not deduplicated against each other', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'session.error',
@@ -1185,8 +1257,10 @@ describe('ForegroundFallbackManager deduplication', () => {
   });
 
   test('cascade continues when second error arrives within dedup window after model switch', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     // Seed session: current model is first entry in orchestrator chain
     await mgr.handleEvent({
@@ -1244,8 +1318,10 @@ describe('ForegroundFallbackManager deduplication', () => {
 
 describe('ForegroundFallbackManager subagent.session.created', () => {
   test('records agent name from subagent.session.created and falls back correctly', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     // Register the session as 'explorer' via subagent creation event
     await mgr.handleEvent({
@@ -1280,7 +1356,7 @@ describe('ForegroundFallbackManager subagent.session.created', () => {
 describe('ForegroundFallbackManager session.deleted', () => {
   test('cleans up session state on session.deleted via coordinator', async () => {
     const coordinator = new SessionLifecycle(() => {});
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       makeChains(),
       true,
@@ -1328,8 +1404,9 @@ describe('ForegroundFallbackManager session.deleted', () => {
   });
 
   test('ignores session.deleted with no sessionID', async () => {
-    const { client } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
     // Should not throw
     await expect(
       mgr.handleEvent({ type: 'session.deleted', properties: {} }),
@@ -1338,7 +1415,7 @@ describe('ForegroundFallbackManager session.deleted', () => {
 
   test('cleans up state using info.id shape via coordinator', async () => {
     const coordinator = new SessionLifecycle(() => {});
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       makeChains(),
       true,
@@ -1378,7 +1455,6 @@ describe('ForegroundFallbackManager session.deleted', () => {
 
   test('does NOT clear inProgress when session.deleted fires', () => {
     const coordinator = new SessionLifecycle(() => {});
-    const { client } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       makeChains(),
       true,
@@ -1429,7 +1505,7 @@ describe('ForegroundFallbackManager resolveChain cross-agent isolation', () => {
     // oracle has no chain in runtimeChains; without the fix resolveChain would
     // fall through to the cross-agent "last resort" and pick a model from
     // orchestrator's chain - re-prompting oracle with an orchestrator model.
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       {
         // oracle intentionally absent - no chain configured
@@ -1459,7 +1535,7 @@ describe('ForegroundFallbackManager resolveChain cross-agent isolation', () => {
   test('uses cross-agent last-resort only when agent name is unknown', async () => {
     // When the agent name is genuinely unknown AND current model is not in any
     // chain, the last-resort flattened chain is acceptable.
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       { orchestrator: ['openai/gpt-4o'] },
       true,
@@ -1488,7 +1564,7 @@ describe('ForegroundFallbackManager resolveChain cross-agent isolation', () => {
     // A user-defined agent (e.g. Build) shares its model with the orchestrator
     // chain but has no chain of its own. It must NOT inherit the orchestrator
     // chain — that would switch the session from Build to Orchestrator.
-    const { client, mocks } = createMockClient();
+    const { mocks } = createMockClient();
     const mgr = new ForegroundFallbackManager(
       { orchestrator: ['openai/gpt-5.6', 'new-api/glm-5.2'] },
       true,
@@ -1522,8 +1598,13 @@ describe('ForegroundFallbackManager no-chain sessions', () => {
     // Councillor is owned by CouncilManager (own model chain + timeout).
     // FG must not abort or re-prompt — that races the council lifecycle and
     // previously produced "[foreground-fallback] no chain configured" noise.
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -1554,8 +1635,10 @@ describe('ForegroundFallbackManager no-chain sessions', () => {
   });
 
   test('councillor session.error: no abort and no re-prompt', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     await mgr.handleEvent({
       type: 'message.updated',
@@ -1582,8 +1665,13 @@ describe('ForegroundFallbackManager no-chain sessions', () => {
   });
 
   test('disableChain agent on session.status: no abort (not just no re-prompt)', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any, 3);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(
+      makeChains(),
+      true,
+      { directory: '/test' } as any,
+      3,
+    );
     mgr.disableChain('orchestrator');
 
     await mgr.handleEvent({
@@ -1621,8 +1709,10 @@ describe('ForegroundFallbackManager no-chain sessions', () => {
 
 describe('ForegroundFallbackManager disableChain', () => {
   test('after disableChain, rate-limit error surfaces instead of falling back', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     mgr.disableChain('orchestrator');
 
@@ -1646,8 +1736,10 @@ describe('ForegroundFallbackManager disableChain', () => {
   });
 
   test('other agents chains are unaffected by disableChain', async () => {
-    const { client, mocks } = createMockClient();
-    const mgr = new ForegroundFallbackManager(makeChains(), true, { directory: '/test' } as any);
+    const { mocks } = createMockClient();
+    const mgr = new ForegroundFallbackManager(makeChains(), true, {
+      directory: '/test',
+    } as any);
 
     mgr.disableChain('orchestrator');
 

+ 5 - 5
src/hooks/foreground-fallback/index.ts

@@ -522,7 +522,7 @@ export class ForegroundFallbackManager {
 
     this.inProgress.add(sessionID);
     try {
-      await abortSessionWithTimeout(getClient(this.input!), sessionID);
+      await abortSessionWithTimeout(getClient(this.input), sessionID);
       await this.execFallback(sessionID);
     } finally {
       this.inProgress.delete(sessionID);
@@ -597,7 +597,7 @@ export class ForegroundFallbackManager {
             agentName,
             tried: [...tried],
           });
-          await abortSessionWithTimeout(getClient(this.input!), sessionID);
+          await abortSessionWithTimeout(getClient(this.input), sessionID);
           return;
         }
       }
@@ -615,7 +615,7 @@ export class ForegroundFallbackManager {
       }
 
       // Retrieve the last user message to re-submit with the fallback model.
-      const result = await getClient(this.input!).session.messages({
+      const result = await getClient(this.input).session.messages({
         sessionID,
       });
       // result.data may contain partial/streaming messages whose `info` is
@@ -630,7 +630,7 @@ export class ForegroundFallbackManager {
 
       // promptAsync queues the prompt and returns immediately - this avoids
       // blocking the event handler while waiting for a full LLM response.
-      const sessionClient = getClient(this.input!).session;
+      const sessionClient = getClient(this.input).session;
       if (typeof sessionClient.promptAsync !== 'function') {
         log('[foreground-fallback] promptAsync unavailable', { sessionID });
         return;
@@ -658,7 +658,7 @@ export class ForegroundFallbackManager {
         log('[foreground-fallback] promptAsync on busy session, aborting', {
           sessionID,
         });
-        await abortSessionWithTimeout(getClient(this.input!), sessionID);
+        await abortSessionWithTimeout(getClient(this.input), sessionID);
         await new Promise((r) => setTimeout(r, REPROMPT_DELAY_MS));
         await sessionClient.promptAsync({ sessionID, ...promptBody });
       }

+ 63 - 66
src/hooks/task-session-manager/index.test.ts

@@ -4622,80 +4622,77 @@ describe('task-session-manager hook', () => {
   test.each([
     ['foreground-created-first', ['foreground-child', 'background-child']],
     ['background-created-first', ['background-child', 'foreground-child']],
-  ])(
-    'ambiguous early created events never supervise the foreground child (%s)',
-    async (_, createdOrder) => {
-      const board = new BackgroundJobBoard();
-      const clock = createSupervisorClock();
-      const abort = mock(async () => undefined);
-      const supervisor = new BackgroundJobSupervisor({
-        backgroundJobStore: board,
-        wallClockTimeoutMs: 100,
-        abortGraceMs: 10,
-        abort,
-        now: clock.now,
-        setTimeout: clock.setTimeout,
-        clearTimeout: clock.clearTimeout,
-      });
-      const { hook } = createHook({
-        backgroundJobBoard: board,
-        backgroundJobSupervisor: supervisor,
-      });
+  ])('ambiguous early created events never supervise the foreground child (%s)', async (_, createdOrder) => {
+    const board = new BackgroundJobBoard();
+    const clock = createSupervisorClock();
+    const abort = mock(async () => undefined);
+    const supervisor = new BackgroundJobSupervisor({
+      backgroundJobStore: board,
+      wallClockTimeoutMs: 100,
+      abortGraceMs: 10,
+      abort,
+      now: clock.now,
+      setTimeout: clock.setTimeout,
+      clearTimeout: clock.clearTimeout,
+    });
+    const { hook } = createHook({
+      backgroundJobBoard: board,
+      backgroundJobSupervisor: supervisor,
+    });
 
-      await hook['tool.execute.before'](
-        { tool: 'task', sessionID: 'parent-1', callID: 'background-call' },
-        {
-          args: {
-            subagent_type: 'explorer',
-            background: true,
-            description: 'background child',
-          },
+    await hook['tool.execute.before'](
+      { tool: 'task', sessionID: 'parent-1', callID: 'background-call' },
+      {
+        args: {
+          subagent_type: 'explorer',
+          background: true,
+          description: 'background child',
         },
-      );
-      await hook['tool.execute.before'](
-        { tool: 'task', sessionID: 'parent-1', callID: 'foreground-call' },
-        {
-          args: {
-            subagent_type: 'explorer',
-            background: false,
-            description: 'foreground child',
-          },
+      },
+    );
+    await hook['tool.execute.before'](
+      { tool: 'task', sessionID: 'parent-1', callID: 'foreground-call' },
+      {
+        args: {
+          subagent_type: 'explorer',
+          background: false,
+          description: 'foreground child',
         },
-      );
+      },
+    );
 
-      for (const taskID of createdOrder) {
-        await hook.event({
-          event: {
-            type: 'session.created',
-            properties: { info: { id: taskID, parentID: 'parent-1' } },
-          },
-        });
-      }
+    for (const taskID of createdOrder) {
+      await hook.event({
+        event: {
+          type: 'session.created',
+          properties: { info: { id: taskID, parentID: 'parent-1' } },
+        },
+      });
+    }
 
-      expect(board.get('background-child')?.background).toBe(false);
-      expect(board.get('foreground-child')?.background).toBe(false);
-      expect(abort).not.toHaveBeenCalled();
+    expect(board.get('background-child')?.background).toBe(false);
+    expect(board.get('foreground-child')?.background).toBe(false);
+    expect(abort).not.toHaveBeenCalled();
 
-      await hook['tool.execute.after'](
-        { tool: 'task', sessionID: 'parent-1', callID: 'foreground-call' },
-        { output: taskLaunchOutput('foreground-child') },
-      );
-      await hook['tool.execute.after'](
-        { tool: 'task', sessionID: 'parent-1', callID: 'background-call' },
-        { output: taskLaunchOutput('background-child') },
-      );
+    await hook['tool.execute.after'](
+      { tool: 'task', sessionID: 'parent-1', callID: 'foreground-call' },
+      { output: taskLaunchOutput('foreground-child') },
+    );
+    await hook['tool.execute.after'](
+      { tool: 'task', sessionID: 'parent-1', callID: 'background-call' },
+      { output: taskLaunchOutput('background-child') },
+    );
 
-      expect(board.get('foreground-child')?.background).toBe(false);
-      expect(board.get('background-child')?.background).toBe(true);
-      const backgroundJob = board.get('background-child');
-      expect(backgroundJob).toBeDefined();
-      const deadline = (backgroundJob?.runStartedAt ?? 0) + 100;
-      await clock.advanceTo(deadline);
+    expect(board.get('foreground-child')?.background).toBe(false);
+    expect(board.get('background-child')?.background).toBe(true);
+    const backgroundJob = board.get('background-child');
+    expect(backgroundJob).toBeDefined();
+    const deadline = (backgroundJob?.runStartedAt ?? 0) + 100;
+    await clock.advanceTo(deadline);
 
-      expect(abort).toHaveBeenCalledTimes(1);
-      expect(abort).toHaveBeenCalledWith('background-child');
-    },
-  );
+    expect(abort).toHaveBeenCalledTimes(1);
+    expect(abort).toHaveBeenCalledWith('background-child');
+  });
 
   test('missing after-hook callID fails closed while an exact background call remains', async () => {
     const board = new BackgroundJobBoard();

+ 1 - 1
src/tools/smartfetch/secondary-model.test.ts

@@ -1,4 +1,4 @@
-import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
+import { afterEach, describe, expect, mock, test } from 'bun:test';
 import { _testConfig, runSecondaryModelWithFallback } from './secondary-model';
 import type { SecondaryModel } from './types';
 

+ 21 - 24
src/utils/background-job-supervisor.test.ts

@@ -134,32 +134,29 @@ describe('BackgroundJobSupervisor', () => {
     ['resolve', async () => undefined],
     ['reject', async () => Promise.reject(new Error('abort failed'))],
     ['hang', () => new Promise<never>(() => {})],
-  ])(
-    'abort %s is requested once and grace remains independent',
-    async (_, abortCall) => {
-      const { board, supervisor, timers, abort } = createSupervisor({
-        abort: abortCall,
-      });
-      const job = launch(board, true);
-      supervisor.onLaunch(job);
-      await timers.advanceTo(100);
-      await timers.advanceTo(119);
+  ])('abort %s is requested once and grace remains independent', async (_, abortCall) => {
+    const { board, supervisor, timers, abort } = createSupervisor({
+      abort: abortCall,
+    });
+    const job = launch(board, true);
+    supervisor.onLaunch(job);
+    await timers.advanceTo(100);
+    await timers.advanceTo(119);
 
-      expect(abort).toHaveBeenCalledTimes(1);
-      expect(board.get(job.taskID)?.state).toBe('running');
-      await timers.advanceTo(120);
+    expect(abort).toHaveBeenCalledTimes(1);
+    expect(board.get(job.taskID)?.state).toBe('running');
+    await timers.advanceTo(120);
 
-      expect(board.get(job.taskID)).toMatchObject({
-        state: 'error',
-        timedOut: true,
-        statusUncertain: true,
-        cancellationRequested: true,
-      });
-      expect(board.getResultSummary(job.taskID)).toContain(
-        'abort was not confirmed',
-      );
-    },
-  );
+    expect(board.get(job.taskID)).toMatchObject({
+      state: 'error',
+      timedOut: true,
+      statusUncertain: true,
+      cancellationRequested: true,
+    });
+    expect(board.getResultSummary(job.taskID)).toContain(
+      'abort was not confirmed',
+    );
+  });
 
   test('completion after the deadline claim cannot replace the timeout', async () => {
     const { board, coordinator, supervisor, timers, abort } =

+ 1 - 1
src/utils/internal-initiator.ts

@@ -36,6 +36,6 @@ export function isInternalInitiatorPart(part: unknown): boolean {
     // prevent board injection on the continuation turn (#922).
     // Upstream key is not a stable plugin contract — graceful degradation
     // if renamed: injection resumes, loop returns, no crash.
-    part.metadata['compaction_continue'] === true
+    part.metadata.compaction_continue === true
   );
 }

+ 1 - 5
src/utils/session.test.ts

@@ -136,11 +136,7 @@ describe('session utilities', () => {
     process.on('unhandledRejection', handler);
     try {
       await expect(
-        promptWithTimeout(
-          client,
-          { sessionID: 's1', parts: [] },
-          5,
-        ),
+        promptWithTimeout(client, { sessionID: 's1', parts: [] }, 5),
       ).rejects.toThrow('Prompt timed out after 5ms');
 
       // Timeout behavior is unchanged — abort is called

+ 1 - 1
src/utils/session.ts

@@ -2,7 +2,7 @@
  * Shared session utilities for council and background managers.
  */
 
-import { type OpencodeClient } from '@opencode-ai/sdk/v2';
+import type { OpencodeClient } from '@opencode-ai/sdk/v2';
 import { log } from './logger';
 
 export const SESSION_ABORT_TIMEOUT_MS = 1_000;